@emotion/react provides CSS-in-JS theming utilities for React applications. It offers a comprehensive API for theme management through React Context, including a theme provider, theme consumer hook, higher-order component for theme injection, and direct context access.
Note: This functionality was previously available in the deprecated emotion-theming package, which was migrated to @emotion/react in version 11.0.0.
npm install @emotion/reactimport { ThemeProvider, useTheme, withTheme, ThemeContext } from '@emotion/react';For CommonJS:
const { ThemeProvider, useTheme, withTheme, ThemeContext } = require('@emotion/react');import React from 'react';
import { ThemeProvider, useTheme, withTheme } from '@emotion/react';
// Define your theme
const theme = {
colors: {
primary: '#007bff',
secondary: '#6c757d'
},
spacing: {
small: '8px',
medium: '16px',
large: '24px'
}
};
// Using ThemeProvider to provide theme
function App() {
return (
<ThemeProvider theme={theme}>
<Header />
<Content />
</ThemeProvider>
);
}
// Using useTheme hook to consume theme
function Header() {
const theme = useTheme();
return (
<header style={{ color: theme.colors.primary }}>
My App
</header>
);
}
// Using withTheme HOC to inject theme as prop
const Content = withTheme(({ theme }) => (
<main style={{ padding: theme.spacing.medium }}>
Content here
</main>
));React component that provides theme context to child components through React's Context API.
interface ThemeProviderProps {
theme: object | ((outerTheme: object) => object);
children: React.ReactNode;
}
declare const ThemeProvider: React.FC<ThemeProviderProps>;Features:
Usage with theme object:
const theme = { colors: { primary: '#007bff' } };
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>Usage with theme function:
<ThemeProvider theme={(outerTheme) => ({
...outerTheme,
colors: { ...outerTheme.colors, primary: '#007bff' }
})}>
<App />
</ThemeProvider>React hook that returns the current theme from the nearest ThemeProvider.
declare function useTheme(): object;Returns: The current theme object from ThemeContext
Usage:
function MyComponent() {
const theme = useTheme();
return (
<div style={{
color: theme.colors.primary,
padding: theme.spacing.medium
}}>
Themed content
</div>
);
}Requirements: Must be used within a component tree that has a ThemeProvider ancestor.
Higher-order component that injects the current theme as a theme prop into the wrapped component.
declare function withTheme<C extends React.ComponentType<React.ComponentProps<C>>>(
Component: C
): React.FC<DistributiveOmit<PropsOf<C>, 'theme'> & { theme?: Theme }>;Parameters:
Component: React component that expects a theme propReturns: Enhanced component with theme automatically injected
Features:
Usage:
// Component that expects theme prop
const Button = ({ theme, color, children, ...props }) => (
<button
style={{
backgroundColor: theme.colors[color],
padding: theme.spacing.small
}}
{...props}
>
{children}
</button>
);
// Enhanced component with automatic theme injection
const ThemedButton = withTheme(Button);
// Usage - theme prop is automatically provided
<ThemedButton color="primary">Click me</ThemedButton>React Context that provides direct access to the theme for advanced use cases.
declare const ThemeContext: React.Context<Theme>;Usage:
import React from 'react';
import { ThemeContext } from '@emotion/react';
function AdvancedComponent() {
const theme = React.useContext(ThemeContext);
return (
<div style={{ color: theme.colors.primary }}>
Direct context access
</div>
);
}When to use:
The css prop can access theme through function interpolation:
import { css } from '@emotion/react';
function ThemedComponent() {
return (
<div
css={theme => ({
color: theme.colors.primary,
padding: theme.spacing.medium,
backgroundColor: theme.colors.background
})}
>
Themed with css prop
</div>
);
}The ClassNames component provides theme in its render prop:
import { ClassNames } from '@emotion/react';
function ThemedWithClassNames() {
return (
<ClassNames>
{({ css, theme }) => (
<div
className={css`
color: ${theme.colors.primary};
padding: ${theme.spacing.medium};
`}
>
Themed with ClassNames
</div>
)}
</ClassNames>
);
}The Global component can use theme in global styles:
import { Global } from '@emotion/react';
function GlobalThemedStyles() {
return (
<Global
styles={theme => ({
html: {
backgroundColor: theme.colors.background,
color: theme.colors.text
},
'h1, h2, h3': {
color: theme.colors.primary
}
})}
/>
);
}// Extensible theme interface
export interface Theme {}
// ThemeProvider props
interface ThemeProviderProps {
theme: Partial<Theme> | ((outerTheme: Theme) => Theme);
children?: React.ReactNode;
}
// useTheme hook type
declare function useTheme(): Theme;
// withTheme HOC type
type withTheme = <C extends React.ComponentType<React.ComponentProps<C>>>(
component: C
) => React.FC<DistributiveOmit<PropsOf<C>, 'theme'> & { theme?: Theme }>;
// ThemeContext type
declare const ThemeContext: React.Context<Theme>;
// Helper types
type PropsOf<C extends keyof JSX.IntrinsicElements | React.JSXElementConstructor<any>> =
JSX.LibraryManagedAttributes<C, React.ComponentPropsWithRef<C>>;
type DistributiveOmit<T, K extends keyof any> = T extends any ? Omit<T, K> : never;The library performs validation in development mode:
ThemeProvider validation:
Common errors:
// ❌ Invalid - theme function returning non-object
<ThemeProvider theme={() => null}>
// ❌ Invalid - theme prop as array
<ThemeProvider theme={['red', 'blue']}>
// ✅ Valid - theme object
<ThemeProvider theme={{ color: 'red' }}>
// ✅ Valid - theme function returning object
<ThemeProvider theme={(outer) => ({ ...outer, color: 'red' })}>From emotion-theming to @emotion/react:
Update imports:
// Before
import { ThemeProvider, useTheme, withTheme } from 'emotion-theming';
// After
import { ThemeProvider, useTheme, withTheme, ThemeContext } from '@emotion/react';No API changes required - the functionality is identical, with the addition of ThemeContext export
Update package.json:
{
"dependencies": {
"@emotion/react": "^11.0.0"
}
}Remove emotion-theming dependency:
npm uninstall emotion-themingThe migration is a simple import path change with no functional differences, plus access to additional integration features with other @emotion/react components.