Next.js: Preferred method of using normalize.css?

Created on 8 Dec 2016  Â·  27Comments  Â·  Source: vercel/next.js

What is the ideal way to load normalize.css (ideally from NPM) in a performant way? I’d like to avoid loading it as a static asset and introduce a header request just for a tiny bit of CSS.

Glamor has a glamor/reset extra, but it’s a lot simpler than normalize, and not what I’m looking for (I’m not really sure how to load extras into Glamor from next anyway).

Most helpful comment

@jaydenseric you're correct that Normalize.css is the jQuery of CSS. Browsers are still pretty inconsistent in their default styling of HTML elements, Normalize.css helps deal with that problem. Normalize.css is a necessity for legacy browser support for modern projects.

All 27 comments

You can use next/head.

@nkzawa I’ve used next/head to load some global syles, but without css-loader on the project, how could I require a CSS file from node_modules? And is this cacheable on the user end or does this bloat out the payload every header request?

You'd like to define style as text in next/head like:

<Head>
  <style>{`
    body {
      margin: 0;
    }
  `}</style>
</Head>

You'd have to convert normalize.css to .json or a component to require, but this would be the best way for now IMO. If you require the file on all pages, then it bundles as a chunk and is loaded only once so it wouldn't bloat payload.

You'd be able to use css-loader too when https://github.com/zeit/next.js/pull/222 was released.

Thanks. This is what I figured, but didn’t know if there was a more automatic way of loading it currently. I guess I could run Webpack or Gulp locally to convert before next hits it.

And that’s good to know that next utilizes common chunking. I feel better about shoving stuff into the Head now. Thanks for answering my questions!

On Dec 9, 2016, at 01:11, Naoyuki Kanezawa notifications@github.com wrote:

You'd like to define style as text in next/head like:




You'd have to convert normalize.css to .json or a component to require, but this would be the best way for now IMO. If you require the file on all pages, then it bundles as a chunk and is loaded only once so it wouldn't bloat payload.

You'd be able to use css-loader too when #222 was released.

—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub, or mute the thread.

Would like to point out that glamor actually uses normalize.css albeit an outdated version v3.0.2
https://github.com/threepointone/glamor/blob/master/src/reset.js

Made a pull request
https://github.com/threepointone/glamor/pull/154

Simply importing import 'glamor/reset' works fine.

Feel free to use my fork if you need 5.0 or wait for the pull request to be merged :)

@FrancosLab Thanks for the tip! I ran across glamor/reset but didn’t notice normalize was part of it—I don’t think it was mentioned on glamor’s README. Thanks for the PR!

So what is the best way to do this right now for [email protected]? I've spent various hours trying to emulate what was done with the with-global-stylesheet example with no luck.

Because normalize.css is a package, simply copying the file isn't enough (Node's require resolution uses package.main). Moreover, emit-file-loader (and file-loader as well) seems to be behaving differently from the example repo. It seems that when passing the options name=dist/[path][name].[ext] to the loader, path always starts with -, resulting in the file being in .next/dist/-/node_modules/normalize.css/normalize.css, which is a problem.

The way to get around this is to basically copy the entire normalize.css file into the static folder or perhaps inline the entire thing into a style tag (I'm using styled components for styling, though).

I tried using webpack-copy-plugin but it looks like the static folder isn't served from .next but actually from the root folder itself <root-folder>/static (where pages lives) so that didn't work either.

Now that Next.js doesn't rely on Glamor, any tips on moving forward with this?

EDIT: Related: https://github.com/zeit/styled-jsx/issues/83, https://github.com/zeit/styled-jsx/pull/100, https://github.com/zeit/next.js/issues/544

The with-global-stylesheet example just got updated here: #1327!

1327 still doesn't work for something as simple as import 'normalize.css'. The main issue seems to be that because Webpack doesn't run on the server, you can't import non-js files on any file that runs in the server.

@migueloller maybe could switch to universal Webpack after v2.0...: https://github.com/zeit/next.js/issues/1245

@sedubois, eagerly waiting for that! 😄

you can add your vote there 😉

Guys, I've been importing normalize just fine through sass-loader, even without the includePaths that just got merged. All you have to do is install normalize-scss (the Sass port of normalize.css) and add @import '~normalize-scss'; at your highest level (custom _document, layout, or page) where you would include an scss stylesheet the way with-global-stylesheet does.

Now if you rather import the original, I bet you could setup an alias to node_modulesusing babel-plugin-module-resolver the way I just did with styles in the example and then import it with js + <style dangerouslySetInnerHTML={{ __html: normalize }} />.

@orlin, you can't simply import it with JS because it will throw an error in the server since it doesn't go through Webpack.

@migueloller, it should work fine, the same way scss is imported from js in the with-global-stylesteet example. Both css and scss are handled by Webpack loaders in the next.config.js and converted to js. I just didn't provide the import normalize from '...' as I would need to install normalize.css and setup a babel-plugin-module-resolver alias to give you a ... path that works.

@orlin,

I've cloned the example, added normalize.css, and played with it a bit. You're right, it's possible to make it work!

I had to make a custom configuration for it though, where the output name of the emitted file was dist/[path]index.js due to the fact that if you don't copy the package.json then Node's require won't find it. Because you don't want this behavior for the other styles (living in your source code and not node_modules) then you have to setup a Webpack rule just for normalize.css (plus other packages you might be using).

This works as a short-term fix but it would definitely be nice to have something that works out of the box as is being discussed in #1245 and https://github.com/zeit/styled-jsx/pull/100#issuecomment-277133969

@rauchg, do you think it would be a good idea to make an example for packages that exist in node_modules? I wouldn't mind making a new example or extending the with-global-stylesheet one.

@migueloller adding it to the same example would probably be preferable

@migueloller I found a super-clean "best-practices" way to do this. Will do a PR on with-global-stylesheet... I hope you didn't spend too much time hacking it.

Global styles, including resets or normalize, are an anti-pattern anyway. Components should control their own styles. Normalize is the jQuery of CSS.

@jaydenseric you're correct that Normalize.css is the jQuery of CSS. Browsers are still pretty inconsistent in their default styling of HTML elements, Normalize.css helps deal with that problem. Normalize.css is a necessity for legacy browser support for modern projects.

Here is the approach I use : (https://github.com/zeit/next.js/#custom-document)

// ./pages/_document.js
import Document, { Head, Main, NextScript } from 'next/document'
import flush from 'styled-jsx/server'

export default class MyDocument extends Document {
  static getInitialProps({ renderPage }) {
    const { html, head, errorHtml, chunks } = renderPage()
    const styles = flush()
    return { html, head, errorHtml, chunks, styles }
  }

  render() {
    return (
      <html>
        <Head>
          <title>My page</title>
          <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.min.css" />
        </Head>
        <body className="custom_class">
          {this.props.customValue}
          <Main />
          <NextScript />
        </body>
      </html>
    )
  }
}

@vinzcelavi Why do you flush the styles?

@sospedra I have no idea 😬 Maybe it could help : https://github.com/zeit/styled-jsx#styled-jsxserver

You probably don't want to do that but instead call Document.getInitialProps as per the updated documentation: https://github.com/zeit/next.js/#custom-document

Why isn't it just fine placing a link tag with the CDN url inside the Head? It worked for me.

@janoist1 I think the problem here is that we’d like to serve normalize ourselves from next, rather then relying on an external CDN. It’s fine in development, but I don’t want to rely on anything external in production.

Here are two ways of solving this if using next-css isn't an option for you (perhaps you're using CSS modules so importing a CSS file from _app won't apply globally).

We first include a link inside the Head for normalize.css and then apply some custom global styles via <style type='text/css'>{globalStyles}</style>

import React from 'react'
import Document, { Head, Main, NextScript } from 'next/document'

const globalStyles = `
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
`

export default class MyDocument extends Document {
  render () {
    return (
      <html>
        <Head>
          <meta name='viewport' content='width=device-width, initial-scale=1' />
          <meta charSet='utf-8' />

          <link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css' />

          <style type='text/css'>{globalStyles}</style>
        </Head>

        <body>
          <Main />
          <NextScript />
        </body>
      </html>
    )
  }
}
Was this page helpful?
0 / 5 - 0 ratings