TL;DR
Create theme.ts
I put mine in /theme/theme.ts
. This is what it looks like:
export type ThemeType = typeof light; // This is the type definition for my theme object.
export const light = {
primary: "#f45511",
text: "#000",
background: "#fff",
...
}
export const dark: ThemeType = {
primary: ...
...
}
const theme = light; // set the light theme as the default.
export default theme;
Create styled.d.ts
I put mine in /theme/styled.d.ts
. This is what it looks like:
import {} from 'styled-components';
import { ThemeType } from './theme'; // Import type from above file
declare module 'styled-components' {
export interface DefaultTheme extends ThemeType {} // extends the global DefaultTheme with our ThemeType.
}
-
import { ThemeType } from './theme';
-> This line imports my unique theme type definition from the abovetheme.ts
file. -
export interface DefaultTheme extends ThemeType {}
-> This line extends the global DefaultTheme to now include properties from my ThemeType.
Now props.theme.primary
will auto-complete and typescript will not have a compile error.
The Long Version
A common pattern in styled-components is to create a top-level theme object (just a plain old object) and have it be passed down to all components using ThemeProvider
. Here's an example:
import { ThemeProvider } from 'styled-components';
import theme from './theme';
const App = () => (
{/* theme obj being passed down through ThemeProvider */}
<ThemeProvider theme={theme}>
<Button>Click Me</Button>
</ThemeProvider>
)
// button.tsx
const Button = styled.button`
color: ${props => (
props.theme.primary
/* accessing props.theme */
)};
background-color: ${props => props.theme.background};
`;
As you can see, ThemeProvider
passes the theme
object to all child components. In the above case, the Button
component uses this theme
object to set its color and background.
Check out the styled-components docs for more details on theme support.
This is all great, but when we use Typescript, it will give us an error since it doesn't know primary
is a field inside theme
. Like this:
Typescript is complaining because the default theme object is just {}
. The ThemeProvider
injects an object of type DefaultTheme
into every prop and the definition for DefaultTheme
is just {}
. Take a look at the type definition.
To solve this we need to use declaration merging and extend DefaultTheme
. I've found the best way to do this is:
Create theme.ts
Create theme.ts
. I usually create this in /theme/theme.ts
. This is an example of what it could look like:
export type ThemeType = typeof light; // This is the type definition for my theme object.
export const light = {
primary: "#f45511",
text: "#000",
background: "#fff",
...
}
export const dark: ThemeType = {
primary: ...
...
}
const theme = light; // set the light theme as the default.
export default theme;
This creates the theme, but also exports a ThemeType
to be used by styled.d.ts
below.
Create styled.d.ts
Create a styled.d.ts
file in your project anywhere (most likely your bundler will pick it up. If not, that's something you'll have to look into).
Add this code to extend the DefaultTheme
and merge it with ThemeType
from your theme.ts
file.
import {} from 'styled-components';
import { ThemeType } from './theme'; // Import type from above file
declare module 'styled-components' {
export interface DefaultTheme extends ThemeType {} // extends the global DefaultTheme with our ThemeType.
}
That's it! You're typescript compiler and VSCode should stop complaining about props.theme.primary
.
If you enjoyed this post... I have nothing to ask of you. Cheers.
Top comments (2)
Helped me with a small annoyance. Thanks!
This was very helpful - thanks!