Download this example using degit
npx degit https://github.com/ben-rogerson/twin.examples/vite-emotion-typescript folder-name
From within the new folder, run yarn
, then yarn start
to start the dev server.
Install Vite
yarn create vite my-vite-app --template react-ts
Install the dependencies
yarn add @emotion/react @emotion/styled
yarn add twin.macro @emotion/babel-plugin-jsx-pragmatic @babel/plugin-transform-react-jsx babel-plugin-macros tailwindcss --dev
Install with npm
Install Vite
npm create vite@latest my-vite-app -- --template react-ts
Install the dependencies
npm install @emotion/react @emotion/styled
npm install --save-dev twin.macro @emotion/babel-plugin-jsx-pragmatic @babel/plugin-transform-react-jsx babel-plugin-macros tailwindcss
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 import GlobalStyles
within a new file placed in src/styles/GlobalStyles.tsx
:
// src/styles/GlobalStyles.tsx
import React from 'react'
import { Global } from '@emotion/react'
import tw, { css, 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
Then import the GlobalStyles file in src/main.tsx
:
// src/main.tsx
import React from 'react'
import { createRoot } from 'react-dom/client'
import GlobalStyles from './styles/GlobalStyles'
import App from './App'
const container = document.getElementById('root')
const root = createRoot(container!)
root.render(
<React.StrictMode>
<GlobalStyles />
<App />
</React.StrictMode>,
)
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 the following to your vite config:
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
optimizeDeps: {
esbuildOptions: {
target: 'es2020',
},
},
esbuild: {
// https://github.com/vitejs/vite/issues/8644#issuecomment-1159308803
logOverride: { 'this-is-undefined-in-esm': 'silent' },
},
plugins: [
react({
babel: {
plugins: [
'babel-plugin-macros',
[
'@emotion/babel-plugin-jsx-pragmatic',
{
export: 'jsx',
import: '__cssprop',
module: '@emotion/react',
},
],
[
'@babel/plugin-transform-react-jsx',
{ pragma: '__cssprop' },
'twin.macro',
],
],
},
}),
],
})
To avoid red squiggly underlines, you’ll need to add the remaining types for your chosen css-in-js framework.
First up, you’ll need to install some types for React:
npm install -D @types/react
// or
yarn add @types/react -D
Then create a file in types/twin.d.ts
and add these declarations:
// types/twin.d.ts
import 'twin.macro'
import { css as cssImport } from '@emotion/react'
import styledImport from '@emotion/styled'
import { CSSInterpolation } from '@emotion/serialize'
declare module 'twin.macro' {
// The styled and css imports
const styled: typeof styledImport
const css: typeof cssImport
}
declare module 'react' {
// The tw and css prop
interface DOMAttributes<T> {
tw?: string
css?: CSSInterpolation
}
}
Then add the following to your tsconfig.json
:
{
"compilerOptions": {
"skipLibCheck": true,
"jsxImportSource": "@emotion/react"
},
"include": ["src", "types"]
}
Learn how to work with twin
- The prop styling guide - A must-read guide to level up on prop styling
- The styled component guide - A must-read guide on getting productive with styled-components
Learn more about emotion