Use the parts of normalize.css (or sanitize.css) you need from your browserslist

  • By CSS Tools
  • Last update: Jan 1, 2023
  • Comments: 17

PostCSS Normalize PostCSS

npm version build status support chat

PostCSS Normalize lets you use the parts of normalize.css or sanitize.css that you need from your browserslist.

@import "normalize.css";
@import "sanitize.css";

PostCSS Normalize uses a non-opinionated version of normalize.css, but an opinionated version may also be used.

@import "normalize.css/opinionated.css";


Here is a sample of what normalize.css looks like when the browserslist is ie >= 9:

 * Add the correct display in IE 9-.

video {
  display: inline-block;

 * Remove the border on images inside links in IE 10-.

img {
  border-style: none;

And here is the same sample when the browserslist is ie >= 10:

 * Remove the border on images inside links in IE 10-.

img {
  border-style: none;


Add PostCSS Normalize to your project:

npm install postcss-normalize --save-dev

Add a browserslist entry in package.json:

  "browserslist": "last 2 versions"

Use PostCSS Normalize to process your CSS:

const postcssNormalize = require('postcss-normalize')

postcssNormalize.process(YOUR_CSS /*, processOptions, pluginOptions */)

Or use it as a PostCSS plugin:

const postcss = require('postcss')
const postcssNormalize = require('postcss-normalize')

  postcssNormalize(/* pluginOptions */)
]).process(YOUR_CSS /*, processOptions */)

PostCSS Normalize runs in all Node environments, with special instructions for:

Node PostCSS CLI Webpack Create React App Gulp Grunt

PostCSS Import Usage

PostCSS Normalize includes a postcssImport function to configure PostCSS Import and allow you to continue using the @import syntax.

const postcss = require('postcss')
const postcssImport = require('postcss-import')
const postcssNormalize = require('postcss-normalize')

      /* pluginOptions (for PostCSS Normalize) */
      /* pluginOptions (for PostCSS Import) */
]) // now you can use @import "normalize.css", etc. again

Alternatively, use @import-normalize or @import-sanitize to avoid conflicts with @import transforms.

@import-normalize "opinionated.css";



The allowDuplicates option determines whether multiple, duplicate insertions of CSS libraries are allowed. By default, duplicate libraries are omitted.

postcssNormalize({ allowDuplicates: true })


The forceImport option defines CSS libraries that will be inserted at the beginning of the CSS file. Unless overriden by allowDuplicates, duplicate CSS libraries would still be omitted.

postcssNormalize({ forceImport: true })

Specific CSS libraries may be defined.

  forceImport: 'sanitize.css'


The browsers option defines an override of the project’s browserslist for PostCSS Normalize. This option should be avoided in leui of a browserslist file.

postcssNormalize({ browsers: 'last 2 versions' })

CSS Libraries

PostCSS Normalize can include normalize.css or sanitize.css and configure either with the following combinations:

@import "normalize"; /* also, @import "normalize.css" */
@import "normalize/opinionated"; /* also, @import "normalize.css/opinionated.css", @import "normalize.css/*" */
@import "sanitize"; /* also, @import "sanitize.css" */
@import "sanitize/assets"; /* also, @import "sanitize.css/assets.css" */
@import "sanitize/forms"; /* also, @import "sanitize.css/forms.css" */
@import "sanitize/reduce-motion"; /* also, @import "sanitize.css/reduce-motion.css" */
@import "sanitize/system-ui"; /* also, @import "sanitize.css/system-ui.css" */
@import "sanitize/typography"; /* also, @import "sanitize.css/typography.css" */
@import "sanitize/ui-monospace"; /* also, @import "sanitize.css/ui-monospace.css" */
@import "sanitize/*"; /* also, @import "sanitize.css/*" (sanitize + all additions) */



  • 1

    UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): TypeError: Cannot read property 'indexOf' of undefined

    First of all, thanks for your work @jonathantneal

    I'd like to report a bug I'm facing while trying to integrate your plugin. Here's my script:

    If I remove the lines 20-22 (those adding the postcss-normalize plugin), all works fine. Any idea?

    Error message:

    UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): TypeError: Cannot read property 'indexOf' of undefined

    I use Node.js v6.9.5


  • 2

    Uncareted `sanitize.css` dependency causing failure while requiring `postcss-normalize`

    [email protected] removes file page.css, which is directly referenced in cssMap.js:

    As sanitize.css is installed at version * rather than something like 12.0.0 or ^12.0.0, npm users are auto-upgraded into this breaking sanitize.css version. This includes users of create-react-app's react-scripts.

  • 3

    Error: Cannot find module '@csstools/normalize.css'

    That's closely related to this (locked) issue here:

    With the difference that I'm not using create-react-app but a custom setup (that is somewhat similar to CRA) and I'm still getting this error after upgrading from 7.0.1 to 8.0.0. I've already removed node_modules and re-installed everything again as it was suggested in the other issue but it's still not working with the same error: Cannot find module '@csstools/normalize.css'.

    Can be reproduced by cloning this repo, upgrading postcss-normalize to 8.0.0 and then running yarn start.

    Any idea what else I have to change to make postcss and postcss-loader find the normalize.css file?

  • 4

    Compatibility with PostCSS 8

    Thanks for your PostCSS plugin!

    As you probably know, PostCSS 8 was released a few days ago. It would be great if you could make this plugin compatible with the latest version. The creators of the project have even released this migration guide.

  • 5

    Injects normalize.css multiple times

    I am using this with module-css with ExtractTextPlugin and it's injecting the normalize.css before every single css file I have in my project above each file's contents. Which means my stitched together css has numerous copies of normalize.css.

    module: {
            loaders: [
                    test: /\.(css|scss)$/,
                    loader: ExtractTextPlugin.extract('css-loader?sourceMap&modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss-loader!sass-loader?sourceMap'),
                    exclude: /node_modules/
        postcss: [
        plugins: [
            new ExtractTextPlugin('style.css', { allChunks: true })

    How do I ensure it only includes it one time at the top of the style.css file?

  • 6

    Feature/postcss 8

    This pull request upgrade to postcss@8

    The test will fail because the current version of postcss-browser-comments still use postcss@7

    should be merged after

    Related Issue

    postcss-browser-comments create-react-app cannot upgrade to postcss@8

  • 7

    Severe performance degradation caused by postcss-browser-comments

    After profiling my webpack builds multiple times I have noticed that postcss-browser-comments is present in almost every task that I zoomed into. After disabling postcss-normalize my build time sped up by 5 times. It seems that postcss-browser-comments performs highly unoptimized transformations on every single CSS file. This side effect is not really explicit in the readme and probably should be dealt with. I will create a similar issue in the postcss-browser-comments repo. (in case it can be fixed in that repo, otherwise I'd prefer having a setting to disable it completely)

    Attaching my performance snapshots:

    The slow one is with postcss-normalize enabled and the fast one is without.

    Perhaps postcss-browser-comments should be run only on the reset.css, instead of every input it gets.

  • 8

    Custom Import breaks SCSS imports

    In the past I've loved using your plugin for this, but I'm currently running into an issue (specifically in this case with laraval mix, but would apply to any precompiled versions of CSS) where the custom import declaration breaks builds for SASS/SCSS due to the fact that its not recognizing the import signature.

    I get that its a postCSS tool, but there are lots of people who use PostCSS in conjunction with other formats of CSS processing. Thoughts on how to make it so that it works not only in the PostCSS landscape, but also with other precompiled formats?

  • 9

    normalize disapears after second compilation

    I've included this lib and the first time webpack builds, normalize is included fine, but after a second compile (after a css change somewhere else), normalize is no longer imported.


    Any ideas?

  • 10

    Optional insertion point?

    First of all whoa, love what you've done with this plugin @jonathantneal!

    What do you think about providing an optional insertion point hook for the Normalize styles? It's common for buildsteps to operate on multiple CSS files and since postcss-normalize blindly injects into everything it finds you could easily end up with duplicated code without realising, which defeats the purpose of saving bytes with browserlist. This is especially true of more complex SPAs and the like.

    Of course you can mitigate this with a dedicated buildstep just for postcss-normalize, but it seems like a gotcha for users.

    Maybe something like

    /* Inject browserlisted Normalize here */
    .rest-of-my-rules {
    require('postcss-normalize').process(YOUR_CSS, {
      atRule: true

    Then if it doesn't find the @normalize rule in a given document it doesn't inject anything. Also gives this method with the current standard of importing Normalize.css with postcss-import, ie:

    /* Inject Normalize here */
    @import 'normalize.css'
    .rest-of-my-rules {
  • 11

    Anyway to have single insertion across multiple compiles?

    I'm using vue, configured the vue-loader to render the .vue file's <style> section to use postcss.

    As each component get its own <style>, this plugin would inject itself multiple times. I would like to have a configuration such that it only inject when @import-normalize is found. So that I can control it to appear only on top-level component. Like:

    plugins : [ 
        require('postcss-normalize')({ injectByImportOnly: true })

    I can workaround it by download the normalize.css and use postcss-import, but it that way I cannot benefit from the browserlist to output a smaller file to my targeted browser.

  • 12

    Fix resolution of @csstools/normalize.css path when using ESM

    When using the postcss-normalize ESM export, the resolution of the @csstools/normalize.css package location fails for me with an Error: Cannot find module '@csstools/normalize.css'.

    Not entirely sure if this is due to using yarn workspaces or a Node version incompatibility, but using the official module.createRequire() and requireResolve() APIs instead of undocumented internals fixes the issue. Confirmed to work on Node v18, but the APIs are available all the way down to Node v12, so this shouldn't break anything.

    Also had to fix the tests to reflect changes in @csstools/normalize.css v12.0.0.

  • 13

    Yarn 2 with PnP error

    I'm using Next.js with a Custom PostCSS config adding this package as well as installing all with Yarn 2. The problem I'm facing is when i try to run yarn dev it gives me this error:

    warn  - SWC minify beta enabled.
    Error: postcss-browser-comments tried to access browserslist (a peer dependency) but it isn't provided by your application; this makes the require call ambiguous and unsound.
    Required package: browserslist
    Required by: postcss-browser-comments@virtual:e68dadf93bc66bf01a9feba2da6a5754dc8f4a54bb6236eb2be70bb5ca41d96f6c6a2fbd7bc879da195ab4b6b7b7dd4437dcec9ededdae5c4bd360875b38b13c#npm:4.0.0 (via /home/herrlegno/personal/website/.yarn/__virtual__/postcss-browser-comments-virtual-d17c7d6d1d/0/cache/
    Ancestor breaking the chain: website@workspace:.
    Require stack:
    - /home/herrlegno/personal/website/.yarn/__virtual__/postcss-browser-comments-virtual-d17c7d6d1d/0/cache/
    - /home/herrlegno/personal/website/.yarn/__virtual__/postcss-normalize-virtual-e68dadf93b/0/cache/
    - /home/herrlegno/personal/website/.yarn/__virtual__/next-virtual-21bdc7957b/0/cache/
    - /home/herrlegno/personal/website/.yarn/__virtual__/next-virtual-21bdc7957b/0/cache/
    - /home/herrlegno/personal/website/.yarn/__virtual__/next-virtual-21bdc7957b/0/cache/
    - /home/herrlegno/personal/website/.yarn/__virtual__/next-virtual-21bdc7957b/0/cache/
    - /home/herrlegno/personal/website/.yarn/__virtual__/next-virtual-21bdc7957b/0/cache/
    - /home/herrlegno/personal/website/.yarn/__virtual__/next-virtual-21bdc7957b/0/cache/
    - /home/herrlegno/personal/website/.yarn/__virtual__/next-virtual-21bdc7957b/0/cache/
    - /home/herrlegno/personal/website/.yarn/__virtual__/next-virtual-21bdc7957b/0/cache/
    - /home/herrlegno/personal/website/.yarn/__virtual__/next-virtual-21bdc7957b/0/cache/
    - /home/herrlegno/personal/website/.yarn/__virtual__/next-virtual-21bdc7957b/0/cache/
        at Function.external_module_.Module._resolveFilename (/home/herrlegno/personal/website/.pnp.cjs:16792:55)
        at Function.mod._resolveFilename (/home/herrlegno/personal/website/.yarn/__virtual__/next-virtual-21bdc7957b/0/cache/
        at Function.external_module_.Module._load (/home/herrlegno/personal/website/.pnp.cjs:16591:48)
        at Module.require (node:internal/modules/cjs/loader:1005:19)
        at require (node:internal/modules/cjs/helpers:102:18)
        at Object.<anonymous> (/home/herrlegno/personal/website/.yarn/__virtual__/postcss-browser-comments-virtual-d17c7d6d1d/0/cache/
        at Module._compile (node:internal/modules/cjs/loader:1101:14)
        at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
        at Module.load (node:internal/modules/cjs/loader:981:32)
        at Function.external_module_.Module._load (/home/herrlegno/personal/website/.pnp.cjs:16641:14)
    Process finished with exit code 1

    I also ran yarn dlx @yarnpkg/doctor and it gave me this:

    ➤ YN0000: ┌ Resolution step
    ➤ YN0000: └ Completed in 7s 216ms
    ➤ YN0000: ┌ Post-resolution validation
    ➤ YN0068: │ postcss-browser-comments ➤ dependencies ➤ browserslist: No matching package in the dependency tree; you may not need this rule anymore.
    ➤ YN0000: └ Completed
    ➤ YN0000: ┌ Fetch step
    ➤ YN0013: │ toposort@npm:2.0.2 can't be found in the cache and will be fetched from the remote registry
    ➤ YN0013: │ treeify@npm:1.1.0 can't be found in the cache and will be fetched from the remote registry
    ➤ YN0013: │ tunnel@npm:0.0.6 can't be found in the cache and will be fetched from the remote registry
    ➤ YN0013: │ typanion@npm:3.7.1 can't be found in the cache and will be fetched from the remote registry
    ➤ YN0013: │ yup@npm:0.32.11 can't be found in the cache and will be fetched from the remote registry
    ➤ YN0000: └ Completed in 2s 734ms
    ➤ YN0000: ┌ Link step
    ➤ YN0000: │ ESM support for PnP uses the experimental loader API and is therefore experimental
    ➤ YN0000: └ Completed
    ➤ YN0000: Done with warnings in 10s 123ms
    ➤ YN0000: Found 1 package(s) to process
    ➤ YN0000: For a grand total of 6 file(s) to validate
    ➤ YN0000: ┌ /home/herrlegno/personal/website/package.json
    ➤ YN0000: │ /home/herrlegno/personal/website/package.json:26:26: Unmet transitive peer dependency on browserslist@>= 4, via postcss-normalize@^10.0.1
    ➤ YN0000: └ Completed in 10s 495ms
    ➤ YN0000: ⠼ --------------------------------------------------------------------------------
    ➤ YN0000: Failed with errors in 10s 496ms

    I'm new to the Yarn 2 package handling approach, so am I in the right place? Thanks.

  • 14

    Reconsider asterisk-versioning of @csstools/normalize.css and sanitize.css

    broken out of and


    [email protected] introduced support for normalize and sanitze.css via

    This generous version range broke v10 and v9 ecosystems when sanitize.css removed a file that postcss-normalize imported. resolved the issue for v10, but this is not addressing the root cause, only fixing the problem until it breaks again. The changelog even alludes to the uncomfortable position we are in.


    Are you in theory amenable to work to remove asterisk-based versioning for direct dependencies, whether done yourself or contributed from the community? Previous comments linked above in #58 seem to indicate yes.

    • takes steps in this direction
    • me and my team would be happy to contribute this back for the 10 and 9 lines.
  • 15

    V9: Cannot find module 'sanitize.css/page.css'

    Related: #60

    Can we also have a 9.0.1 patch please ?

    I'm stuck with PostCSS 7 in my project and it looks like postcss-normalize 9.0.0 now breaks every project build with this error:

    Syntax Error: Error: Loading PostCSS Plugin failed: Cannot find module 'sanitize.css/page.css'


  • 16

    Node 10 support

    Hi there, we're upgrading dependencies in CRA and we see that you've dropped support for Node 10, which we're still supporting today (although we plan to deprecated).

    It looks like this isn't an issue for PostCSS 8, so I wondered if it was that changes you made mean this no longer works in Node 10.x, or if this was around support only?

    If it's the latter, would you consider reverting that - or if not, would it be OK if we fork this until we drop Node 10 if that's OK?

  • 17

    Use PostCSS 8 events

    Using Once is a bad practice, because it doesn't guarantee plugin comparability (for instance, if @postcss-normalize will be inserted by mixin).

    For this plugin AtRule is much better event.