Add Theme-Based Styling to a React App with styled-components ThemeProvider
Learn how to set up styled-components ThemeProvider to share a theme across a React app, consume it in styled components, and switch themes at runtime.
14 Dec 2025, 22:49 UTC

Desired Outcome
Configure a React project so styled-components can access a shared theme object, enabling consistent design tokens and runtime theme switching without a page reload.
Prerequisites
- Node.js >= 14 and npm or yarn installed.
- A React application using styled-components version 5 or later.
- Basic familiarity with JSX and ES6 syntax.
Procedure
-
Install styled-components
Run in the project root:
# npm npm install styled-components # or yarn yarn add styled-componentsStandard package installation permissions are sufficient.
-
Create theme objects
Define design tokens in
src/theme.js:// src/theme.js export const lightTheme = { colors: { primary: '#1976d2', background: '#ffffff', text: '#212121' }, spacing: { unit: 8 }, typography: { fontFamily: "'Roboto', sans-serif", fontSizeBase: '16px' } }; export const darkTheme = { colors: { primary: '#90caf9', background: '#121212', text: '#eeeeee' }, spacing: { unit: 8 }, typography: { fontFamily: "'Roboto', sans-serif", fontSizeBase: '16px' } }; -
Wrap the app with ThemeProvider
Place
ThemeProviderat the root of the component tree, typically insrc/index.jsorsrc/main.jsx:// src/index.js import React from 'react'; import ReactDOM from 'react-dom/client'; import { ThemeProvider } from 'styled-components'; import { lightTheme } from './theme'; import App from './App'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <ThemeProvider theme={lightTheme}> <App /> </ThemeProvider> );Note: Components outside this tree receive
undefinedforprops.theme. -
Consume the theme in styled components
Use
props.themeinside interpolations. For regular React components that need theme values outside CSS, use theuseThemehook. Interpolations are not React components, so hooks cannot be called inside them.// src/components/Button.js import styled from 'styled-components'; const Button = styled.button` background-color: ${props => props.theme.colors.primary}; color: ${props => props.theme.colors.text}; padding: ${props => `${props.theme.spacing.unit}px ${props.theme.spacing.unit * 2}px`}; font-family: ${props => props.theme.typography.fontFamily}; font-size: ${props => props.theme.typography.fontSizeBase}; border: none; cursor: pointer; `; export default Button;If a non-styled component needs the theme, call
useThemeat the top level of that component:// src/components/ThemeLabel.js import React from 'react'; import { useTheme } from 'styled-components'; const ThemeLabel = () => { const theme = useTheme(); return <p>Current primary: {theme.colors.primary}</p>; }; export default ThemeLabel; -
Switch themes at runtime
Replace the theme object passed to
ThemeProviderto trigger a re-render of consuming components. Keep theme state in a component above the provider:// src/index.js import React, { useState } from 'react'; import ReactDOM from 'react-dom/client'; import { ThemeProvider } from 'styled-components'; import { lightTheme, darkTheme } from './theme'; import App from './App'; function AppWrapper() { const [isDark, setIsDark] = useState(false); const theme = isDark ? darkTheme : lightTheme; return ( <ThemeProvider theme={theme}> <App isDark={isDark} onToggleTheme={() => setIsDark(!isDark)} /> </ThemeProvider> ); } const root = ReactDOM.createRoot(document.getElementById('root')); root.render(<AppWrapper />);Expose the toggle through props so child components can update the state:
// src/App.js import React from 'react'; import Button from './components/Button'; function App({ isDark, onToggleTheme }) { return ( <div> <h1>Themed Button</h1> <Button onClick={onToggleTheme}> Switch to {isDark ? 'Light' : 'Dark'} Mode </Button> </div> ); } export default App;
Expected Checks
- Inspect a rendered button in browser devtools and confirm the computed
background-colormatches the current theme token (for example,#1976d2in light mode). - Click the toggle button and verify colors change immediately without a page reload.
- Write a unit test with Jest and React Testing Library that renders the component inside a
ThemeProviderwith a known theme and asserts the computed styles orprops.themevalues.
Recovery Options
- If
props.themeisundefined, confirm the component is rendered inside theThemeProvidertree and that the provider is not conditionally unmounted. - Do not mutate the theme object. Create a new object when changing values, as shown in the dark/light toggle.
- For server-side rendering, wrap the application with
StyleSheetManagerto avoid duplicate style tags and CSS flashing.
This guide modifies application code and dependencies. To revert, remove the ThemeProvider wrapper and restore the previous component tree, or uninstall styled-components if it was added solely for this task.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.