How to use styled-system with typescript
Styled-Components. There are a bunch of libraries already written for it one of which is styled-system which simplifies the creation of a component-based design system.
Now styled-system is awesome but I had some trouble finding good ways of integrating it with typescript especially when using custom components. So here is a little guide on how to do it.
Prerequisite
If you haven’t already install the types for styled-system.
npm install -D @types/styled-system
# or yarn
yarn add -D @types/styled-system
# or pnpm
pnpm add -D @types/styled-system
Normally Styled Tags
If you only need to pass some additional props to a styled component, the only thing to do is add a generic from ‘styled-system’ to it:
import { layout, LayoutProps } from 'styled-system'; import styled from 'styled-components'; export const Image = styled.img<LayoutProps>` ${layout} `;
There is a props equivalent for everything in Styled-System for example color is ColorProps, space is SpaceProps and so on.
Custom Components
Passing additional props to a component, beside the ones generated by ‘styled-system’, becomes a bit more tricky but there’s still a good solution for this. We can extend our Props type definition from the ‘styled-system’ types and pass that to our components:
import { layout, LayoutProps, position, PositionProps } from 'styled-system'; import styled from 'styled-components'; import React from 'react'; interface Props extends PositionProps, LayoutProps { children: React.ReactNode; } const Container = styled.div<Props>` background-color: blue; ${position}; ${layout}; `; export const DropdownMenu: React.FC<Props> = ({ children, ...props }) => { return <Container {...props}>{children}</Container>; };
Advanced Custom Components
If we want to add additional props to our styled div so that we can consume them for conditional styling for example we run into some conflicting types between our Div HTMLAttributes and custom props. Extending the HTMLAttributes does the trick here:
import { space, SpaceProps } from 'styled-system'; import styled from 'styled-components'; import React from 'react'; interface ContainerProps extends React.HTMLAttributes<HTMLDivElement> { isOwner: boolean; } type Props = ContainerProps & SpaceProps & { content: string; }; const Container = styled.div<ContainerProps>` ${space}; ${(props) => props.isOwner ? 'align-self: flex-end' : 'align-self: flex-start' }; `; export const Message: React.FC<Props> = ({ content, isOwner, ...props }) => { return ( <Container isOwner={isOwner} {...props}> {content} </Container> ); };
We want to give the styled div a custom prop “isOwner” to style it conditionally. For this we have to extend the props that the div element expects (HtmlAttributes)
We combine all props from the container, space props and our custom “content”-prop together into one type and pass that to the component. In the end we can pass:
- All Props that a normal div element would except, these would get passed to our Container
- All Space Props from styled-system, these get also passed to our Container
- Our Custom Props “isOwner” and “content”, “isOwner” get’s passed to the Container and “content” will be the child
We have a strongly typed Component that leverages styled-system, styled-components and custom props 🥳