How to set up Styled Components in NextJS
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
You can skip this step if you already have a NextJS application up and running. Otherwise, let’s create a boilerplate NextJS application first.
npx create-next-app@latestYou can run the dev server by running the following command.
npm run devNow 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-componentsIf you are using yarn, it's highly recommended the following resolution to avoid breaking changes.
"resolutions": {
"styled-components": "^5"
}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>
);
}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;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>;
};Now, your components will generate the CSS class names automatically. You can read more about it in the 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>
);
}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-componentsThen, add the plugin to the .babelrc file:
{
"presets": ["next/babel"],
"plugins": [["styled-components", { "ssr": true, "displayName": true }]]
}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 namesAlternatively, 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;
},
});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>
);
}
}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
