How to make dynamic interfaces with styled-components themes in React-TypeScript
Table of contents
No headings in the article.
If you’re using React with TypeScript and styled-components (or similar CSS-in-JS) libraries you probably came across this situation:
You have a theme defined. It might look something like this:
const theme = {
colors: {
black: '#000',
orange: '#f89f92',
pink: '#ff96db',
purple: '#181257',
white: '#fff
},
// rest of the theme goes here
};
Wouldn’t it be cool if your component’s props can autocomplete with values you put in your theme?
##The problem You probably want to use some (all?) of the theme as properties in your component. Things like paddings, margins, colors, font sizes, etc. But because you’re working with Typescript you want to define what values are accepted into the props. In the most generic case that would be all the available values defined in the theme.
The easiest solution is to define your component interfaces like so:
interface IText extends React.HTMLAttributes<HTMLElement> {
color?: 'black' | 'orange' | ...etc;
}
This limits the allowed values of the color prop to be one of the strings you’ve listed.
But we already have all the information defined in the theme and don’t want to repeat ourselves and/or update these values in your theme AND in your interface.
##The solution The component below has a singlecolor prop, which accepts only the colors defined in the theme as values.
import * as React from 'react';
import styled from 'styled-components';
import theme from './theme';
interface IText extends React.HTMLAttributes<HTMLElement> {
color?: keyof typeof theme.colors;
}
interface IStyledProps {
color: string;
}
const StyledText = styled.p`
color: ${(props: IStyledProps) => props.theme.colors[props.color]};
`;
const Typography = (props: IText) => (
<StyledText color={props.color || 'black'}>
{props.children}
</StyledText>
);
This is achieved by mapping all the keys in our theme (the color names, not the actual hex codes) in an “or statement” using the keyof and typeof operators.
In other words this:
keyof typeof theme.colors
Generates this:
'black' | 'orange' | ...etc
You can now start using your component, feeling secure it will work as intended and using that sweet, sweet intellisense!
Here is a sandbox where you can see this working.