This post is about highlighting the main differences between Emotion and Stitches. Some of these differences are related to the authoring experience. Others are about what happens behind the scenes.
Disclaimer: This post isn't about comparing which one is best. Emotion and Stitches are two different CSS-in-JS solutions with different concerns.
styled
functionIn Emotion, the styled
function is a default import.
In Stitches, the styled
function is a named import.
// Emotion
import styled from '@emotion/styled';
// Stitches
import { styled } from '@stitches/react';
In Emotion, you can write CSS in template strings or in an object syntax.
In Stitches, you write CSS using the object style syntax. The reasons for this are: performance, bundle size and developer experience (type checks and autocomplete suggestions for both properties and values).
// Emotion (string syntax)
const Button = styled.button`
color: red;
font-size: 14px;
:hover {
color: black;
font-size: 14px;
}
`;
// Stitches
const Button = styled('button', {
color: 'red',
fontSize: '14px';
'&:hover': {
color: 'black',
fontSize: '14px';
},
});
In Emotion, pseudo-classes and pseudo-selectors are automatically chained to the scoped selector. However, other selectors, such as class or id selectors, require the &
sign, otherwise they're considered descendants.
In Stitches, all chained selectors require the &
sign.
const Button = styled.button`
// chained
:hover {
}
// chained
::before {
}
// descendant
.class {
}
`;
// Stitches
const Button = styled('button', {
// all chained
'&:hover': {},
'&::before': {},
'&.class': {},
});
In Emotion, you can interpolate the component's props to conditionally set styles.
In Stitches, we've introduced the concept of variants. You can conditionally apply variants at the consumption level, including at different breakpoints.
// Emotion
const Button = styled.button`
${(props) =>
props.color === 'violet' &&
`
background-color: 'blueviolet'
`}
${(props) =>
props.color === 'gray' &&
`
background-color: 'gainsboro'
`}
`;
// Stitches
const Button = styled('button', {
variants: {
color: {
violet: { backgroundColor: 'blueviolet' },
gray: { backgroundColor: 'gainsboro' },
},
},
});
() => <Button color="violet">Button</Button>;
In Emotion, you can add a theme via the <ThemeProvider/>
. For this, you need to install an additional emotion-theming
package. The theme gets injected to every component, and you can access it via prop interpolation.
In Stitches, you can define tokens in the config file and seamlessly consume and access directly in the Style Object.
// Emotion
import { ThemeProvider } from 'emotion-theming';
() => (
<ThemeProvider
theme={{
colors: {
red500: 'tomato',
},
space: {
1: '5px',
2: '10px',
},
}}
>
<App />
</ThemeProvider>
);
const Button = styled.button`
color: ${(props) => props.theme.colors.red500};
margin: ${(props) => `${props.theme.space[1]} ${props.theme.space[2]`};
`;
// Stitches
const { styled } = createStitches({
tokens: {
colors: {
red500: 'tomato',
},
space: {
1: '5px',
2: '10px',
},
},
});
const Button = styled('button', {
color: '$red500',
margin: '$1 $2',
});
In Emotion, you can add at-rules directly in the CSS.
In Stitches, you can do the same. But you can also define breakpoints in the media
object and access directly in the Style Object.
// Emotion
const Box = styled.div`
padding: 12px;
@media (min-width: 480px) {
padding: 24px;
}
`;
// Stitches
const { styled } = createStitches({
media: {
'@bp1': '(min-width: 480px)',
},
});
const Box = styled('div', {
padding: '12px',
'@bp1': {
padding: '24px',
},
});
In Emotion, you can add global styles with the Global
component from the @emotion-core
package.
In Stitches, you can use the global
API. Learn more here.
// Emotion
import { Global } from '@emotion/core';
export function EmotionApp() => (
<>
<Global
styled={{
body: { margin: '0' },
}}
/>
</>
);
// Stitches
import { globalCss } from '@stitches/react';
const globalStyles = globalCss({
body: {
margin: '0',
},
});
export function App() => {
globalStyles();
return <div>Your app</div>
}
In Emotion, you can import the keyframes
function from @emotion/core
.
In Stitches, you can use the keyframes
function. Learn more here.
// Emotion
import { keyframes } from '@emotion/core';
import style from '@emotion/styled';
const fadeIn = keyframes`
0% { opacity: 0 }
100% { opacity: 1 }
`;
const Box = styled.div`
animation-name: ${fadeIn};
`;
// Stitches
import { keyframes } from '@stitches/react';
const fadeIn = keyframes({
'0%': { opacity: '0' },
'100%': { opacity: '1' },
});
const Box = styled('div', {
animationName: fadeIn,
});
In Emotion, there are two ways to server-side render. Each with their own pros and cons. You can read more about it here. The default approach is to rely on renderToString
from react-dom/server
. The advanced approach is to rely on several packages from @emotion/core
, create-emotion-server
and @emotion/cache
.
In Stitches, you use the getCssText
function. Learn more about server-side rendering. Many UI libraries document that responsive variants don't work with server-side rendering. Stitches supports cross-browser server-side rendering, even for responsive styles and variants.
Stitches is a lightweight, performant styling library with a focus on component architecture and developer experience.
Stitches avoids unnecessary prop interpolations at runtime, making it more performant than other styling libraries.
Both @stitches/core
and @stitches/react
libraries weigh in at ~6kb gzipped.
Stitches is built on TypeScript. By default, everything you do in Stitches will be type checked. As a user, you'll be suggested autocompletion for CSS Properties, token usage in values, breakpoints and related element attributes. Variants are also typed automatically.
When choosing a CSS-in-JS library, keep in mind what your goals are. As demonstrated in this article, both libraries are capable of achieving many of the same goals.
If you decide to give Stitches a try, make sure to check out our GitHub, Twitter and Discord.
Share this post on Twitter.