Skip to content

stevenocchipinti/taskematic

Repository files navigation

twin, next, styled-components

Download this example using degit:

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

Or keep scrolling for installation instructions.

Table of contents

Getting started

Installation

Install Next.js

npx create-next-app

Install the dependencies

npm install twin.macro tailwindcss styled-components
Install with Yarn
yarn create next-app

Install the dependencies

yarn add twin.macro tailwindcss styled-components

Add the global styles

Twin uses the same preflight base styles as Tailwind to smooth over cross-browser inconsistencies.

The GlobalStyles import adds these base styles along with some @keyframes for the animation classes and some global css that makes the ring classes and box-shadows work.

You can add Twin’s GlobalStyles import in pages/_app.js:

// page/_app.js
import { GlobalStyles } from 'twin.macro'

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

export default App

Add the twin config

Twin’s config can be added in a couple of different files.

a) Either in babel-plugin-macros.config.js:

// babel-plugin-macros.config.js
module.exports = {
  twin: {
    preset: 'styled-components',
  },
}

b) Or in package.json:

// package.json
"babelMacros": {
  "twin": {
    "preset": "styled-components"
  }
},

Add the babel config

Add this babel configuration in .babelrc.js:

// In .babelrc.js
module.exports = {
  presets: [['next/babel', { 'preset-react': { runtime: 'automatic' } }]],
  plugins: ['babel-plugin-macros', ['styled-components', { ssr: true }]],
}

Add the next config

Add this next configuration in next.config.js:

// next.config.js
module.exports = {
  webpack: (config, { isServer }) => {
    // Fixes npm packages that depend on `fs` module
    if (!isServer) {
      config.node = { fs: 'empty' }
    }

    return config
  },
}

'fs' is a server-side dependency which we don’t want added client-side. Adding the code above will make sure you don’t experience errors.

Add the server stylesheet

To avoid the ugly Flash Of Unstyled Content (FOUC), add a server stylesheet in pages/_document.js that gets read by Next.js:

// pages/_document.js
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()
    }
  }
}

Complete the TypeScript setup

If you’re using TypeScript, you’ll need to add the remaining types for your chosen css-in-js framework.

Setup instructions

First up, you’ll need to install some types for react and styled-components:

npm install -D @types/react @types/styled-components
// or
yarn add @types/react @types/styled-components -D

Then twin needs some type declarations added for your chosen css-in-js library, otherwise you’ll see errors like this:

Module '"../node_modules/twin.macro/types"' has no exported member 'styled'.
// or
Module '"../node_modules/twin.macro/types"' has no exported member 'css'.
// or
Property 'css' does not exist on type 'DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>'.

To fix this, create a twin.d.ts file in your project root (src/twin.d.ts with create-react-app) and add these declarations:

// twin.d.ts
import 'twin.macro'
import styledImport, { CSSProp, css as cssImport } from 'styled-components'

declare module 'twin.macro' {
  // The styled and css imports
  const styled: typeof styledImport
  const css: typeof cssImport
}

declare module 'react' {
  // The css prop
  interface HTMLAttributes<T> extends DOMAttributes<T> {
    css?: CSSProp
  }
  // The inline svg css prop
  interface SVGProps<T> extends SVGProps<SVGSVGElement> {
    css?: CSSProp
  }
}

// The 'as' prop on styled components
declare global {
  namespace JSX {
    interface IntrinsicAttributes<T> extends DOMAttributes<T> {
      as?: string
    }
  }
}

Then add the following in tsconfig.json:

// tsconfig.json
{
  "files": ["twin.d.ts"],
  // or
  // "include": ["twin.d.ts"],
}

Now that you’ve added the definitions, you can use these imports:

import tw, { css, styled, theme } from 'twin.macro'

And these props:

<div tw="">
<div css={}>

Customization

Twin options

Name Type Default Description
config string "tailwind.config.js" The path to your Tailwind config
preset string "emotion" The css-in-js library behind the scenes - also supports 'styled-components' and 'goober'
autoCssProp boolean true This code automates the import of 'styled-components/macro' so you can use their css prop
hasSuggestions boolean true Display class suggestions when a class isn't found
dataTwProp boolean true Add a prop to your elements in development so you can see the original tailwind classes, eg: <div data-tw="bg-black" />
debugPlugins boolean false Display generated class information in your terminal from your plugins
debug boolean false Display information in your terminal about the Tailwind class conversions
disableColorVariables boolean false Disable css variables in colors (not gradients) to help support IE11/react native

Tailwind config

For style customizations, add a tailwind.config.js in your project root.

It’s important to know that you don’t need a tailwind.config.js to use Twin. You already have access to every class with every variant. Unlike Tailwind, twin.macro only generates styles for the classes so you don’t need to use PurgeCSS.

Choose from one of the following configs:

  • a) Start with an empty config:

    // tailwind.config.js
    module.exports = {
      theme: {
        extend: {
          colors: {},
        },
      },
      plugins: [],
    }
  • b) Start with a full config:

    # cd into your project folder then:
    curl https://raw.githubusercontent.com/tailwindcss/tailwindcss/master/stubs/defaultConfig.stub.js > tailwind.config.js

    In the config, twin only reads from the theme: {} and the plugins: [] entries, so strip out the rest.

Plugins

External

You can use many Tailwind plugins with twin, like tailwindcss-typography and @tailwindcss/forms but there’s no compatibility with plugins that use the addVariant functions.

See list of supported plugins →

Custom classes

You can add your own custom css within a plugin. Here’s an example of a custom class that adds breakpoint based paddings from theme values:

// tailwind.config.js
module.exports = {
  // ...
  plugins: [paddings],
}

function paddings({ addComponents, theme }) {
  addComponents({
    '.my-padding': {
      '@screen md': {
        'padding-left': theme`padding.3`,
        'padding-right': theme`padding.3`,
      },
      '@screen lg': {
        'padding-left': theme`padding.6`,
        'padding-right': theme`padding.6`,
      },
    },
  })
}

Usage

Twin has a couple of different styling techniques to choose from.

Styled props

Use Twin’s tw prop when you have no conditional styles:

import 'twin.macro'

const Input = () => <input tw="border hover:border-black" />

Nest Twin’s tw import within a css prop to add conditional styles:

import tw from 'twin.macro'

const stylesInput = ({ hasHover }) => [
  tw`border`, // Add base styles first
  hasHover && tw`hover:border-black`, // Then conditional styles
]

const Input = props => <input css={stylesInput(props)} />

Your can add both tw and css props on the same element:

import tw from 'twin.macro'

const Input = ({ hasHover }) => (
  <input tw="border" css={[hasHover && tw`hover:border-black`]} />
)

Or mix sass and tw styles with the css import:

import tw, { css } from 'twin.macro'

const hoverStyles = css`
  &:hover {
    ${tw`text-black`}
  }
`

const stylesInput = ({ hasHover }) => [
    tw`border` // Add base styles first,
    hasHover && hoverStyles // Then conditional styles
]

const Input = props => <input css={stylesInput(props)} />

Tip: Prefer booleans over ternaries to reduce your line length and improve scannability.

Styled components

You can also use the tw import to create and style new components:

import tw from 'twin.macro'

const Input = tw.input`border hover:border-black`

And clone and style existing components:

const PurpleInput = tw(Input)`border-purple-500`

Then switch to the styled import to add conditional styling:

import tw, { styled, css } from 'twin.macro'

const stylesWidth = css`border: 1px solid hotpink`,

const Input = styled.input(({ hasHover }) => [
    tw`border rounded`, // Add base styles first
    hasHover && tw`hover:border-black`, // Then conditional styles
    !hasHover && stylesWidth // Then any css/sass in variables
])

const Component = () => <Input hasHover />

Next steps

Learn more about styled-components

View more styled-components examples

About

NextJS + TailwindCSS + TwinMacro + Beautiful-DnD

Resources

Stars

Watchers

Forks