twin.macro
๐ฆนโโ๏ธ Twin blends the magic of Tailwind with the flexibility of css-in-js (emotion, styled-components, solid-styled-components, stitches and goober) at build time.
Top Related Projects
A utility-first CSS framework for rapid UI development.
Visual primitives for the component age. Use the best bits of ES6 and CSS to style your apps without stress ๐
๐ฉโ๐ค CSS-in-JS library designed for high performance style composition
Documentation about css-modules
[Not Actively Maintained] CSS-in-JS with near-zero runtime, SSR, multi-variant support, and a best-in-class developer experience.
Zero-runtime Stylesheets-in-TypeScript
Quick Overview
Twin.macro is a CSS-in-JS library that combines the power of Tailwind CSS with the flexibility of CSS-in-JS. It allows developers to use Tailwind's utility classes within styled-components or Emotion, providing a seamless integration of utility-first CSS with component-based styling.
Pros
- Combines the benefits of Tailwind CSS and CSS-in-JS
- Offers better performance compared to traditional Tailwind CSS usage
- Provides type safety and autocompletion when used with TypeScript
- Allows for easy customization and extension of Tailwind's utility classes
Cons
- Requires additional setup and configuration compared to vanilla Tailwind CSS
- May have a steeper learning curve for developers unfamiliar with CSS-in-JS
- Can increase bundle size if not properly optimized
- Limited community support compared to more established CSS solutions
Code Examples
- Basic usage with styled-components:
import tw, { styled } from 'twin.macro'
const Button = styled.button`
${tw`bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded`}
`
- Conditional styling:
import tw from 'twin.macro'
const Button = tw.button`
bg-blue-500 text-white font-bold py-2 px-4 rounded
${props => props.primary && tw`bg-red-500 hover:bg-red-700`}
`
- Combining with custom CSS:
import tw, { styled } from 'twin.macro'
const Card = styled.div`
${tw`bg-white rounded-lg shadow-md p-6`}
max-width: 300px;
transition: transform 0.2s ease-in-out;
&:hover {
transform: scale(1.05);
}
`
Getting Started
- Install the necessary dependencies:
npm install twin.macro @emotion/react @emotion/styled
- Add the Twin babel macro to your babel config:
{
"plugins": ["babel-plugin-macros"]
}
- Create a twin configuration file (twin.config.js):
module.exports = {
theme: {
extend: {
colors: {
electric: '#db00ff',
ribbon: '#0047ff',
},
},
},
plugins: [],
}
- Start using Twin in your components:
import tw from 'twin.macro'
const Button = tw.button`bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded`
Competitor Comparisons
A utility-first CSS framework for rapid UI development.
Pros of Tailwind CSS
- Larger community and ecosystem, with more resources and third-party tools
- Easier to get started with, as it doesn't require additional setup or configuration
- Better performance for larger projects due to its purging capabilities
Cons of Tailwind CSS
- Can lead to verbose HTML with many utility classes
- Less flexible for creating custom designs or complex components
- Requires additional tooling for optimal file size in production
Code Comparison
Tailwind CSS:
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Button
</button>
Twin.macro:
import tw from 'twin.macro'
const Button = tw.button`
bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded
`
Twin.macro allows for a more component-based approach, integrating Tailwind's utility classes directly into styled components. This can lead to cleaner JSX and better separation of concerns. However, Tailwind CSS's approach is more straightforward and doesn't require additional libraries or setup, making it easier for beginners to adopt.
Visual primitives for the component age. Use the best bits of ES6 and CSS to style your apps without stress ๐
Pros of styled-components
- More intuitive syntax for developers familiar with CSS
- Better support for dynamic styling based on props
- Wider adoption and larger community
Cons of styled-components
- Larger bundle size due to runtime CSS generation
- Steeper learning curve for developers new to CSS-in-JS
- Potential performance issues with complex styling logic
Code Comparison
styled-components:
const Button = styled.button`
background-color: ${props => props.primary ? 'blue' : 'white'};
color: ${props => props.primary ? 'white' : 'blue'};
padding: 10px 20px;
border: 2px solid blue;
`;
twin.macro:
import tw from 'twin.macro'
const Button = tw.button`
bg-blue-500 text-white
px-4 py-2
border-2 border-blue-500
`
Twin.macro leverages Tailwind CSS classes, resulting in a more concise and predictable styling approach. It also offers better performance due to its compile-time CSS generation. However, styled-components provides more flexibility for complex, dynamic styling scenarios and has a larger ecosystem of tools and resources.
Both libraries have their strengths, and the choice between them often depends on project requirements, team preferences, and performance considerations.
๐ฉโ๐ค CSS-in-JS library designed for high performance style composition
Pros of Emotion
- More established and widely adopted in the React community
- Offers a broader set of features for CSS-in-JS, including server-side rendering support
- Provides a more flexible API for creating and composing styles
Cons of Emotion
- Steeper learning curve for developers new to CSS-in-JS
- Requires more setup and configuration compared to Twin.macro
- Can lead to larger bundle sizes due to its comprehensive feature set
Code Comparison
Emotion:
import { css } from '@emotion/react'
const style = css`
background-color: hotpink;
&:hover {
color: ${props => props.color};
}
`
Twin.macro:
import tw from 'twin.macro'
const style = tw`
bg-pink-500
hover:text-[color:var(--color)]
`
Summary
Emotion is a more comprehensive CSS-in-JS solution with a wider range of features and flexibility. It's well-suited for complex projects that require advanced styling capabilities. Twin.macro, on the other hand, focuses on integrating Tailwind CSS with CSS-in-JS, offering a simpler approach for developers familiar with utility-first CSS. The choice between the two depends on project requirements, team expertise, and preferred styling methodology.
Documentation about css-modules
Pros of css-modules
- Simpler learning curve, as it builds on existing CSS knowledge
- Better compatibility with existing CSS tooling and workflows
- Easier to integrate with legacy projects or codebases
Cons of css-modules
- Less flexible for dynamic styling compared to twin.macro
- Requires additional build configuration and tooling setup
- Limited ability to share styles across components without duplication
Code Comparison
css-modules:
.button {
background-color: blue;
color: white;
padding: 10px 20px;
}
import styles from './Button.module.css';
const Button = () => <button className={styles.button}>Click me</button>;
twin.macro:
import tw from 'twin.macro';
const Button = () => (
<button tw="bg-blue-500 text-white px-5 py-2">Click me</button>
);
css-modules offers a more traditional approach to styling, maintaining separation between CSS and JavaScript. It's easier to adopt for teams familiar with CSS but requires additional setup.
twin.macro provides a more integrated, inline styling approach using Tailwind CSS utility classes. It offers greater flexibility for dynamic styling and reduces context switching between files, but has a steeper learning curve for those unfamiliar with Tailwind CSS.
[Not Actively Maintained] CSS-in-JS with near-zero runtime, SSR, multi-variant support, and a best-in-class developer experience.
Pros of Stitches
- Near-zero runtime, with CSS generated at build time
- Built-in support for server-side rendering (SSR)
- Offers a more traditional CSS-in-JS API, which may be familiar to developers coming from other styling libraries
Cons of Stitches
- Less flexible than Twin.macro for integrating with existing CSS frameworks
- Requires learning a new API and syntax for styling components
- May have a steeper learning curve for developers used to traditional CSS or utility-first approaches
Code Comparison
Twin.macro:
import tw from 'twin.macro'
const Button = tw.button`
bg-blue-500
hover:bg-blue-700
text-white
font-bold
py-2
px-4
rounded
`
Stitches:
import { styled } from '@stitches/react'
const Button = styled('button', {
backgroundColor: '$blue500',
color: 'white',
fontWeight: 'bold',
padding: '0.5rem 1rem',
borderRadius: '0.25rem',
'&:hover': {
backgroundColor: '$blue700',
},
})
Both Twin.macro and Stitches offer powerful styling solutions for React applications, but they differ in their approach and syntax. Twin.macro leverages Tailwind CSS utility classes, providing a familiar experience for Tailwind users, while Stitches offers a more traditional CSS-in-JS API with additional features like SSR support and near-zero runtime overhead.
Zero-runtime Stylesheets-in-TypeScript
Pros of vanilla-extract
- Type-safe CSS-in-JS with zero runtime overhead
- Supports static extraction of styles for improved performance
- Seamless integration with existing CSS ecosystems and tooling
Cons of vanilla-extract
- Steeper learning curve for developers familiar with traditional CSS
- Limited real-time style updates compared to runtime CSS-in-JS solutions
- Requires additional build step for style extraction
Code Comparison
vanilla-extract:
import { style } from '@vanilla-extract/css';
export const button = style({
backgroundColor: 'blue',
color: 'white',
padding: '10px 20px',
});
twin.macro:
import tw from 'twin.macro';
const Button = tw.button`
bg-blue-500
text-white
py-2
px-4
`;
Twin.macro offers a more concise syntax using Tailwind-like utility classes, while vanilla-extract provides a more traditional CSS-in-JS approach with type safety. Twin.macro allows for easier adoption of utility-first CSS, whereas vanilla-extract focuses on zero-runtime CSS generation and type safety. Both solutions aim to improve the developer experience and performance of styling in modern web applications, but with different approaches and trade-offs.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
The magic of Tailwind with the flexibility of css-in-js.
Style jsx elements using Tailwind classes:
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 Input = ({ hasHover }) => (
<input css={[tw`border`, hasHover && tw`hover:border-black`]} />
)
Or mix sass styles with the css import:
import tw, { css } from 'twin.macro'
const hoverStyles = css`
&:hover {
border-color: black;
${tw`text-black`}
}
`
const Input = ({ hasHover }) => (
<input css={[tw`border`, hasHover && hoverStyles]} />
)
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`
Switch to the styled import to add conditional styling:
import tw, { styled } from 'twin.macro'
const StyledInput = styled.input(({ hasBorder }) => [
`color: black;`,
hasBorder && tw`border-purple-500`,
])
const Input = () => <StyledInput hasBorder />
Or use backticks to mix with sass styles:
import tw, { styled } from 'twin.macro'
const StyledInput = styled.input`
color: black;
${({ hasBorder }) => hasBorder && tw`border-purple-500`}
`
const Input = () => <StyledInput hasBorder />
How it works
When babel runs over your javascript or typescript files at compile time, twin grabs your classes and converts them into css objects. These css objects are then passed into your chosen css-in-js library without the need for an extra client-side bundle:
import tw from 'twin.macro'
tw`text-sm md:text-lg`
// รขยย รขยย รขยย รขยย รขยย รขยย
{
fontSize: '0.875rem',
'@media (min-width: 768px)': {
fontSize: '1.125rem',
},
}
Features
รฐยยย Simple imports - Twin collapses imports from common styling libraries into a single import:
- import styled from '@emotion/styled'
- import css from '@emotion/react'
+ import { styled, css } from 'twin.macro'
รฐยยยน Adds no size to your build - Twin converts the classes youรขยยve used into css objects using Babel and then compiles away, leaving no runtime code
รฐยยยฑ Apply variants to multiple classes at once with variant groups
import 'twin.macro'
const interactionStyles = () => (
<div tw="hover:(text-black underline) focus:(text-blue-500 underline)" />
)
const mediaStyles = () => <div tw="sm:(w-4 mt-3) lg:(w-8 mt-6)" />
const pseudoElementStyles = () => <div tw="before:(block w-10 h-10 bg-black)" />
const stackedVariants = () => <div tw="sm:hover:(bg-black text-white)" />
const groupsInGroups = () => <div tw="sm:(bg-black hover:(bg-white w-10))" />
รฐยยย Helpful suggestions for mistypings - Twin chimes in with class and variant values from your Tailwind config:
รขยย ml-1.25 was not found
Try one of these classes:
- ml-1.5 > 0.375rem
- ml-1 > 0.25rem
- ml-10 > 2.5rem
รฐยยยรฏยธย Use the theme import to add values from your tailwind config
import { css, theme } from 'twin.macro'
const Input = () => <input css={css({ color: theme`colors.purple.500` })} />
See more examples using the theme import รขยย
รฐยยยก Works with the official tailwind vscode plugin - Avoid having to look up your classes with auto-completions straight from your Tailwind config - setup instructions รขยย
รฐยยยฅ Add !important to any class with a trailing or leading bang!
<div tw="hidden!" /> || <div tw="!hidden" />
// รขยย รขยย รขยย รขยย รขยย รขยย รขยย รขยย รขยย
<div css={{ "display": "none !important" }} />
Add !important to multiple classes with bracket groups:
<div tw="(hidden ml-auto)!" />
// รขยย รขยย รขยย รขยย รขยย รขยย รขยย รขยย รขยย
<div css={{ "display": "none !important", "marginLeft": "auto !important" }} />
Get started
Twin works with many modern stacks - take a look at these examples to get started:
App build tools and libraries
- Parcel
styled-components / emotion / emotion (ts) - Webpack
styled-components (ts) / emotion (ts) - Preact
styled-components / emotion / goober - Create React App
styled-components / emotion - Vite
styled-components (ts) / emotion (ts) / solid (ts) - Jest / React Testing Library
styled-components (ts) / emotion (ts)
Advanced frameworks
- Next.js
styled-components / styled-components (ts) / emotion / emotion (ts) / stitches (ts) - T3 App
styled-components (ts) / emotion (ts) - Blitz.js
emotion (ts) - Gatsby
styled-components / emotion
Component libraries
- Storybook
styled-components (ts) / emotion / emotion (ts) - yarn/npm workspaces + Next.js + shared ui components
styled-components - Yarn workspaces + Rollup
emotion - HeadlessUI (ts)
Community
Drop into our Discord server for announcements, help and styling chat.
Resources
- รฐยยยฅ Docs: The prop styling guide - A must-read guide to level up on prop styling
- รฐยยยฅ Docs: The styled component guide - A must-read guide on getting productive with styled components
- Docs: Options - Learn about the features you can tweak via the twin config
- Plugin: babel-plugin-twin - Use the tw and css props without adding an import
- Example: Advanced theming - Add custom theming the right way using css variables
- Example: React + Tailwind breakpoint syncing - Sync your tailwind.config.js breakpoints with react
- Helpers: Twin VSCode snippets - For devs who want to type less
- Plugins: VSCode plugins - VScode plugins that work with twin
- Article: "Why I Love Tailwind" by Max Stoiber - Max (inventor of styled-components) shares his thoughts on twin
Special thanks
This project stemmed from babel-plugin-tailwind-components so a big shout out goes to Brad Cornes for the amazing work he produced. Styling with tailwind.macro has been such a pleasure.
Top Related Projects
A utility-first CSS framework for rapid UI development.
Visual primitives for the component age. Use the best bits of ES6 and CSS to style your apps without stress ๐
๐ฉโ๐ค CSS-in-JS library designed for high performance style composition
Documentation about css-modules
[Not Actively Maintained] CSS-in-JS with near-zero runtime, SSR, multi-variant support, and a best-in-class developer experience.
Zero-runtime Stylesheets-in-TypeScript
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot