← Back to all tutorials

Babel Loaders

Set up Babel with Webpack to transpile modern JavaScript (ES6+) into browser-compatible code using babel-loader and presets.

Babel Loaders

Modern JavaScript uses features like arrow functions, template literals, destructuring, and classes — but older browsers do not support them. Babel is a JavaScript compiler that transpiles modern syntax into backwards-compatible code. In this episode you will integrate Babel into Webpack using babel-loader.

What Is Babel?

Babel reads modern JavaScript and outputs equivalent code that works in older environments:

// Input (ES6+)
const greet = (name) => `Hello, ${name}!`;

// Babel output (ES5)
var greet = function(name) {
    return "Hello, " + name + "!";
};

What Are Loaders?

Webpack can only understand JavaScript and JSON natively. Loaders teach Webpack how to process other file types — transforming them before they are added to the bundle.

LoaderWhat It Processes
babel-loaderModern JavaScript → compatible JavaScript
css-loaderCSS files → JavaScript module
sass-loaderSASS/SCSS → CSS
file-loaderImages/fonts → output files with URLs
ts-loaderTypeScript → JavaScript

Each loader is specified in the module.rules array of webpack.config.js.

Step 1: Install Babel Packages

npm install babel-loader @babel/core @babel/preset-env --save-dev
PackagePurpose
babel-loaderThe Webpack loader that passes files through Babel
@babel/coreThe core Babel compiler engine
@babel/preset-envA smart preset that determines which transformations are needed based on your target browsers

Step 2: Add the Loader Rule

Add the Babel loader to the module.rules array in webpack.config.js:

module: {
    rules: [
        {
            test: /\.js$/,
            exclude: /node_modules/,
            use: {
                loader: 'babel-loader',
                options: {
                    presets: ['@babel/preset-env'],
                },
            },
        },
    ],
},

Understanding the Rule

PropertyValuePurpose
test/\.js$/A regex that matches files ending in .js
exclude/node_modules/Skip library code — they should already be transpiled
use.loader'babel-loader'The loader to apply to matched files
use.options.presets['@babel/preset-env']Tells Babel which transformations to apply

The test property uses a regular expression. /\.js$/ matches any filename ending with .js. The backslash escapes the dot (which is a regex wildcard), and $ means "end of string."

What @babel/preset-env Does

@babel/preset-env is a "smart" preset that automatically determines which JavaScript features need to be transpiled based on your target environment. Instead of manually listing individual Babel plugins, you specify which browsers to support:

// In package.json — add a browserslist field
{
    "browserslist": [
        "> 1%",
        "last 2 versions",
        "not dead"
    ]
}
QueryMeaning
> 1%Browsers with more than 1% global market share
last 2 versionsThe last 2 versions of each browser
not deadExclude browsers without official support

If all your target browsers support arrow functions natively, Babel will not transform them — keeping the output smaller and faster.

Step 3: Build and Test

npm run build

Webpack now passes every .js file through Babel before bundling. If you build in development mode and inspect the output, you will see that modern syntax has been converted to compatible code.

Using a Separate Babel Config File

For larger projects, move Babel options to a separate config file:

.babelrc

{
    "presets": ["@babel/preset-env"]
}

Then simplify the Webpack rule:

{
    test: /\.js$/,
    exclude: /node_modules/,
    use: 'babel-loader',
}

Babel automatically looks for .babelrc or babel.config.json in the project root. This keeps the Webpack config clean and makes Babel settings reusable across tools.

Adding Babel Plugins

// .babelrc with additional plugins
{
    "presets": ["@babel/preset-env"],
    "plugins": [
        "@babel/plugin-proposal-class-properties",
        "@babel/plugin-proposal-optional-chaining"
    ]
}

Plugins handle individual transformations that are not included in presets. @babel/preset-env covers most standard features, but experimental or proposal-stage features require explicit plugins.

How Loaders Process Files

Source File (.js)
    ↓
test: /\.js$/  →  Does the filename match? Yes
    ↓
exclude: /node_modules/  →  Is it in node_modules? No
    ↓
babel-loader  →  Passes file through Babel
    ↓
@babel/preset-env  →  Transpiles modern syntax
    ↓
Webpack  →  Adds the transformed code to the bundle

Multiple Loaders

A single rule can chain multiple loaders. They execute in reverse order — from the last loader to the first (right to left in the use array). You will see this in action with CSS and SASS loaders in the next episodes.

use: ['loader-c', 'loader-b', 'loader-a']
// Execution order: loader-a → loader-b → loader-c

Updated webpack.config.js

const path = require('path');

module.exports = {
    mode: 'development',
    devtool: 'eval-source-map',
    entry: './src/index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist'),
    },
    devServer: {
        static: { directory: path.resolve(__dirname, 'dist') },
        port: 3000,
        open: true,
        hot: true,
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env'],
                    },
                },
            },
        ],
    },
};

Key Takeaways

  • Loaders teach Webpack how to process non-standard file types — they transform files before bundling
  • Babel transpiles modern JavaScript (ES6+) into code that older browsers can run
  • babel-loader connects Babel to Webpack; @babel/core is the engine; @babel/preset-env decides what to transpile
  • The test property uses regex to match files; exclude skips node_modules
  • @babel/preset-env uses your browserslist config to only transpile what is needed
  • Babel config can be inline in Webpack or in a separate .babelrc file
  • Multiple loaders in a rule execute in reverse order (right to left)