How Do I Fix the ‘babel-preset-es2015’ Error When Using Arrow Functions in React

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 to public/index.html (instead of build/).
  • 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.

Related blog posts