When I first started building my React JS project, I was excited to use arrow functions because they’re a clean ES6 syntax. But then, I hit a wall. My build kept failing with a Babel error, and it took some digging to understand what was going on. In this article, I’ll walk you through my original setup, the error I faced, why it happens, and how I fixed it. I’ll also share a working example with extra functionality that you can practice with.
My Original Setup
I was compiling my React project with Webpack. Here’s the webpack.config.js
I had:
const HTMLWebPackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
entry: [ 'babel-polyfill', './src/index.js' ],
output: {
path: path.resolve(__dirname, "build"),
filename: "bundle.js"
},
module:{
rules:[
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
{
test: /\.scss$/,
exclude: /node_modules/,
use: ['style-loader', 'css-loader', 'sass-loader']
}
],
},
resolve: {
extensions: ['*', '.js', '.jsx'],
},
plugins: [
new HTMLWebPackPlugin({ template: path.join(__dirname, '/build/index.html') })
],
devServer:{
port:5200
}
};
Inside my project root, I also had a .babelrc
file:
{
"presets": [
"es2015", "react",
"@babel/preset-env",
"@babel/preset-react"
]
}
And in one of my React class components, I wrote an arrow function like this:
addBackDrop = e => {
if (this.state.showDatePicker && !ReactDOM.findDOMNode(this).contains(e.target)) {
this.showDatePicker(false);
}
}
The Error I Saw
When I ran npm run dev
, I immediately hit this error:
ERROR in ./src/index.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
Error: Plugin/Preset files are not allowed to export objects, only functions.
In ...\node_modules\babel-preset-es2015\lib\index.js
At first glance, it was confusing. Why would my arrow function cause this?
What Went Wrong
The issue wasn’t with arrow functions themselves—it was my Babel configuration.
"es2015"
and"react"
are old Babel 6 presets.@babel/preset-env
and@babel/preset-react
are Babel 7 presets.
By mixing them, Babel 7 tried to load a Babel 6 preset that exported an object instead of a function. That’s why it complained with:
Plugin/Preset files are not allowed to export objects, only functions.
On top of that, class field arrow functions (addBackDrop = e => {}
) require the class properties plugin in Babel 7.
How I Fix It
Here’s what I did step by step:
Installed the right Babel 7 packages
npm i -D @babel/core babel-loader @babel/preset-env @babel/preset-react @babel/plugin-proposal-class-properties
Cleaned up my .babelrc
{
"presets": [
["@babel/preset-env", { "targets": "defaults", "useBuiltIns": "usage", "corejs": 3 }],
"@babel/preset-react"
],
"plugins": ["@babel/plugin-proposal-class-properties"]
}
Fix my Webpack entry
I dropped babel-polyfill
(it’s deprecated) and just pointed to my index.js
:
entry: "./src/index.js",
If I wanted explicit polyfills, I could also add at the top of my index.js
:
import "core-js/stable";
import "regenerator-runtime/runtime";
Sanity checks
- I made sure my
babel-loader
version was^8.x
. - I pointed
html-webpack-plugin
topublic/index.html
(instead ofbuild/
). - When issues persisted, I deleted
node_modules
and reinstalled everything fresh.
A Working Example With Extra Practice
Once Babel was sorted, arrow functions in class components worked perfectly. To practice, I built a small demo component that:
- Toggles a date picker open/close.
- Closes it when you click outside.
- Lets you filter a list with a controlled input.
Here’s the code:
src/App.jsx
import React from "react";
export default class App extends React.Component {
state = {
showDatePicker: false,
query: "",
items: ["React", "Babel", "Webpack", "Class Fields", "Arrow Functions"]
};
containerRef = React.createRef();
togglePicker = () => {
this.setState(s => ({ showDatePicker: !s.showDatePicker }));
};
handleBackdrop = (e) => {
const el = this.containerRef.current;
if (this.state.showDatePicker && el && !el.contains(e.target)) {
this.setState({ showDatePicker: false });
}
};
handleQueryChange = (e) => {
this.setState({ query: e.target.value });
};
componentDidMount() {
document.addEventListener("click", this.handleBackdrop);
}
componentWillUnmount() {
document.removeEventListener("click", this.handleBackdrop);
}
get filteredItems() {
const q = this.state.query.toLowerCase();
return this.state.items.filter(i => i.toLowerCase().includes(q));
}
render() {
const { showDatePicker, query } = this.state;
return (
<div style={{ fontFamily: "system-ui, sans-serif", padding: 24 }}>
<h1>Arrow Functions + Class Fields (Babel 7)</h1>
<section ref={this.containerRef} style={{ display: "grid", gap: 12, maxWidth: 480 }}>
<button onClick={this.togglePicker}>
{showDatePicker ? "Hide" : "Show"} Date Picker
</button>
{showDatePicker && (
<div style={{ border: "1px solid #ccc", padding: 12, borderRadius: 8 }}>
<p>This is a placeholder “date picker”. Click anywhere outside to close.</p>
<input type="date" />
</div>
)}
<hr />
<label>
Filter list:{" "}
<input
value={query}
onChange={this.handleQueryChange}
placeholder="type to filter…"
/>
</label>
<ul>
{this.filteredItems.map(item => (
<li key={item}>{item}</li>
))}
</ul>
</section>
</div>
);
}
}
src/index.js
import React from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
const root = createRoot(document.getElementById("root"));
root.render(<App />);
public/index.html
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Babel 7 Arrow Functions Demo</title>
<meta name="viewport" content="width=device-width,initial-scale=1" />
</head>
<body>
<div id="root"></div>
</body>
</html>
Bonus Hooks Version
I also practiced rewriting this with React hooks. With useState
, useRef
, and useEffect
, the same functionality becomes much cleaner.
import React, { useEffect, useMemo, useRef, useState } from "react";
export default function AppHooks() {
const [showDatePicker, setShowDatePicker] = useState(false);
const [query, setQuery] = useState("");
const items = ["React", "Babel", "Webpack", "Class Fields", "Arrow Functions"];
const containerRef = useRef(null);
const togglePicker = () => setShowDatePicker(v => !v);
const handleQueryChange = (e) => setQuery(e.target.value);
const handleBackdrop = (e) => {
const el = containerRef.current;
if (showDatePicker && el && !el.contains(e.target)) {
setShowDatePicker(false);
}
};
useEffect(() => {
document.addEventListener("click", handleBackdrop);
return () => document.removeEventListener("click", handleBackdrop);
}, [showDatePicker]);
const filteredItems = useMemo(() => {
const q = query.toLowerCase();
return items.filter(i => i.toLowerCase().includes(q));
}, [items, query]);
return (
<div style={{ fontFamily: "system-ui, sans-serif", padding: 24 }}>
<h1>Arrow Functions + Hooks</h1>
<section ref={containerRef} style={{ display: "grid", gap: 12, maxWidth: 480 }}>
<button onClick={togglePicker}>
{showDatePicker ? "Hide" : "Show"} Date Picker
</button>
{showDatePicker && (
<div style={{ border: "1px solid #ccc", padding: 12, borderRadius: 8 }}>
<p>This is a placeholder “date picker”. Click anywhere outside to close.</p>
<input type="date" />
</div>
)}
<hr />
<label>
Filter list:{" "}
<input value={query} onChange={handleQueryChange} placeholder="type to filter…" />
</label>
<ul>
{filteredItems.map(item => <li key={item}>{item}</li>)}
</ul>
</section>
</div>
);
}
Final Thought
I learned that sometimes the scariest-looking errors aren’t about my code at all they’re about configuration mismatches. Mixing Babel 6 and Babel 7 presets caused my build to break, but once I cleaned up my .babelrc
and installed the right packages, arrow functions worked beautifully.
If you run into the dreaded “Plugin/Preset files are not allowed to export objects, only functions” error, double check your Babel presets. Stick with Babel 7, add the class properties plugin, and you’ll be back on track in no time.