Twin 101: Use Tailwind CSS + CSS-in-JS in a Next.js App

how-toreactnext.jstwintailwindemotion
Elvis Duru

Elvis Duru / March 5, 2022 (Updated on July 2, 2022)

4 min read --- views

Introduction

Twin is a library that let’s you enjoy the best of Tailwind CSS together with CSS-in-JS libraries like emotion or styled-components. In this article I’ll walk you through how to setup Tailwind CSS and CSS-in-JS (emotion) in your Next.js app with twin.macro.

Installation

For this example we’ll be using Emotion as our CSS-in-JS library. There are two ways to get started with Twin. If you’re starting a new Next.js project you can simply download the next-emotion starter using the following command - remember to replace folder-name with the name of your project:

npx degit https://github.com/ben-rogerson/twin.examples/next-emotion folder-name

This will clone the starter which has everything already setup and configured for you. Once that is done, you should navigate into the folder and run npm install to install dependencies, then npm run dev to start the dev server.

Existing Projects

For existing projects, we’ll have to install the dependencies and configure the project.

Step 1 - Install Dependencies

Run the following command to install the required dependencies:

npm install @emotion/react @emotion/styled @emotion/css @emotion/server
npm install -D twin.macro tailwindcss @emotion/babel-plugin babel-plugin-macros

Step 2 - Add the Global Styles

Twin comes with a GlobalStyles import that adds some the base styles from tailwind together with some @keyframes for animations, and some global css variables to alleviate cross-browser inconsistencies.

Create a new file in the path styles/GlobalStyles.js and add the following:

styles/GlobalStyles.js
import { Global, css } from '@emotion/react'
import tw, { theme, GlobalStyles as BaseStyles } from 'twin.macro'

const customStyles = css({
  body: {
    WebkitTapHighlightColor: theme`colors.purple.500`,
    ...tw`antialiased`,
  },
})

const GlobalStyles = () => (
  <>
    <BaseStyles />
    <Global styles={customStyles} />
  </>
)

export default GlobalStyles

Next, import the GlobalStyles in pages/_app.js:

pages/_app.js
import GlobalStyles from './../styles/GlobalStyles'

const App = ({ Component, pageProps }) => (
  <>
    <GlobalStyles />
    <Component {...pageProps} />
  </>
)

export default App

Step 3 - SSR Styles

To use emotion’s SSR with Next.js, we need to add a custom Document component in pages/_document.js that renders critical styles and inserts them into the <head>.

pages/_document.js
import React from 'react'
import Document, { Html, Head, Main, NextScript } from 'next/document'
import { extractCritical } from '@emotion/server'

export default class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const initialProps = await Document.getInitialProps(ctx)
    const critical = extractCritical(initialProps.html)
    initialProps.html = critical.html
    initialProps.styles = (
      <React.Fragment>
        {initialProps.styles}
        <style
          data-emotion-css={critical.ids.join(' ')}
          dangerouslySetInnerHTML={{ __html: critical.css }}
        />
      </React.Fragment>
    )

    return initialProps
  }

  render() {
    return (
      <Html lang="en">
        <Head>
          <style
            data-emotion-css={this.props.ids.join(' ')}
            dangerouslySetInnerHTML={{ __html: this.props.css }}
          />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    )
  }
}

Step 4 (Optional) - Adjusting Twin’s Config Options

The default preset is set to ‘emotion’, but if you want to adjust Twin’s other options, you can do so in any of the files below:

  1. Either in babel-plugin-macros.config.js
babel-plugin-macros.config.js
module.exports = {
  twin: {
    preset: 'emotion',
  },
}
  1. Or in package.json
package.json
"babelMacros": {
  "twin": {
    "preset": "emotion"
  }
},

Add The Babel Config

Create a new file .babelrc.js in your project’s root directory and add the following lines:

.babelrc.js
module.exports = {
  presets: [
    [
      'next/babel',
      {
        'preset-react': {
          runtime: 'automatic',
          importSource: '@emotion/react',
        },
      },
    ],
  ],
  plugins: ['@emotion/babel-plugin', 'babel-plugin-macros'],
}

Customizing Twin

To be able to customize twin, you need to complete Step 4 above. You can find all the configuration options for twin.macro here.

Customizing Tailwind CSS

While it’s not compulsory, you might want to change the default style configuration that tailwind provides to suit your theme. To customize tailwind’s styles for your project, you’ll need to add a tailwind.config.js file in your project’s root directory.

You can start with the empty config below:

tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: {},
    },
  },
  plugins: [],
}

Or, you can start with the full config, and tweak to your satisfaction. From your root folder run the following to get the full config:

npx tailwindcss-cli@latest init --full

Plugins

Twin supports many tailwind plugins, like @tailwindcss/typography and @tailwindcss/forms, but plugins that use the addVariant function are not compatible.

Here’s the list of supported plugins

Other tools

VSCode plugins and extensions

VSCode Snippets

Next Steps

Resources

Get the latest articles, in your inbox

Every couple of weeks, I share the best content from my blog. It's short, sweet, and practical. I'd love you to join. You may opt out at any time.