A guide on theming your web applications with Styled Components.
Styled Components comes in build in with a ThemeProvider
to help you with this cause. Theme Provider is similar to a React Context Provider (in the sense that it is one). You have to wrap your content with a ThemeProvider
and you can get started:
import { ThemeProvider } from "styled-components";
function App() {
return (
<ThemeProvider theme={{}}>
<p>All the other stuff goes here...</p>
</ThemeProvider>
);
}
Theme can be any simple POJO. Consider:
const theme = {
colors: {
primary: `yellow`,
secondary: `red`,
}
}
return (
<ThemeProvider theme={theme}>
</ThemeProvider>
);
A theme can be accessed in a styled component with props.theme
usage. The only consideration being that where this Button
is rendered should be wrapped somewhere in it's parent with ThemeProvider
that provides it's theme
.
const Button = styled(Button)`
background-color: ${(props) => props.theme.primary};
`;
But what if it is not wrapped with a ThemeProvider
? If you believe in creating components that would work even without it's context parent, then you would want to give it some theme as defaultProps
.
const Button = styled(Button)`
background-color: ${(props) => props.theme.colors.primary};
`;
Button.defaultProps = {
theme: {
colors: {
primary: "transparent",
},
},
};
Multiple Theme Providers can be nested within one another. A component will pick up the theme from nearest Theme Provider it is nested within.
const Button = styled.button`
background-color: ${props => props.theme.colors.primary};
`;
const theme = {
colors: {
primary: `yellow`,
}
}
return (
<ThemeProvider theme={theme}>
<Button>Primary Button</Button>
<ThemeProvider theme={specialTheme}>
<Button>Special Button</Button>
</ThemeProvider>
</ThemeProvider>
);
Styled Components packs another trick in it's sleeve with nested Theme Providers. Styled Components delivers the current theme that it receives from it's parent as an argument which you can use to manipulate or add values to the theme.
import Navbar from "./Navbar";
const theme = (currentTheme) => ({
...currentTheme,
navbar: {
height: "6rem",
},
});
return (
<ThemeProvider theme={theme}>
<ThemeProvider theme={specialTheme}>
<Navbar />
</ThemeProvider>
</ThemeProvider>
);
Variants are how we can create components that adapt based on props. You might have seen these in UI libraries:
<Button primary>Primary Button</Button>
<Button secondary>Secondary Button</Button>
With styled-components, you can adapt based on props.
const Button = styled.button`
${(props) =>
props.primary &&
`
background-color: ${props.theme.colors.primary};
`}
${(props) =>
props.secondary &&
`
background-color: ${props.theme.colors.secondary};
`}
`;
The traditional way of building variants are a pain on scale as you can imagine. Especially if you are building a design system.
Styled Components family has a library called styled theming. It has an easier API for creating and maintaining variant based styles. For eg. To create a button that would be different in light and dark mode:
import styled, { ThemeProvider } from "styled-components";
import theme from "styled-theming";
const backgroundColor = theme("mode", {
light: "#f1c40f",
dark: "#f39c12",
});
const Button = styled.div`
background-color: ${backgroundColor};
`;
export default function App() {
return (
<ThemeProvider theme={{ mode: "light" }}>
<Button>Primary Button</Button>
</ThemeProvider>
);
}
Okay, but what if we need to create a secondary variant of this? That's where the variants
functions comes in to play.
import styled, { ThemeProvider } from "styled-components";
import theme from "styled-theming";
const backgroundColor = theme("mode", "variant", {
primary: {
light: "#f1c40f",
dark: "#f39c12",
},
secondary: {
light: "#2ecc71",
dark: "#27ae60",
},
});
const Button = styled.div`
background-color: ${backgroundColor};
`;
export default function App() {
return (
<ThemeProvider theme={{ mode: "light" }}>
<Button variant="primary">Primary Button</Button>
<Button variant="secondary">Secondary Button</Button>
</ThemeProvider>
);
}
What are some other styled-component magic ✨ you use? For using styled-components with TypeScript, see my post on that.