styled-system props typing with TypeScript

I’m using styled-system and one key of the library is to use the shorthand props to allow easy and fast theming.

I’ve simplified my component but here is the interesting part:

import React from 'react'
import styled from 'styled-components'
import { color, ColorProps } from 'styled-system'

const StyledDiv = styled('div')<ColorProps>`
  ${color}
`

const Text = ({ color }: ColorProps) => {
  return <StyledDiv color={color} />
}

I have an error on the color prop which says:

Type ‘string | (string | null)[] | undefined’ is not assignable to
type ‘string | (string & (string | null)[]) | undefined’.

I think that’s because styled-system use the same naming as the native HTML attribute color and it conflicts.

How do I solve this?

21 thoughts on “styled-system props typing with TypeScript”

  1. color seems to be declared in react’s declaration file under HTMLAttributes – it’s not exported.

    I had to work around this by creating a custom prop

    Example is using @emotion/styled but also works with styled-components

    // component.js
    import styled from '@emotion/styled';
    import { style, ResponsiveValue } from 'styled-system';
    import CSS from 'csstype';
    
    const textColor = style({
      prop: 'textColor',
      cssProperty: 'color',
      key: 'colors'
    });
    
    
    type Props = {
      textColor?: ResponsiveValue<CSS.ColorProperty>
    }
    
    const Box = styled.div<Props>`
      ${textColor};
    `
    
    export default Box;
    
    // some-implementation.js
    import Box from '.';
    
    const Page = () => (
      <Box textColor={['red', 'green']}>Content in a box</Box>
    );
    
    Reply
  2. Building on Chris’ answer, and using the latest docs on on custom props.

    // core/constants/theme.ts
    // Your globally configured theme file
    export const theme = { colors: { primary: ['#0A43D2', '#04122B'] } }
    
    // core/constants/styledSystem.ts
    import {
      color as ssColor,
      ColorProps as SSColorProps,
      TextColorProps,
      compose,
      system,
    } from 'styled-system'
    
    // Styled-system patch for the color prop fixing "Types of property 'color' are incompatible"
    // when appling props to component that extend ColorProps.
    export interface ColorProps extends Omit<SSColorProps, 'color'> {
      textColor?: TextColorProps['color']
    }
    
    export const color = compose(
      ssColor,
      system({
        // Alias color as textColor
        textColor: {
          property: 'color',
          // This connects the property to your theme, so you can use the syntax shown below E.g "primary.0".
          scale: 'colors'
        }
      })
    )
    
    // components/MyStyledComponent.ts
    import { color, ColorProps } from 'core/constants/styledSystem.ts'
    
    interface MyStyledComponentProps extends ColorProps {}
    
    export const MyStyledComponent = styled.div<MyStyledComponentProps>`
      ${color}
    `
    
    // components/MyComponent.ts
    export const MyComponent = () => <MyStyledComponent textColor="primary.0">...
    

    EDIT: updating to styled-components ^5.0.0 fixes this

    https://github.com/styled-components/styled-components/blob/master/CHANGELOG.md#v500—2020-01-13

    Reply
  3. What I did was to use Typescript cast capabilities and keep styled-system logic intact. e.g.:

    const Heading: React.FC<ColorProps> = ({ color, children }) => {
      return <HeadingContainer color={(color as any)} {...props}>{children}</HeadingContainer>;
    };
    
    Reply
  4. Just to add to xuanlopez‘ answer – not sure what issue the 5.0.0 release specifically resolves – but using $color as the renamed prop rather than textColor designates it as a transient prop in styled components so as a prop it won’t appear in the rendered DOM.

    Reply

Leave a Comment