skip to Main Content

I have created a Text component which receives couple of props. These props are passed to the styled component, where styles are applied. I dont want to pass props to the DOM, I only want to access them inside styled component.

Converting props to transient props works, but it creates repeating code which I think makes the whole component unnecessarily bigger.

I would like to know, how to pass some props to my styled component without passing them to the DOM and without having to redefine every prop. I tried using shouldForwardProps but I cant get it to work with Typescript.

type Props = {
  children: React.ReactNode;
  size: number;
  height: number;
  color: string;
};

const StyledText = styled.p<{ $color: string; $size: number; $height: number }>`
  color: ${({ $color }) => $color};
  font-size: ${({ $size }) => `${$size}px;`};
  line-height: ${({ $height }) => `${$height}px;`};
  ...
`;

const Text: React.FC<Props & React.HTMLAttributes<HTMLParagraphElement>> = ({ children, size, height, color, ...rest }) => {
  return (
    <StyledText $size={size} $height={height} $color={color} {...rest}>
      {children}
    </StyledText>
  );
};

2

Answers


  1. What you should thrive for is having a default StyledText component and then override this component using the styled function

    const StyledText = styled.p`
      color: #000;
      font-size: 1rem;
      line-height: 130%;
    `;
    

    and then you override it like this

    const StyledTextLarge = styled(StyledText)`
      color: #333333;
      font-size: 1.2rem;
      line-height: 140%;
    `;
    

    Usually in a design system, you define the different variants of your text/typography. It is limited (ie: heading, subheading, text). Therefore it is better to have different components like this than influencing the final user to pass variables that risk not to comply with the design you imagined

    Login or Signup to reply.
  2. Here is a solution using shouldForwardProps as you suggested which I believe is what you are asking for. I’ve tried to tie in the customAttributeNames to the Props, but I’ve given up on that.

    const customAttributeNames = ["size", "height", "color"] as const;
    
    type TCustomAttribute = typeof customAttributeNames[number];
    
    type Props = React.HTMLAttributes<HTMLParagraphElement> & {
      children: React.ReactNode;
      size: number;
      height: number;
      color: string;
    };
    
    const StyledTextWithoutDomAttributes = styled.p.withConfig({
      shouldForwardProp: (prop) =>
        !(customAttributeNames as readonly string[]).includes(prop)
    })<Pick<Props, TCustomAttribute>>`
      color: ${({ color }) => color};
      font-size: ${({ size }) => `${size}px;`};
      line-height: ${({ height }) => `${height}px;`};
    `;
    
    const StyledTextWithDomAttributes = styled.p<Pick<Props, TCustomAttribute>>`
      color: ${({ color }) => color};
      font-size: ${({ size }) => `${size}px;`};
      line-height: ${({ height }) => `${height}px;`};
    `;
    
    const Text: React.FC<Props> = ({ children, size, height, color, ...rest }) => {
      return (
        <>
          <StyledTextWithDomAttributes
            size={size}
            height={height}
            color={color}
            {...rest}
          >
            {children}
          </StyledTextWithDomAttributes>
          <StyledTextWithoutDomAttributes
            size={size}
            height={height}
            color={color}
            {...rest}
          >
            {children}
          </StyledTextWithoutDomAttributes>
        </>
      );
    };
    

    Here is a sandbox so you can see it working.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search