DEV Community

Cover image for Next.js + Styled Components The Really Simple Guide ▲ + 💅
Adrian Prieto
Adrian Prieto

Posted on

Next.js + Styled Components The Really Simple Guide ▲ + 💅

I recently started using Next.js, and quite honestly I have to say, is an awesome tool, you get a lot out of the box, and while there are some things I don't necessary agree with, overall it makes really simple to create static and server‑rendered applications.

Next comes bundled with styled-jsx, which is a full, scoped and component-friendly CSS support for JSX (rendered on the server or the client), and while this is great, I rather use styled components, it's just my preference.

This guide features how you use a different styling solution than styled-jsx that also supports universal styles. That means we can serve the required styles for the first render within the HTML and then load the rest in the client.

Next.js has an example repo that already comes with styled components, but you need to clone it and then try to understand what is happening under the hood, I decided to make this quick and really simple guide that illustrates the process of making styled components work with next.js.

Lets get to it!

1. Create a project directory and install next and react dependencies



mkdir my-next-app && cd my-next-app && yarn add next react react-dom



Enter fullscreen mode Exit fullscreen mode

Next.js only supports React 16.
We had to drop React 15 support due to the way React 16 works and how we use it.

2. Add scripts to your package.json



{
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start"
  },
  "dependencies": {
    "next": "^...",
    "react": "^...",
    "react-dom": "^..."
  }
}


Enter fullscreen mode Exit fullscreen mode

After that, the file-system is the main API. Every .js file becomes a route that gets automatically processed and rendered.

3. Create a /pages directory and your first page.

From your project root directory:



mkdir pages && touch pages/index.js


Enter fullscreen mode Exit fullscreen mode

Populate ./pages/index.js:



export default () => (
   <div>
       <h1>My First Next.js Page</h1>
   </div>
)


Enter fullscreen mode Exit fullscreen mode

and then just run yarn dev and go to http://localhost:3000.

localhost:3000

So far, we get:

  • Automatic transpilation and bundling (with webpack and babel)
  • Hot code reloading
  • Server rendering and indexing of ./pages

4. Add styled-components 



yarn add styled-components


Enter fullscreen mode Exit fullscreen mode

Let's now edit ./pages/index.js:



import styled from 'styled-components';

export default () => (
  <div>
    <Title>My First Next.js Page</Title>
  </div>
);

const Title = styled.h1`
  color: red;
`;


Enter fullscreen mode Exit fullscreen mode

If you reload the page, you will get an error, this is because we haven’t set up the correct configuration yet, not to worry, we are doing that next.

5. Add babel plugin and custom .bablerc file

First, lets install the styled components babel plugin as a dev dependency:



yarn add -D babel-plugin-styled-components


Enter fullscreen mode Exit fullscreen mode

Then create a .babelrc file in the root of the project.



touch .babelrc


Enter fullscreen mode Exit fullscreen mode
  • Add a babel/preset
  • Add a styled-components plugin, set the ssr flag to true, displayName to true and preprocess to false.

The final .babelrc file should look like this:



{
  "presets": [
    "next/babel"
  ],
  "plugins": [
    [
      "styled-components",
      {
        "ssr": true,
        "displayName": true,
        "preprocess": false
      }
    ]
  ]
}


Enter fullscreen mode Exit fullscreen mode

Note: displayName will generate class names that are easier to debug (will contain also the component name instead of just hashes); preprocess – experimental feature turned off explicitly.

6. Create the custom _document.js file

If you have used create-react-app before, you are used to knowing where your main document is, well, next.js does not expose this file, but you can override the default Document by adding a _document.js file in your pages folder.



touch pages/_document.js


Enter fullscreen mode Exit fullscreen mode

We will be extending the <Document /> and injecting the server side rendered styles into the <head>.

To override that default behavior, you must create a file at ./pages/_document.js, where you can extend the Document class.
https://github.com/zeit/next.js/#custom-document

This is how a custom _document.js would look like, if we just rendered the page and nothing else:



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

export default class MyDocument extends Document {
  static getInitialProps ({ renderPage }) {
    // Returns an object like: { html, head, errorHtml, chunks, styles }     
    return renderPage();
  }

  render () {    
    return (
      <html>
        <Head>
          <title>My page</title>
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </html>
    )
  }
}


Enter fullscreen mode Exit fullscreen mode

This is how it looks like once we add SSR styled components.



import Document, { Head, Main, NextScript } from 'next/document';
// Import styled components ServerStyleSheet
import { ServerStyleSheet } from 'styled-components';

export default class MyDocument extends Document {
  static getInitialProps({ renderPage }) {
    // Step 1: Create an instance of ServerStyleSheet
    const sheet = new ServerStyleSheet();

    // Step 2: Retrieve styles from components in the page
    const page = renderPage((App) => (props) =>
      sheet.collectStyles(<App {...props} />),
    );

    // Step 3: Extract the styles as <style> tags
    const styleTags = sheet.getStyleElement();

    // Step 4: Pass styleTags as a prop
    return { ...page, styleTags };
  }

  render() {
    return (
      <html>
        <Head>
          <title>My page</title>
          {/* Step 5: Output the styles in the head  */}
          {this.props.styleTags}
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </html>
    );
  }
}


Enter fullscreen mode Exit fullscreen mode

Once this is done, restart your server and the error should go away, your <h1> tag should be red, and SSR styled components should work.

Thats it, so to recapitulate:

  1. Create a project and install dependencies
  2. Add scripts
  3. Create a pages folder and a first page
  4. Add styled components
  5. Add a babel plugin and a custom .babelrc file
  6. Create a custom _document.js file

As you can see, if you already have an existing next.js project, you only need to implement steps 4 to 6.

There is also a way to use plain .css files with next.js, I will be writing a guide on how to set it up soon.

Resources

This post was originally posted on my website on 06/26/2018.

Top comments (38)

Collapse
 
blelem profile image
Berthier Lemieux

Thanks, this was really useful.

Let me add some SEO for people google for their Next + styled-component issues. Adding the babel configuration .babelrc in step 5 will fix the following errors/warnings:

Warning: Prop `className` did not match. Server: "sc-htpNat kRxhCE" Client: "sc-bdVaJa gCHvCg"
Enter fullscreen mode Exit fullscreen mode
It looks like you've wrapped styled() around your React component (Component), but the className prop is not being passed down to a child. No styles will be rendered unless className is composed within your React component.
Enter fullscreen mode Exit fullscreen mode
Collapse
 
mtrabelsi profile image
Marwen Trabelsi • Edited

not really working, here explanation:
it only works for the first load, if you hard reload + remove cach, then it crashes and keeps crashing

Collapse
 
noncototient profile image
Bo • Edited

Thanks mate, worked beautifully with the latest version of NextJS (v7)

Edit: actually, results in this annoying error:

Warning: Prop `className` did not match.
Enter fullscreen mode Exit fullscreen mode

Any ideas? GitHub issues and Google are not helping.

Collapse
 
cedricgourville profile image
Cédric Gourville • Edited

yes 2 years later

.babelrc


{
   "env": {
      "development": {
         "plugins": [
            [
               "styled-components",
               { "ssr": true, "displayName": true, "preprocess": false }
            ]
         ],
         "presets": ["next/babel"]
      },
      "production": {
         "plugins": [
            [
               "styled-components",
               { "ssr": true, "displayName": true, "preprocess": false }
            ]
         ],
         "presets": ["next/babel"]
      }
   },
   "plugins": [
      [
         "styled-components",
         { "ssr": true, "displayName": true, "preprocess": false }
      ]
   ]
}


Enter fullscreen mode Exit fullscreen mode
Collapse
 
nicklima profile image
Nick Lima

Thanks for the post. It helped me a lot in the past.

I'm here to thank you and to say for the future users looking for styled-components on Next.js that now with the new Next.js 12 version, you can handle the styled-components natively in next-config file:

// next.config.js

module.exports = {
  compiler: {
    // ssr and displayName are configured by default
    styledComponents: true,
  },
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
matheusdamiao profile image
Matheus Damião

Thank you! That was a huge saving!

Collapse
 
bbynog profile image
bbynog

thank you!

Collapse
 
quadsurf profile image
Chris Castro • Edited

chrome user agent stylesheet annoyingly has a default margin of 8px around my website... how/where do you add body css to override that globally? I tried using/importing { createGlobalStyle } from 'styled-components' but that didn't seem to work, probably because my implementation was all trial and error guessing

"""const GlobalStyle = createGlobalStyle
body {
margin: 0
}
"""
i then wrapped the above component around my app, and all it did was make the contents of my app disappear like whiteout :-(

also, github.com/vercel/next.js/tree/mas... offers no explanation on how/where to inject global css

Collapse
 
wynxnyw profile image
Kyle Winkler

Well one major issue is you shouldn't wrap your app with the GlobalStyle component.
This is a common mistake actually, but you'll notice that GlobalStyle isnt a react component and it does not take children as a prop so anything inside won't be rendered.

Instead you need to render it as a self closing tag <GlobalStyle />

Collapse
 
normancarcamo profile image
Norman Enmanuel

The solution to this problem is using the updated version of the pages/_document.js file.
github.com/vercel/next.js/blob/mas...

Collapse
 
susannelundblad profile image
Susanne Karin Lundblad

Hi! When adding <title> to the _document.js file I get an error

Warning: <title> should not be used in _document.js's <Head>. https://err.sh/next.js/no-document-title

Collapse
 
pjaws profile image
Paul Matthew Jaworski

That's absolutely right. You should put it in next/head instead.

Collapse
 
ahikmatf profile image
Asep Hikmat Fatahillah • Edited

I am using latest NextJS (8.0.3) and Styled-Components (4.1.3) and seems like it doesn't need to extend the <Document /> component anymore (step 6), because I don't see any error(?)

edit: Nope, my fault. Step 6 will make the page's style rendered before being served in the browser, so there will be no flash of unstyled content

Collapse
 
mustafaturk profile image
Mustafa Türk • Edited

Thanks!

Is there a specific reason for installing babel-plugin-styled-components as a dev dependency, since you are not using it anywhere? Or am I missing something here?

Collapse
 
sfkiwi profile image
Mike Sutherland

Thanks, really useful. Solved the issues I was running into

Collapse
 
janpauldahlke profile image
jan paul

sorry to comment so late on this man. dont know if you will even read this.

do you have some experiences how much your steps 1- 4 in

MyDocument extends Document

would impact speed, in terms of build time?

i mean is styled components still i thing in react nowadays? i moved to angular and am tinkering on next in spare time, but did not quiete like tailwinds bootstrappy approach

Collapse
 
jefdavis54 profile image
Jeff • Edited

This was a useful starting point, but I found that Next.js maintains an example that is more up to date. Check it out here...

Collapse
 
aprietof profile image
Adrian Prieto

Hey Jeff, you are probably right, Next.js has made so many great changes since I wrote this post, that I'm not surprised this is no longer up to date, I would definitely suggest to follow their documentation. Thanks for the heads up. By the way what are the main changes?