Create styled-component library

Part 2 —statically typed styled-components

Kacper Brezdeń
4 min readJan 26, 2019

If you’re interested in the first part of the article click the link below 🤓

Now that we’ve build our first component you’ve probably noticed that we didn’t take advantage either of static typescript or styled components conditional styles via props. To modify component’s styles conditionally we can take advantage of template string syntax inside of component definition.

// src/components/button/index.tsximport styled from "styled-components";

const StyledButton = styled.button`
padding-bottom: 10px;
padding-left: 20px;
padding-right: 20px;
padding-top: 15px;
background-color: ${props => (props.active ? "red" : "green")};
&:active {
outline: 0;
color: #fff;
top: 1px;
}
`;
export const Button = StyledButton;

There seems to be something wrong with our code though. We didn’t specify what kinds of properties does our component accept. That will trigger IDE red highlight and compilation error. To fix that we have to create interface and use it as a generic type of our component’s props.

// src/components/button/index.tsximport styled from "styled-components";interface ButtonProps {
active?: boolean;
}
const StyledButton = styled.button<ButtonProps>`
padding-bottom: 10px;
padding-left: 20px;
padding-right: 20px;
padding-top: 15px;
background-color: ${props => (props.active ? "red" : "green")};
(...)

No error 🎉. To pass properties to the styled component we simply pass them as for any other react component.

// src/components/button/index.tsximport React from "react";

import { storiesOf } from "@storybook/react";
import {Button} from './';

storiesOf("Button", module)
.add("with text", () => (
<div>
<Button active={true}>Active button</Button>
<Button active={false}>Not active button</Button>
</div>
));
Different styles based on active property

Now it seems to be working just fine. The story shows the correct styles for static properties. Let’s create interactive version of this story. In order to provide temporary state to the stories we can use storybook-addon @sambego/storybook-state.

npm i @sambego/storybook-state

Here is a snippet of story that changes button’s state after each click:

// src/components/button/index.tsximport React from "react";

import { storiesOf } from "@storybook/react";
import { Button } from "./";
import { State, Store } from "@sambego/storybook-state";

const store = new Store({
active: false
});

storiesOf("Button", module).add("with text", () => (
<State store={store}>
{state => (
<Button
active={state.active}
onClick={() => {
store.set({ active: !state.active });
}}
>
Active button
</Button>
)}
</State>
));
Another great success 😂

Theming

Theming is another great feature of the styled-components library. It allows you to store common values like colors, paddings or even functions in global object that will be provided to all of the components. It becomes even more powerful when we combine this feature with typescript goodness.

To add theme support, wrap your component in ThemeProvider to give additional property called theme to your component.

// src/components/button/button.stories.jsimport { ThemeProvider } from "styled-components";

...

<ThemeProvider theme={{activeColor: 'red', regularColor: 'green'}}>
<Button
active={state.active}
onClick={() => {
store.set({ active: !state.active });
}}
>
Active button
</Button>
</ThemeProvider>

...

This gives us ability to refer to theme attributes in styled component itself.

const StyledButton = styled.button<ButtonProps>`
...
background-color: ${props => (props.active ? props.theme.activeColor : props.theme.regularColor)};
...
Result is the same with theme

Theme props property is provided by styled components itself and we didn’t have to specify its structure because its type is set to any (that basically turns typescript type checking completely).

With bigger projects you probably wouldn’t like to have to guess what means what. It even comes in handy when you try to read your 6 months old code, believe me 🙈. With nice statically typed code there’s no need for guessing since everything is a result of something else. That’s why we don’t want ANY in our code!!! How do we go about this? We need to tell typescript what type of theme we’re using. To do so let’s define our theme type as specified below. And reexport styled from “styled-components” with statically typed theme.

// src/theme.tsexport interface Theme {
activeColor: string;
regularColor: string;
}
// src/styled.tsimport * as baseStyled from "styled-components";
import { Theme } from "./theme"; //Import our interface
const {
default: styledTyped /* Rename default export of baseStyled */,
ThemeProvider
} = baseStyled as baseStyled.ThemedStyledComponentsModule<Theme>;
// And cast it to ThemedStyledComponentsModule<Theme> with previously specified Theme typeexport { ThemeProvider };
export default styledTyped;

Then we have to change styled instance in our button component to use this reexported strongly typed version of styled-components created above.

// src/components/button/index.tsimport styled from "../../styled"; //Changed to our local styled

interface ButtonProps {
active?: boolean;
}
const StyledButton = styled.button<ButtonProps>`
...
Typescript intellisense works for theme property 🙉

If this doesn’t convince you that typescript is awesome I have some other React examples in the making. I sure think that it changes the way we write our apps and helps avoid many errors without the need to check your code manually.

In the next story I’ll cover testing, building and publishing your package to the npm repository. We’ll also play around with more storybook addons 🤩

--

--

Kacper Brezdeń

Full stack web developer, struggling for motivation to create something useful as a side project