Skip to main content

Loaders

Loaders are transformations that are applied to the source code of modules. They allow you to pre-process files as you import or “load” them. Loaders can transform files from different languages (like TypeScript) to JavaScript, or load inline images as data URLs.

Overview

Webpack only understands JavaScript and JSON files natively. Loaders allow webpack to process other types of files and convert them into valid modules that can be consumed by your application and added to the dependency graph.

Using Loaders

There are three ways to use loaders: Specify loaders in your webpack configuration:
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.ts$/,
        use: 'ts-loader'
      }
    ]
  }
};

Inline

Specify loaders explicitly in each import statement:
import styles from 'style-loader!css-loader!./styles.css';
Inline loader syntax is not recommended. Use configuration-based loaders instead for better maintainability.

CLI

webpack --module-bind 'css=style-loader!css-loader'

Loader Configuration

Basic Rule

module.exports = {
  module: {
    rules: [
      {
        test: /\.txt$/,
        use: 'raw-loader'
      }
    ]
  }
};

Multiple Loaders

Loaders are evaluated/executed from right to left (or bottom to top):
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader', // 3. Inject CSS into DOM
          'css-loader',   // 2. Turn CSS into CommonJS
          'sass-loader'   // 1. Turn Sass into CSS
        ]
      }
    ]
  }
};
The order matters! Loaders transform files in a chain, passing results from one to the next.

Loader Options

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          { loader: 'style-loader' },
          {
            loader: 'css-loader',
            options: {
              modules: true,
              importLoaders: 1
            }
          },
          {
            loader: 'sass-loader',
            options: {
              sassOptions: {
                indentWidth: 2
              }
            }
          }
        ]
      }
    ]
  }
};

Rule Conditions

Test

Match files with a regular expression:
module.exports = {
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        use: 'babel-loader'
      }
    ]
  }
};

Include and Exclude

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        include: path.resolve(__dirname, 'src'),
        exclude: /node_modules/,
        use: 'babel-loader'
      }
    ]
  }
};

Resource Query

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        resourceQuery: /inline/,
        use: 'url-loader'
      }
    ]
  }
};
Matches: import './style.css?inline'

issuer

Match based on the module that imports the resource:
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        issuer: /\.js$/,
        use: 'css-loader'
      }
    ]
  }
};
This applies css-loader only when CSS is imported from JavaScript files.

Common Loaders

File Loaders

{
  test: /\.(js|jsx)$/,
  exclude: /node_modules/,
  use: {
    loader: 'babel-loader',
    options: {
      presets: ['@babel/preset-env', '@babel/preset-react']
    }
  }
}

Loader Features

Chaining

Loaders can be chained to transform files through multiple stages:
module.exports = {
  module: {
    rules: [
      {
        test: /\.scss$/,
        use: [
          'style-loader',  // 4. Inject into DOM
          'css-loader',    // 3. CSS to JS
          'postcss-loader', // 2. Process with PostCSS
          'sass-loader'    // 1. Sass to CSS
        ]
      }
    ]
  }
};

Inline Loaders

Prefixes for inline loaders:
  • ! - Disable normal loaders
  • !! - Disable all loaders (pre, normal, post)
  • -! - Disable pre and normal loaders
import styles from '!style-loader!css-loader!./styles.css';

Loader Context

Loaders execute with access to the loader API:
// Example loader implementation
module.exports = function(source) {
  // this.resourcePath - path to the file being loaded
  // this.addDependency() - add file as dependency
  // this.async() - return async callback
  // this.getOptions() - get loader options
  
  const options = this.getOptions();
  const callback = this.async();
  
  // Transform source
  const result = transform(source, options);
  
  callback(null, result);
};

How Loaders Work Internally

Webpack processes modules through NormalModule:
// From NormalModule.js (simplified)
const { runLoaders } = require('loader-runner');

class NormalModule {
  build(options, compilation, resolver, fs, callback) {
    return this.doBuild(options, compilation, resolver, fs, (err) => {
      // Run loaders
      runLoaders({
        resource: this.resource,
        loaders: this.loaders,
        context: loaderContext,
        readResource: fs.readFile.bind(fs)
      }, (err, result) => {
        // Process result
        this.parser.parse(result.result, {
          source: result.result,
          current: this,
          module: this
        });
      });
    });
  }
}

Loader Types

Synchronous Loaders

module.exports = function(source) {
  return someSyncOperation(source);
};

Asynchronous Loaders

module.exports = function(source) {
  const callback = this.async();
  
  someAsyncOperation(source, (err, result) => {
    if (err) return callback(err);
    callback(null, result);
  });
};

Pitching Loaders

module.exports = function(source) {
  return source;
};

module.exports.pitch = function(remainingRequest, precedingRequest, data) {
  // Called before normal loader execution
  // Can short-circuit loader chain
};

Best Practices

  1. Use specific test patterns - Be precise with regex to avoid unnecessary processing
  2. Minimize loader scope - Use include to limit files processed
  3. Cache loader results - Many loaders support caching options
  4. Keep loaders simple - Each loader should do one thing well
  5. Chain loaders - Combine simple loaders rather than creating complex ones

Common Patterns

JavaScript/TypeScript

module.exports = {
  module: {
    rules: [
      {
        test: /\.(js|jsx|ts|tsx)$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true
            }
          }
        ]
      }
    ]
  }
};

Styles

module.exports = {
  module: {
    rules: [
      {
        test: /\.module\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: {
                localIdentName: '[name]__[local]--[hash:base64:5]'
              }
            }
          }
        ]
      }
    ]
  }
};

Assets

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif)$/,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 8 * 1024 // 8kb
          }
        }
      }
    ]
  }
};