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.
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
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
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 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 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.
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()
}
}
}
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={}>
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 |
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 theplugins: []
entries, so strip out the rest.
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 →
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`,
},
},
})
}
Twin has a couple of different styling techniques to choose from.
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.
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 />
Learn more about styled-components
View more styled-components examples
- React
- Preact
- Create React App
- Gatsby
- Next.js (current)
- Snowpack