# Webpack Configuration

Redwood uses webpack. And with webpack, comes configuration.

One of Redwood's tenets is convention over configuration. So it's worth repeating that you don't have to do any of this. Take the golden path, and everything will work just fine.

But another of Redwood's tenets is to make the hard stuff possible. Whether Webpack configuration falls into the hard-stuff category or not is up for debate. But one thing we know for sure is it can be an epic time sink. We hope that documenting it well will make this process fast, easy, and maybe even enjoyable.

While configuring webpack, at some point, you may wonder what exactly our configuration is. The following section aims to explain that. But if you just want to configure webpack, jump to Configuring Webpack.

# Redwood's Webpack Configuration Files

You can find Redwood's webpack config files in @redwoodjs/core's config directory. Redwood has four webpack configs:

File Description
webpack.common.js Base config; merges with development, production, and user-defined configs
webpack.development.js Config used when developing locally
webpack.production.js Config used when building for production
webpack.stat.js Config used when --stats is provided to yarn rw build; merges the production config with Webpack Bundle Analyzer

# webpack.common.js

This is the base config; it merges with the development and production configs, configures options commmon to both, and merges the user-defined config along the way.

This is where the bulk of the configuration happens. Since we distinguish between a development and production configs, we do as the docs say and export a function.

Here's a table of the plugins we use:

Plugin Description
HtmlWebpackPlugin Simplifies the creation of HTML files to serve your webpack bundles
DirectoryNamedWebpackPlugin Makes it possible to control what file within directory will be treated as entry file
MiniCssExtractPlugin Extracts CSS into separate files
CopyWebpackPlugin Copies individual files or entire directories, which already exist, to the build directory
DotenvPlugin Expose (a subset of) dotenv variables

# webpack.development.js

This is the config used when developing locally. yarn rw dev starts webpack-dev-server with this config.

With webpack dev server, files aren't written to disk, so you won't see anything in dist. Nor do we do any optimiztions.

The main thing to configure here is devServer. But you can already configure many of its options via redwood.toml—see App Configureation: redwood.toml.

# webpack.production.js

This is the config used when building for production (yarn rw build). There's not much here right now.

# webpack.stat.js

Redwood bundles with webpack.stat.js if you provide the --stats option to yarn rw build:

yarn rw build --stats

Note that this'll skip building the api side.

This config uses Webpack Bundle Analyzer. When it finishes it'll launch an interactive zoomable treemap in your browser to examine the contents of all your bundles.

# Supported Extensions and Loaders

When a file is imported, one of the following loaders will be run. They're executed in the following order:

  1. .md, .test.js, .stories.js: null-loader
  2. .js, .jsx, .ts., .tsx: babel
  3. .css, .scss, .module.css, .module.scss: sass-loader, css-loader, style-loader. Sass requires sass-loader and sass to be installed.
  4. .png, .jpg, .gif: url-loader
  5. .svg: svg-react-loader
  6. *: file-loader

Redwood's webpack configuration comes with postcss-loader. It's ready to plug in your postcss-loader config, ./web/config/postcss.config.js:

// ./web/config/postcss.config.js

module.exports = {
    plugins: {
        'autoprefixer': {},

# Configuring Webpack

You can configure webpack by adding a ./web/config/webpack.config.js file to your app. Note that the config directory doesn't exist by default; you'll have to create it.

Two formats are supported:

  1. Mutating the base config by exporting a function:
module.exports = (config, { env }) => {
  if (env === 'development') {
    // Add dev plugin


  return config

  1. Merging the base config by exporting an object:
module.exports = {
  module: {
    rules: [{...}]

There's a couple reasons for using one format over the other. You can only configure a speicifc environment (i.e. development, production) using the first format. And as the Changing the Title of the Page example shows, you can also only modify existing rules using the first format.

If you're adding extras, the second format should work just fine.

# Examples

# Changing the Title of the Page

It's actually easier to change this in ./web/index.html. But this still serves as a good example of how to configure webpack.

By default, the title of the page will be the same as your app's base directory. For example, if your app's base directory is redwood-app, you'll see "redwood-app":


This is set in webpack.common.js:

// redwood/packages/core/config/webpack.common.js

new HtmlWebpackPlugin({
  title: path.basename(redwoodPaths.base),  template: path.resolve(redwoodPaths.base, 'web/src/index.html'),
  inject: true,
  chunks: 'all',

To change this, in your ./web/config/webpack.config.js, search config's plugins array for HtmlWebpackPlugin and change it's title option. Note that, here, we're using the first format for configuring webpack, exporting a function:

// ./web/config/webpack.config.js

module.exports = (config, { env }) => {
  config.plugins.forEach((plugin) => {
    if ( === 'HtmlWebpackPlugin') {
      plugin.options.title = 'Some Custom Title'    }

  return config

Now, back in the browser, you'll see:


# Adding Tailwind CSS

This section is inspired by's excellent blog post, Adding Tailwind CSS to RedwoodJS. Note that the webpack.config.js file is no longer necessary since Redwood's webpack configuration now comes with postcss-loader by default (see Supported Extensions and Loaders). Neither is PurgeCSS, since, as of Tailwind CSS v1.4, it's built-in.

While following this example is a great way to learn about configuration in Redwood, do note that you can skip this section entirely and use the Tailwind CSS generator instead:

yarn rw generate util tailwind

First, install the development dependencies:

cd web
yarn add -D postcss-loader tailwindcss autoprefixer

As mentioned, we don't have to tell webpack to use postcss-loader. But we do have to configure it:

mkdir config
touch config/postcss.config.js
// ./web/config/postcss.config.js

const path = require('path')

module.exports = {
  plugins: [
    require('tailwindcss')(path.resolve(__dirname, '../tailwind.config.js')),

We've used the word "postcss-loader" three times, and it's because there's actually three different things: 1) the postcss-loader "rule" in webpack, which is there by default, 2) the postcss-loader package, and 3) the configuration for the postcss-loader rule, which is what goes in postcss.config.js. Welcome to the wonderful world of configuration!

Now, initialize tailwind:

yarn tailwindcss init

This generates the tailwind config file, tailwind.config.js. Note that, ordirinarly, we'd move this file to web/config, like we did with postcss.config.js. But since the Tailwind CSS IntelliSense won't work unless tailwind.config.js is in a base directory like web, we're just leaving this one here for now.

Finally, use the tailwind directives in web/src/index.css

/* ./web/src/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

And that should be it!

# Enabling Sass

Support for Sass is already configured, so all you have to do is install the development dependencies:

cd web
yarn add -D sass-loader sass