Styled Components happens to be one of my favorite CSS in JS libraries all time and have been part of almost all of my ReactJS projects.
As I'm transitioning most of my projects to include TypeScript, there are things I stumbled along, but there are things that feel perfect. Listing down some of them here.
Styled Components library does not ship with types. Instead we have to install it from the Definitely Typed repository.
npm i --save-dev @types/styled-components
One of the major advantages of using a CSS-in-JS solution is the ability to pass custom props on runtime and adapt CSS accordingly.
const Heading = styled.h1<{ active: boolean }>`
color: ${(props) => (props.active ? "red" : "blue")};
`;
Just as in JSX Elements, you can pass the generic type with <>
after the component. Now, your styled-component is typed and there would be a static error on the element if you have not passed active
prop.
To use it for extending a component:
import Title from "./Title";
const Heading = styled(Title)<{ active: boolean }>`
color: ${(props) => (props.active ? "red" : "blue")};
`;
However, do note that active
as a prop is being passed to the Title
component even though it is not explicity said so. If someone adds an optional active
prop to the component later, this might be problematic. To avoid this, you can refractor to:
const Heading = styled(({ active, ...rest }) => <Title {...rest} />)<{
active: boolean;
}>`
color: ${(props) => (props.active ? "red" : "blue")};
`;
However, this syntax is obviously more convoluted and creates an extra component. Whether it is worth all the mess for uncovering an accidental prop is upto you.
Styled Components has the ability to specify a theme with the help of ThemeProvider
. You can later access the theme
with ${props=>props.theme.main.something}
. Even if we avoid everything else, just the autocomplete from the theme object is worth doing this for.
From the docs:
So the first step is creating a declarations file. Let's name it
styled.d.ts
for example.
// import original module declarations
import "styled-components";
// and extend them!
declare module "styled-components" {
export interface DefaultTheme {
borderRadius: string;
colors: {
main: string;
secondary: string;
};
}
}
But manually typing the theme like this is pain, mainly because you have to edit two different files everytime you add or remove something from the theme object. Instead you can do:
import theme from "../theme";
import "styled-components";
declare module "styled-components" {
type Theme = typeof theme;
export interface DefaultTheme extends Theme {}
}
Here, we are making use of Typescript's type inference for our theme
object to do it for us 🙌.
css
propThere are two css
functions in the Styled Components documentation for some reason. Here I'm talking about the css
attribute that can be used on an element when the Babel plugin is enabled.
<div
css={`
display: flex;
`}
>
...
</div>
But TypeScript is not aware of this css
property and produces an error. I don't know about you, but those red lines do very well bother me 👻.
To get around this, you can add the following to the styled.d.ts
:
import "styled-components";
import { CSSProp } from "styled-components";
declare module "react" {
interface Attributes {
css?: CSSProp | CSSObject;
}
}
There is an easy for specifying media queries from the documentation, but while the syntax for it is user friendly, the implementation by itself is hard to reason about for TypeScript (and as it happens, for new users too).
Instead I find myself using a much simpler alternative:
const customMediaQuery = (maxWidth: number) =>
`@media (max-width: ${maxWidth}px)`;
const media = {
custom: customMediaQuery,
desktop: customMediaQuery(922),
tablet: customMediaQuery(768),
phone: customMediaQuery(576),
};
const Content = styled.div`
height: 3em;
width: 3em;
background: papayawhip;
/* Now we have our methods on media and can use them instead of raw queries */
${media.desktop} {
background: dodgerblue;
}
${media.tablet} {
background: mediumseagreen;
}
${media.phone} {
background: palevioletred;
}
`;
render(<Content />);
That one pain point I still have is about the ref
. Adding a ref
to a styled component still gives me an error, the same as it did an year ago.
Otherwise, Styled Components 💙 TypeScript.