Nextjs + typescript +styled-components
Tags: nextjs, beginners, typescript
Published: 2020-06-02 08:15:44

### Summary

1 - [Add Typescript](#typescript)
2 - [Install styled-components](#styled-components)
3 - [Apply globalStyle](#globalstyle)
4 - [Bonus 1 - Absolute imports](#bonus1)
5 - [Bonus 2 - SSR with stylesheet rehydration](#bonus2)

In case you get lost, all the code is available on https://github.com/rffaguiar/setup-nextjs-typescript-styled-components

You can also reach me on twitter [@rffaguiar](https://twitter.com/rffaguiar).

Let's move on!

You have just started to learn Next.js on https://nextjs.org/learn/basics/create-nextjs-app. Now you want to start building your amazing apps on your own. The small tutorial didn't teach how to add styled-components, typescript, or global style. Don't worry, let me baby step on these.

**Attention**: the following package versions were used on this setup:

```typescript
"dependencies": {
    "next": "9.4.4",
    "react": "16.13.1",
    "react-dom": "16.13.1",
    "styled-components": "^5.1.1"
  },
  "devDependencies": {
    "@types/node": "^14.0.9",
    "@types/react": "^16.9.35",
    "babel-plugin-styled-components": "^1.10.7",
    "typescript": "^3.9.3"
  }
```

#### <a name="typescript"></a> Add Typescript

Rename any of your .js to .tsx . Go ahead and rename your index.js to index.tsx and try to start your server. You will receive an error on CLI that you're trying to use Typescript but you don't have the necessary packages. Run:

```shell
npm install --save-dev typescript @types/react @types/node
```

When you start the server after the ts packages, 2 files are created for you: `next-env.d.ts` and `tsconfig.json`.

- `next-env.d.ts`: Nextjs type declaration file referencing its types inside of your **node_modules/next/types/global**
- `tsconfig.json`: contains the compiler options required to compile the project and specifies the root.

Your typescript is ready.

#### <a name="styled-components"></a> Install styled-components

```shell
npm install --save styled-components
```

For testing purposes, make your `index.tsx` like this:
```typescript
import styled from "styled-components";

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

const Home = () => {
  return (
    <div>
      <p>hello</p>
      <Title>Title</Title>
    </div>
  );
};

export default Home;
```

Go to the browser and inspect the Title (h1) element.

![Title element with weird className](https://dev-to-uploads.s3.amazonaws.com/i/a5mrc9kpw8e04vwfpdjx.png)

See how terrible that className is? `.sc-AxjAm.gxygnu` certainly isn't descriptive!

That's why it's recommended to install the babel plugin together.

```shell
npm install --save-dev babel-plugin-styled-components
```

Create a file `.babelrc` at the root of your project with the following:

```javascript
{
  "presets": ["next/babel"],
  "plugins": [["styled-components", { "ssr": true }]]
}
```

Restart the server and inspect the element again.

![Title element with a better className](https://dev-to-uploads.s3.amazonaws.com/i/kxsb8m87np2h1ixsz3ao.png)

Pretty cool, right? Now the className is a lot more descriptive.
The babel plugin gives more power-ups to styled-components + Nextjs:
- Smaller bundles
- Server-side rendering compatibility
- Better debugging
- Minification
- Dead code elimination

#### <a name="globalstyle"></a> Apply globalStyle

Cool! Now you have an incredible JS framework with a powerful style system. But...wait, what if you wanted to reset and/or share styles across all of your pages? Here we go with styled-components' `globalStyle`.

First, let's start with a `Layout` component. This is going to wrap every page and has all the shared styles.

Outside the `/pages` directory, create another folder `/layout` with `Basic.tsx` inside:

```Mardown
# /layout
# --/Basic.tsx
# /pages
# --/index.tsx
```

Inside of `Basic.tsx` you include and return your shared styles. The trick here is to include the `createGlobalStyle` and return it on `Basic.tsx` render.

```typescript
import { createGlobalStyle } from "styled-components";

export const GlobalStyle = createGlobalStyle`
	// this is the shared style
  html {
    box-sizing: border-box;
  }

  *,
  *::before,
  *::after {
    box-sizing: inherit;
  }
	
h1 {
	color: yellow !important; // the important is just to show that the style works!
}

  // anything else you would like to include
`;

const BasicLayout = ({ children }: { children: any }) => {
  return (
    <>
      <GlobalStyle />
      {children}
    </>
  );
};

export default BasicLayout;
```

Returning to `pages/index.tsx`. Import the newly created BasicLayout component and wrap the `Home` returned React element with BasicLayout.

```typescript
import styled from "styled-components";
import BasicLayout from "../layout/Basic";

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

const Home = () => {
  return (
    <BasicLayout>
      <p>hello</p>
      <Title>Title</Title>
    </BasicLayout>
  );
};

export default Home;
```

From now on, all the pages that include `BasicLayout` components are going to inherit the styles.

Congratulations!!! Now you have a proper Nextjs + Typescript + styled-component with global styles working!

#### <a name="bonus1"></a> Bonus 1 - Absolute imports

By default Nextjs allows you to use relative imports. You know, those never-ending imports `../../../../finally.tsx`. If you want to use an absolute import, you have to change just one line on `tsconfig.json`: the `baseUrl`.

```typescript
"compilerOptions": {
    // other options
    "baseUrl": "."
  },
```

Now, all absolute imports start at the same level as the `tsconfig.json` file. Using the previous `pages/index.tsx` import as an example, you can change A to B.

```typescript
// A
import BasicLayout from "../layout/Basic";

// B
import BasicLayout from "layout/Basic";

```

#### <a name="bonus2"></a> Bonus 2 - SSR with stylesheet rehydration

The fancy term which means: serve the required styles for the first render within the HTML and then load the rest in the client.

You need to create a custom `/pages/_document.tsx` and copy the following logic into it. That's it.

```typescript
import Document 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()
    }
  }
}
```

The code above was taken directly from the `styled-components` example on [nextjs github repo](https://github.com/vercel/next.js/blob/master/examples/with-styled-components/pages/_document.js).