DEV Community

Mohammad Faisal
Mohammad Faisal

Posted on • Updated on • Originally published at mohammadfaisal.dev

How to set up Styled Components in NextJS

To read more articles like this, visit my blog

Introduction

Styled Components is a library that allows you to write CSS in your JavaScript/TypeScript code with many features like theming, nesting, and media queries. It's a great way to write CSS in React.

There are other options as well, but this is one of the best options out there because it's easy to use and has many features. It's also trendy because it's closely related to raw CSS and easy to understand.

If you are only interested in the project, you can find it on Github

Create a boilerplate project

If you already have a NextJS application up and running, you can skip this step. Otherwise, let’s create a boilerplate NextJS application first

npx create-next-app@latest
Enter fullscreen mode Exit fullscreen mode

You can run the dev server by running the following command

npm run dev
Enter fullscreen mode Exit fullscreen mode

Now Visit http://localhost:3000 to see your application up and running.

Installation

First, install the dependencies:

npm install --save styled-components
npm install --save-dev @types/styled-components
Enter fullscreen mode Exit fullscreen mode

If you are using yarn, it's highly recommended the following resolution to avoid breaking changes.

"resolutions": {
  "styled-components": "^5"
}
Enter fullscreen mode Exit fullscreen mode

It will save you from a lot of headaches.

Setup

Next, create a _app.tsx file in the pages directory. This special file in NextJS allows you to customize the app. You can read more about it in the documentation.

import { AppProps } from "next/app";
import { ThemeProvider } from "styled-components";
import React, { useState, useEffect } from "react";

const theme = {
  colors: {
    primary: "#0070f3",
  },
};

export default function MyApp({ Component, pageProps }: AppProps) {
  const [showChild, setShowChild] = useState(false);
  useEffect(() => {
    setShowChild(true);
  }, []);

  if (!showChild) {
    return null;
  }

  return (
    <ThemeProvider theme={theme}>
      <Component {...pageProps} />
    </ThemeProvider>
  );
}
Enter fullscreen mode Exit fullscreen mode

Notice that we are using the ThemeProvider component from styled-components, and we are passing the theme object as a prop. This is how we can use the theme in our components.

You will also notice that we use the useState and useEffect hooks. We want to show the child component only after the theme is loaded.

Doing this is not mandatory, but it's good practice.

Usage

Now, you can use the theme in your components. For example, you can create a Button component:

import styled from "styled-components";

const StyledButton = styled.button`
  color: ${(props) => props.theme.colors.primary};
`;

export default StyledButton;
Enter fullscreen mode Exit fullscreen mode

Using the theme prop, we can access the theme object and use its colors.

But what if we want to use the theme in a non-styled component? For example, we want to use the theme in a div element. We can use the useTheme hook:

import { useTheme } from "styled-components";

const MyComponent = () => {
  const theme = useTheme();
  return <div style={{ color: theme.colors.primary }}>Hello World</div>;
};
Enter fullscreen mode Exit fullscreen mode

Now, your components will generate the CSS class names automatically. You can read more about it in documentation

and render the button on the root page.

import StyledButton from "../components/button";
import styles from "../styles/Home.module.css";

export default function Home() {
  return (
    <div className={styles.container}>
      <StyledButton> This is a button</StyledButton>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

The problem

You will notice that the tag names are not being generated. For example, if you inspect the element, you will see that the tag name is div instead of sc-div.

This is because NextJS uses server-side rendering, and the styles are generated on the server. This is not a problem in development, but it's a problem in production.

Also, the class name is not meaningful. Let’s fix these two problems.

Solution

We need to use the styled-components babel plugin to fix this. First, install the plugin:

npm install --save-dev babel-plugin-styled-components
Enter fullscreen mode Exit fullscreen mode

Then, add the plugin to the .babelrc file:

{
  "presets": ["next/babel"],
  "plugins": [["styled-components", { "ssr": true, "displayName": true }]]
}
Enter fullscreen mode Exit fullscreen mode

Notice that we have added the following configurations

ssr: true -> This means we are enabling the server-side rendering

displayName: true -> It will set some meaningful class names
Enter fullscreen mode Exit fullscreen mode

Alternatively, you can add the following to your next.config.js file:

const withPlugins = require("next-compose-plugins");
const withTM = require("next-transpile-modules")(["styled-components"]);

module.exports = withPlugins([withTM], {
  webpack(config, options) {
    return config;
  },
});
Enter fullscreen mode Exit fullscreen mode

Now restart the server, and you will see that the button className will be button__StyledButton-sc-egbi27-0 exllgz

This makes it easier to understand.

Handle server-side styles

The next step is to handle the server-side styles. We need to add the ServerStyleSheet from styled-components to the _document.tsx file:

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

export default class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const sheet = new ServerStyleSheet();
    const originalRenderPage = ctx.renderPage;

    try {
      ctx.renderPage = () =>
        originalRenderPage({
          enhanceApp: (App) => (props) =>
            sheet.collectStyles(<App {...props} />),
        });

      const initialProps = await Document.getInitialProps(ctx);
      return {
        ...initialProps,
        styles: (
          <>
            {initialProps.styles}
            {sheet.getStyleElement()}
          </>
        ),
      };
    } finally {
      sheet.seal();
    }
  }

  render() {
    return (
      <Html>
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Here we are using the ServerStyleSheet to collect the styles from the server, and then we are adding them to the styles prop.

Conclusion

This was a straightforward example, but you can use the styled-components library to create complex components with many features.

To learn more about the library, you can read the documentation

GitHub Repo:

https://github.com/Mohammad-Faisal/styled-components-nextjs-integration-demo

Enter fullscreen mode Exit fullscreen mode

Top comments (0)