Styled Components & TypeScript - 😍
Tags: styledcomponents, react, typescript
Published: 2019-12-07 12:55:43

[Styled Components](https://www.styled-components.com/) happens to be one of my favorite CSS in JS libraries all time and have been part of almost all of my [ReactJS](https://reactjs.org/) 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.

### 1. Installing the types

Styled Components library does not ship with types. Instead we have to install it from the [Definitely Typed](https://github.com/DefinitelyTyped/DefinitelyTyped) repository.

```bash
npm i --save-dev @types/styled-components
```

### 2. Custom props 

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.

```typescript
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:

```typescript
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:

```typescript
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. 


### 3. Typing the theme

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.

```typescript
// 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:

```typescript
import {} from "styled-components";
import theme from "../theme";

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 🙌.

### 4. Making use of `css` prop

There 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](https://www.styled-components.com/docs/tooling#babel-plugin) is enabled.
 
```javascript
<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`:

```typescript
import {} from "styled-components";
import { CSSProp } from "styled-components";

declare module "react" {
  interface Attributes {
    css?: CSSProp | CSSObject;
  }
}
```

### 5. Media templates

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:

```typescript
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 />);
```

[Courtesy](https://github.com/styled-components/styled-components/issues/2303#issuecomment-480047731)

>

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](https://spectrum.chat/styled-components/general/typescript-support-for-refs~76750641-e0de-4fdb-82b8-f09aa5ca42f1).

Otherwise, Styled Components 💙 TypeScript.

> Originally posted on my [blog](https://blog.agney.dev/styled-components-&-typescript/)