React Emotion
Introduction
Emotion is a popular CSS-in-JS library that allows you to write CSS styles with JavaScript. It provides a flexible and powerful way to style your React components with dynamic capabilities. Emotion combines the best parts of inline styles, styled components, and traditional CSS to create a styling solution that works seamlessly with React's component model.
In this guide, you'll learn how to use Emotion in your React applications and explore its key features that make styling React components more maintainable and powerful.
Installation
To get started with Emotion in your React project, you'll need to install a couple of packages:
# Using npm
npm install @emotion/react @emotion/styled
# Using yarn
yarn add @emotion/react @emotion/styled
Basic Usage with the css Prop
One of the simplest ways to use Emotion is with the css
prop. However, to use the css prop, you'll need to add the JSX Pragma at the top of your file or configure Babel.
Let's see how to use the css
prop with the JSX Pragma:
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';
function Button({ children }) {
return (
<button
css={css`
background-color: #4CAF50;
border: none;
color: white;
padding: 10px 20px;
text-align: center;
font-size: 16px;
cursor: pointer;
border-radius: 4px;
transition: background-color 0.3s;
&:hover {
background-color: #45a049;
}
`}
>
{children}
</button>
);
}
export default Button;
This creates a styled button component with hover effects. The css
prop accepts a tagged template literal with CSS syntax. You can use nested selectors (like &:hover
) similar to SCSS.
Using Object Styles
If you prefer to write your styles as JavaScript objects, Emotion supports that too:
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';
function Card({ title, content }) {
return (
<div
css={css({
padding: '20px',
backgroundColor: 'white',
borderRadius: '8px',
boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)',
margin: '16px 0'
})}
>
<h2
css={css({
fontSize: '18px',
color: '#333',
marginBottom: '10px'
})}
>
{title}
</h2>
<p
css={css({
fontSize: '14px',
color: '#666'
})}
>
{content}
</p>
</div>
);
}
export default Card;
Styled Components with Emotion
Emotion provides a styled
API similar to styled-components. This allows you to create reusable styled components:
import styled from '@emotion/styled';
// Create a styled button
const Button = styled.button`
background-color: ${props => props.primary ? '#4CAF50' : '#f0f0f0'};
color: ${props => props.primary ? 'white' : 'black'};
padding: 10px 20px;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
background-color: ${props => props.primary ? '#45a049' : '#e0e0e0'};
transform: translateY(-2px);
}
`;
// Usage
function App() {
return (
<div>
<Button primary>Primary Button</Button>
<Button>Secondary Button</Button>
</div>
);
}
The styled
API creates components with styles attached to them. The styles can access props (like primary
in the example) to conditionally apply styling.
Dynamic Styling with Props
One of the most powerful features of Emotion is the ability to style components dynamically based on props:
import styled from '@emotion/styled';
const Box = styled.div`
padding: ${props => props.padding || '10px'};
margin: ${props => props.margin || '0'};
background-color: ${props => props.bgColor || '#fff'};
border: ${props => props.border || 'none'};
border-radius: ${props => props.rounded ? '8px' : '0'};
box-shadow: ${props => props.shadow ? '0 2px 4px rgba(0,0,0,0.1)' : 'none'};
width: ${props => props.width || 'auto'};
max-width: ${props => props.maxWidth || 'none'};
`;
// Usage
function App() {
return (
<div>
<Box padding="20px" bgColor="#f5f5f5" rounded shadow>
This is a box with custom styling!
</Box>
<Box padding="10px" border="1px solid #ddd" margin="20px 0">
Another box with different styling.
</Box>
</div>
);
}
Global Styles
Sometimes you need to apply global styles to your application. Emotion provides the Global
component for this purpose:
import { Global, css } from '@emotion/react';
function GlobalStyles() {
return (
<Global
styles={css`
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica,
Arial, sans-serif;
font-size: 16px;
line-height: 1.5;
color: #333;
background-color: #f9f9f9;
}
a {
color: #0077cc;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
`}
/>
);
}
// Use in your top-level App component
function App() {
return (
<>
<GlobalStyles />
{/* Your app content */}
</>
);
}
Theming with Emotion
Emotion integrates with React's Context API to provide theming capabilities:
import { ThemeProvider } from '@emotion/react';
import styled from '@emotion/styled';
// Define your theme
const theme = {
colors: {
primary: '#0070f3',
secondary: '#1a2a5e',
success: '#0070f3',
error: '#ff0000',
background: '#ffffff',
text: '#333333'
},
fonts: {
body: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
heading: 'Georgia, serif'
},
fontSizes: {
small: '14px',
medium: '16px',
large: '18px',
xlarge: '24px',
xxlarge: '32px'
},
spacing: {
small: '8px',
medium: '16px',
large: '24px',
xlarge: '32px'
}
};
// Create a themed button
const ThemedButton = styled.button`
background-color: ${props => props.theme.colors.primary};
color: white;
font-family: ${props => props.theme.fonts.body};
font-size: ${props => props.theme.fontSizes.medium};
padding: ${props => props.theme.spacing.small} ${props => props.theme.spacing.medium};
border: none;
border-radius: 4px;
cursor: pointer;
&:hover {
background-color: ${props => props.theme.colors.secondary};
}
`;
// Applying the theme
function App() {
return (
<ThemeProvider theme={theme}>
<div>
<h1>Themed Components</h1>
<ThemedButton>Themed Button</ThemedButton>
</div>
</ThemeProvider>
);
}
Composition and Reuse
Emotion makes it easy to compose and reuse styles:
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';
// Reusable styles
const flexCenter = css`
display: flex;
justify-content: center;
align-items: center;
`;
const card = css`
padding: 20px;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
`;
function ProfileCard({ name, role, children }) {
return (
<div
css={css`
${card}
width: 300px;
margin: 20px auto;
`}
>
<div
css={css`
${flexCenter}
flex-direction: column;
`}
>
<h2>{name}</h2>
<p>{role}</p>
{children}
</div>
</div>
);
}
function App() {
return (
<ProfileCard name="Jane Doe" role="Developer">
<button
css={css`
margin-top: 10px;
padding: 8px 16px;
background-color: #0077cc;
color: white;
border: none;
border-radius: 4px;
`}
>
Contact Me
</button>
</ProfileCard>
);
}
Real-world Example: A Theme Switcher
Let's create a practical example that demonstrates how to build a theme switcher with Emotion:
/** @jsxImportSource @emotion/react */
import React, { useState, useContext } from 'react';
import { css, ThemeProvider, Global } from '@emotion/react';
import styled from '@emotion/styled';
// Define themes
const themes = {
light: {
name: 'light',
colors: {
background: '#ffffff',
text: '#333333',
primary: '#0070f3',
secondary: '#f5f5f5',
border: '#dddddd'
}
},
dark: {
name: 'dark',
colors: {
background: '#121212',
text: '#e0e0e0',
primary: '#4da6ff',
secondary: '#2d2d2d',
border: '#444444'
}
}
};
// Create ThemeContext
const ThemeContext = React.createContext({
theme: themes.light,
toggleTheme: () => {}
});
// Button component
const Button = styled.button`
background-color: ${props => props.theme.colors.primary};
color: ${props => props.theme.name === 'dark' ? '#ffffff' : '#ffffff'};
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
margin: 5px;
transition: background-color 0.3s ease;
&:hover {
opacity: 0.9;
}
`;
// Card component
const Card = styled.div`
background-color: ${props => props.theme.colors.secondary};
color: ${props => props.theme.colors.text};
padding: 20px;
border-radius: 8px;
border: 1px solid ${props => props.theme.colors.border};
margin: 20px 0;
transition: all 0.3s ease;
`;
const Container = styled.div`
max-width: 800px;
margin: 0 auto;
padding: 20px;
`;
// ThemeSwitcher component
function ThemeSwitcher() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button
onClick={toggleTheme}
css={css`
background-color: ${theme.colors.secondary};
color: ${theme.colors.text};
padding: 10px;
border: 1px solid ${theme.colors.border};
border-radius: 4px;
cursor: pointer;
position: fixed;
top: 20px;
right: 20px;
`}
>
Switch to {theme.name === 'light' ? 'Dark' : 'Light'} Mode
</button>
);
}
// App component
function App() {
const [currentTheme, setCurrentTheme] = useState(themes.light);
const toggleTheme = () => {
setCurrentTheme(currentTheme.name === 'light' ? themes.dark : themes.light);
};
const themeContextValue = {
theme: currentTheme,
toggleTheme
};
return (
<ThemeContext.Provider value={themeContextValue}>
<ThemeProvider theme={currentTheme}>
<Global
styles={css`
body {
background-color: ${currentTheme.colors.background};
color: ${currentTheme.colors.text};
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
margin: 0;
padding: 0;
transition: background-color 0.3s ease, color 0.3s ease;
}
`}
/>
<Container>
<ThemeSwitcher />
<h1>Emotion Theme Switcher Demo</h1>
<Card>
<h2>What is Emotion?</h2>
<p>Emotion is a performant and flexible CSS-in-JS library that lets you write styles with JavaScript.</p>
<Button>Learn more</Button>
</Card>
<Card>
<h2>Key Features</h2>
<ul
css={css`
li {
margin: 8px 0;
}
`}
>
<li>Dynamic styling with props</li>
<li>Theme support</li>
<li>Composition and style reuse</li>
<li>Global styles</li>
<li>Server-side rendering support</li>
</ul>
<Button>View Documentation</Button>
</Card>
</Container>
</ThemeProvider>
</ThemeContext.Provider>
);
}
export default App;
Performance Considerations
Emotion is designed to be performant, but here are some tips to ensure your styles don't impact performance:
- Memoize dynamic styles: When creating styles that depend on props, consider using
React.useMemo
to prevent unnecessary style recalculations.
import React, { useMemo } from 'react';
import { css } from '@emotion/react';
function DynamicComponent({ color, fontSize }) {
const dynamicStyle = useMemo(() =>
css`
color: ${color};
font-size: ${fontSize}px;
`,
[color, fontSize]
);
return <div css={dynamicStyle}>Dynamic Content</div>;
}
- Extract reusable styles: Define common styles outside of your component to avoid recreating them on each render.
Summary
Emotion is a powerful CSS-in-JS library that provides a flexible and intuitive way to style your React applications. We've covered the basic usage with the css
prop, object styles, the styled
API, dynamic styling with props, global styles, theming, composition, and a real-world example.
Key takeaways:
- Emotion allows you to write CSS directly in your JavaScript/React code
- It supports dynamic styling based on props and themes
- You can use both string styles (template literals) or object styles
- Emotion makes it easy to reuse and compose styles
- It provides theming capabilities via React's Context API
- Emotion is designed to be performant but requires some considerations for optimal performance
Additional Resources
To deepen your understanding of Emotion, consider exploring these resources:
- Emotion Official Documentation
- Theming in Emotion
- Composition Patterns
- Server-Side Rendering with Emotion
Exercises
- Create a Button component with multiple variants (primary, secondary, warning, danger) using Emotion's styled API.
- Build a simple form with styled form elements (inputs, labels, submit button) using Emotion.
- Implement a card component with different visual states based on props (loading, error, success).
- Create a responsive navigation bar that adapts to different screen sizes using Emotion's media queries.
- Build a theme switcher that allows users to toggle between different color schemes.
By completing these exercises, you'll gain hands-on experience with Emotion and strengthen your styling skills in React.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)