Download this example using degit:
npx degit https://github.com/ben-rogerson/twin.examples/next-emotion folder-name
Or keep scrolling for installation instructions.
Install Next.js
npx create-next-app
Install the dependencies
npm install @emotion/react @emotion/styled @emotion/css @emotion/server
npm install -D twin.macro tailwindcss @emotion/babel-plugin babel-plugin-macros
Install with Yarn
yarn create next-app
Install the dependencies
yarn add @emotion/react @emotion/styled @emotion/css @emotion/server
yarn add -D twin.macro tailwindcss @emotion/babel-plugin babel-plugin-macros
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
If your notice your page flickering on first render, this might fix the problem.
Creating a _document.js
file like this will put critical styles in the head of the page.
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 page = await ctx.renderPage()
const styles = extractCritical(page.html)
return { ...initialProps, ...page, ...styles }
}
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>
)
}
}
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: 'emotion',
},
}
b) Or in package.json
:
// package.json
"babelMacros": {
"twin": {
"preset": "emotion"
}
},
Note: The preset gets set to 'emotion' by default, so adding the config is only useful if you want to adjust Twin’s other options.
Add this babel configuration in .babelrc.js
:
// .babelrc.js
module.exports = {
presets: [
[
'next/babel',
{
'preset-react': {
runtime: 'automatic',
importSource: '@emotion/react',
},
},
],
],
plugins: ['@emotion/babel-plugin', 'babel-plugin-macros'],
}
Add this next configuration in next.config.js
if you aren’t using Webpack 5 yet:
// next.config.js
module.exports = {
webpack: (config, { isServer }) => {
if (!isServer) {
// Unset client-side javascript that only works server-side
// https://github.com/vercel/next.js/issues/7755#issuecomment-508633125
config.node = { fs: 'empty', module: 'empty' }
}
return config
},
}
Webpack 5 config
The API changed slightly in Webpack 5, so use this config instead:
// next.config.js
module.exports = {
future: { webpack5: true }, // Use webpack 5
webpack: (config, { isServer }) => {
if (!isServer) {
// Unset client-side javascript that only works server-side
// https://github.com/vercel/next.js/issues/7755#issuecomment-508633125
config.resolve = {
fallback: { fs: 'empty', module: 'empty' },
}
}
return config
},
}
'fs' is a server-side dependency which we don’t want added client-side. Adding the code above will make sure we don’t experience errors.
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:
npm install -D @types/react
// or
yarn add @types/react -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 from '@emotion/styled'
import { css as cssImport } from '@emotion/react'
declare module 'twin.macro' {
// The styled and css imports
const styled: typeof styledImport
const css: typeof cssImport
}
Then add the following to your typescript config:
// tsconfig.json
{
"compilerOptions": {
"jsxImportSource": "@emotion/react" // for the css prop
},
"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'. |
hasSuggestions | boolean |
true |
Display suggestions when a class isn’t found |
dataTwProp | boolean /string |
true |
Add a prop to your elements in development so you can see the original tailwind classes, eg: <div data-tw="bg-black" /> , add all to keep the prop in production |
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 |
includeClassNames | boolean |
false |
Look in className props for tailwind classes to convert |
dataCsProp | boolean |
true |
Add a prop to your elements in development so you can see the original cs prop classes, eg: <div data-cs="maxWidth[1em]" /> |
disableCsProp | boolean |
false |
Disable twin from reading values specified in the cs prop. |
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: npx tailwindcss-cli@latest init --full
In the config, twin only reads the
darkMode
,theme
andplugins
entries, so feel free to remove 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 emotion
View more emotion examples
- React
- Preact
- Create React App
- Gatsby
- Next.js (current)
- Snowpack