postcss-loader

Loader for webpack to process CSS with PostCSS

Install

npm i -D postcss-loader

Usage

Configuration

postcss.config.js

module.exports = {
  parser: 'sugarss',
  plugins: {
    'postcss-import': {},
    'postcss-preset-env': {},
    cssnano: {},
  },
};

You can read more about common PostCSS Config here.

Config Cascade

You can use different postcss.config.js files in different directories. Config lookup starts from path.dirname(file) and walks the file tree upwards until a config file is found.

|– components
| |– component
| | |– index.js
| | |– index.png
| | |– style.css (1)
| | |– postcss.config.js (1)
| |– component
| | |– index.js
| | |– image.png
| | |– style.css (2)
|
|– postcss.config.js (1 && 2 (recommended))
|– webpack.config.js
|
|– package.json

After setting up your postcss.config.js, add postcss-loader to your webpack.config.js. You can use it standalone or in conjunction with css-loader (recommended). Use it before css-loader and style-loader, but after other preprocessor loaders like e.g sass|less|stylus-loader, if you use any (since webpack loaders evaluate right to left/bottom to top).

webpack.config.js

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

⚠️ When postcss-loader is used standalone (without css-loader) don't use @import in your CSS, since this can lead to quite bloated bundles

webpack.config.js (recommended)

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          { loader: 'css-loader', options: { importLoaders: 1 } },
          'postcss-loader',
        ],
      },
    ],
  },
};

Options

Name Type Default Description

Name

Type

Default

Description

exec

{Boolean}

{Boolean} undefined Enable PostCSS Parser support in CSS-in-JS

Name

Type

Default

Description

parser

{String\|Object\|Function}

{String\|Object\|Function} undefined Set PostCSS Parser

Name

Type

Default

Description

syntax

{String\|Object}

{String\|Object} undefined Set PostCSS Syntax

Name

Type

Default

Description

stringifier

{String\|Object\|Function}

{String\|Object\|Function} undefined Set PostCSS Stringifier

Name

Type

Default

Description

config

{String\|Object\|Boolean}

{String\|Object\|Boolean} undefined Set postcss.config.js config path && ctx

Name

Type

Default

Description

plugins

{Function\|Object\|Array<Function\|Object>}

{Function\|Object\|Array<Function\|Object>} [] Set PostCSS Plugins

Name

Type

Default

Description

sourceMap

{String\|Boolean}

{String\|Boolean} compiler.devtool Enables/Disables generation of source maps

Exec

Type: Boolean Default: undefined

If you use JS styles without the postcss-js parser, add the exec option.

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.style.js$/,
        use: [
          'style-loader',
          { loader: 'css-loader', options: { importLoaders: 1 } },
          {
            loader: 'postcss-loader',
            options: { parser: 'sugarss', exec: true },
          },
        ],
      },
    ],
  },
};

Config

Type: Boolean|String|Object Default: undefined

Options specified in the config file are combined with options passed to the loader. Loader options overwrite options from config.

Boolean

Enables/Disables autoloading config.

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: 'postcss-loader',
        options: {
          config: false,
        },
      },
    ],
  },
};

String

Allows to specify the absolute path to the config file.

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: 'postcss-loader',
        options: {
          config: path.resolve(__dirname, 'custom.config.js'),
        },
      },
    ],
  },
};

Object

Name Type Default Description

Name

Type

Default

Description

path

{String}

{String} undefined PostCSS Config Directory

Name

Type

Default

Description

context

{Object}

{Object} undefined PostCSS Config Context
Path

Type: String Default: undefined

You can manually specify the path to search for your config (postcss.config.js) with the config.path option. This is needed if you store your config in a separate e.g ./config || ./.config folder.

⚠️ Otherwise it is unnecessary to set this option and is not recommended

⚠️ Note that you can't use a filename other than the supported config formats (e.g .postcssrc.js, postcss.config.js), this option only allows you to manually specify the directory where config lookup should start from

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: 'postcss-loader',
        options: {
          config: {
            path: 'path/to/.config/', // ✅
            path: 'path/to/.config/css.config.js', // ❌
          },
        },
      },
    ],
  },
};
Context (ctx)

Type: Object Default: undefined

Name Type Default Description

Name

Type

Default

Description

env

{String}

{String} 'development' process.env.NODE_ENV

Name

Type

Default

Description

file

{Object}

{Object} loader.resourcePath extname , dirname , basename

Name

Type

Default

Description

options

{Object}

{Object} {} Options

postcss-loader exposes context ctx to the config file, making your postcss.config.js dynamic, so can use it to do some real magic ✨

postcss.config.js

module.exports = ({ file, options, env }) => ({
  parser: file.extname === '.sss' ? 'sugarss' : false,
  plugins: {
    'postcss-import': { root: file.dirname },
    'postcss-preset-env': options['postcss-preset-env']
      ? options['postcss-preset-env']
      : false,
    cssnano: env === 'production' ? options.cssnano : false,
  },
});

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: 'postcss-loader',
        options: {
          config: {
            ctx: {
              'postcss-preset-env': { ...options },
              cssnano: { ...options },
            },
          },
        },
      },
    ],
  },
};

Plugins

Type: Function|Object|Array<Function\|Object> Default: []

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: 'postcss-loader',
        options: {
          plugins: (loader) => [
            require('postcss-import')({ root: loader.resourcePath }),
            require('postcss-preset-env')(),
            require('cssnano')(),
          ],
        },
      },
    ],
  },
};

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: 'postcss-loader',
        options: {
          plugins: {
            'postcss-import': {},
            'postcss-nested': {},
            'postcss-short': { prefix: 'x' },
          },
        },
      },
    ],
  },
};

It is possible to disable the plugin specified in the config.

postcss.config.js

module.exports = {
  plugins: {
    'postcss-short': { prefix: 'x' },
    'postcss-import': {},
    'postcss-nested': {},
  },
};

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: 'postcss-loader',
        options: {
          plugins: {
            'postcss-import': {},
            'postcss-nested': {},
            // Turn off the plugin
            'postcss-short': false,
          },
        },
      },
    ],
  },
};

Syntaxes

Type: String|Object Default: undefined

Name Type Default Description

Name

Type

Default

Description

parser

{String\|Object}

{String\|Object} undefined Custom PostCSS Parser

Name

Type

Default

Description

syntax

{String\|Object}

{String\|Object} undefined Custom PostCSS Syntax

Name

Type

Default

Description

stringifier

{String\|Object\|Function}

{String\|Object\|Function} undefined Custom PostCSS Stringifier

Parser

Type: String|Object|Function Default: undefined

String

The passed string is converted to the form require('string').

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.sss$/i,
        loader: 'postcss-loader',
        options: {
          // Will be converted to `require('sugarss')`
          parser: 'sugarss',
        },
      },
    ],
  },
};
Object

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.sss$/i,
        loader: 'postcss-loader',
        options: {
          parser: require('sugarss'),
        },
      },
    ],
  },
};
Function

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.sss$/i,
        loader: 'postcss-loader',
        options: {
          parser: require('sugarss').parse,
        },
      },
    ],
  },
};

Syntax

Type: String|Object Default: undefined

String

The passed string is converted to the form require('string').

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: 'postcss-loader',
        options: {
          // Will be converted to `require('sugarss')`
          syntax: 'sugarss',
        },
      },
    ],
  },
};
Object

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: 'postcss-loader',
        options: {
          stringifier: require('sugarss'),
        },
      },
    ],
  },
};

Stringifier

Type: String|Object|Function Default: undefined

String

The passed string is converted to the form require('string').

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: 'postcss-loader',
        options: {
          // Will be converted to `require('sugarss')`
          stringifier: 'sugarss',
        },
      },
    ],
  },
};
Object

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: 'postcss-loader',
        options: {
          stringifier: require('sugarss'),
        },
      },
    ],
  },
};
Function

webpack.config.js

const Midas = require('midas');
const midas = new Midas();

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: 'postcss-loader',
        options: {
          stringifier: midas.stringifier,
        },
      },
    ],
  },
};

SourceMap

Type: Boolean|String Default: compiler.devtool

By default generation of source maps depends on the devtool option. All values enable source map generation except eval and false value. In most cases this option should be discouraged. If need postcss-loader to generate an inline map, use the inline value. postcss-loader will use the previous source map given by other loaders and update it accordingly, if no previous loader is applied before postcss-loader, the loader will generate a source map for you.

webpack.config.js

module.exports = {
  devtool: 'source-map',
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          { loader: 'style-loader', options: { sourceMap: true } },
          { loader: 'css-loader' },
          { loader: 'postcss-loader' },
          { loader: 'sass-loader' },
        ],
      },
    ],
  },
};

'inline'

You can set the sourceMap: 'inline' option to inline the source map within the CSS directly as an annotation comment.

webpack.config.js

module.exports = {
  devtool: 'source-map',
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          { loader: 'style-loader', options: { sourceMap: true } },
          { loader: 'css-loader' },
          { loader: 'postcss-loader', options: { sourceMap: 'inline' } },
          { loader: 'sass-loader' },
        ],
      },
    ],
  },
};
.class {
  color: red;
}

/*# sourceMappingURL=data:application/json;base64, ... */

Examples

Stylelint

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              plugins: [require('postcss-import')(), require('stylelint')()],
            },
          },
        ],
      },
    ],
  },
};

Autoprefixing

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              plugins: [require('autoprefixer')({ ...options })],
            },
          },
        ],
      },
    ],
  },
};

:warning: postcss-preset-env includes autoprefixer, so adding it separately is not necessary if you already use the preset.

CSS Modules

This loader cannot be used with CSS Modules out of the box due to the way css-loader processes file imports. To make them work properly, either add the css-loader’s importLoaders option.

webpack.config.js

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

or use postcss-modules instead of css-loader.

CSS-in-JS

If you want to process styles written in JavaScript, use the postcss-js parser.

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.style.js$/,
        use: [
          'style-loader',
          { loader: 'css-loader', options: { importLoaders: 2 } },
          { loader: 'postcss-loader', options: { parser: 'postcss-js' } },
          'babel-loader',
        ],
      },
    ],
  },
};

As result you will be able to write styles in the following way

import colors from './styles/colors';

export default {
  '.menu': {
    color: colors.main,
    height: 25,
    '&_link': {
      color: 'white',
    },
  },
};

:warning: If you are using Babel you need to do the following in order for the setup to work

  1. Add babel-plugin-add-module-exports to your configuration
  2. You need to have only one default export per style module

Extract CSS

webpack.config.js

const devMode = process.env.NODE_ENV !== 'production';

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
        ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: devMode ? '[name].css' : '[name].[hash].css',
    }),
  ],
};

Emit assets

To write a asset from the postcss plugin to the webpack's output file system, need to add a message in result.messages. The message should contain the following fields:

  • type = asset - Message type (require, should be equal asset)
  • file - file name (require)
  • content - file content (require)
  • sourceMap - sourceMap
  • info - asset info

webpack.config.js

const customPlugin = () => (css, result) => {
  result.messages.push({
    type: 'asset',
    file: 'sprite.svg',
    content: '<svg>...</svg>',
  });
};

const postcssPlugin = postcss.plugin('postcss-assets', customPlugin);

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              plugins: [postcssPlugin()],
            },
          },
        ],
      },
    ],
  },
};

Add dependencies

There are two way to add dependencies:

  1. (Recommended). Postcss plugin should emit message in result.messages.

The message should contain the following fields:

  • type = dependency - Message type (require, should be equal dependency)
  • file - absolute file path (require)

webpack.config.js

const path = require('path');

const customPlugin = () => (css, result) => {
  result.messages.push({
    type: 'dependency',
    file: path.resolve(__dirname, 'path', 'to', 'file'),
  });
};

const postcssPlugin = postcss.plugin('postcss-assets', customPlugin);

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              plugins: [postcssPlugin()],
            },
          },
        ],
      },
    ],
  },
};
  1. Pass loaderContext in plugin.

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              config: 'path/to/postcss.config.js',
            },
          },
        ],
      },
    ],
  },
};

postcss.config.js

module.exports = (loaderContext) => ({
  plugins: [require('path/to/customPlugin')(loaderContext)],
});

customPlugin.js

const path = require('path');

const customPlugin = (loaderContext) => (css, result) => {
  loaderContext.webpack.addDependency(
    path.resolve(__dirname, 'path', 'to', 'file')
  );
};

module.exports = postcss.plugin('postcss-assets', customPlugin);

Maintainers


Michael Ciniawsky

Alexander Krasnoyarov