styled-components defaultProps
Asked Answered
C

5

9

If I have the following button with a defaultProp

export interface IButton {
  variant: 'action' | 'secondary';
}

export const Button = styled('button')<IButton>`
  background-color: #fff;

  ${props =>
    props.variant === 'action' &&
    css`
      color: blue;
    `};

  ${props =>
    props.variant === 'secondary' &&
    css`
      color: gray;
    `};
`;

Button.defaultProps = {
  variant: 'action',
};

Is there a way to type it? When trying to use it like

<Button>Hello</Button>

Typescript complains about not passing variant, is there a way to type defaultProps with styled components?

Cychosz answered 7/9, 2018 at 16:41 Comment(1)
This question is outdated. This now works with StyledComponents + TypeScript + React. See my answer below.Jada
E
5

The problem is that TypeScript 3.0's support for defaultProps in checking JSX elements requires the type of the defaultProps to be declared on the component. Mutating the defaultProps of an existing component won't work, and I'm not aware of any good way to declare the defaultProps on a component generated by a function like styled. (In a way, this makes sense: the library creates a component and doesn't expect you to modify it. Maybe the library even sets defaultProps itself for some internal purpose.) kingdaro's solution is fine, or you can use a wrapper component:

const Button1 = styled('button')<IButton>`
  background-color: #fff;

  ${props =>
    props.variant === 'action' &&
    css`
      color: blue;
    `};

  ${props =>
    props.variant === 'secondary' &&
    css`
      color: gray;
    `};
`;

export class Button extends React.Component<IButton> {
  static defaultProps = {
    variant: 'action'
  };
  render() {
    return <Button1 {...this.props}/>;
  }
}
Epiphysis answered 9/9, 2018 at 22:45 Comment(2)
If I go with approach TS complains that onClick does not exist on type intrinsicattributes. Am I missing something?Cychosz
Please add all the relevant code to the question and I'll take a look. (I don't see a use of onClick.)Epiphysis
S
4

You can achieve what you want by destructuring your props.

It seems that you still have to let your component know about its prop types. For that, just pass all props without destructuring them (see background-color down below).

import styled from "styled-components";

interface IProps {
  variant?: 'action' | 'secondary';
}

export const Button = styled.div`
  ${(props: IProps) => `background-color: #fff;`}
  ${({ variant = 'action' }) => variant === 'action' ? `color: blue;` : `color: gray;`}
`;
Squilgee answered 2/4, 2019 at 8:40 Comment(0)
C
2

To my knowledge, this isn't quite possible yet, and unfortunately isn't covered by the defaultProps support added in TS 3.0 (that only applies to normal component classes, and I think functional components). Others feel free to correct me if I'm wrong on this.

There are other ways to write it, though. Here's how I usually go about doing it:

export interface IButton {
  variant?: 'action' | 'secondary';
}

const variantStyles = {
  action: css`
    color: blue;
  `,
  secondary: css`
    color: gray;
  `,
};

export const Button = styled('button')<IButton>`
  background-color: #fff;
  ${props => variantStyles[props.variant || 'action']};
`;
Crotch answered 7/9, 2018 at 16:57 Comment(0)
J
1

As of Dec 2022, the example code in the original question works for me using Styled Components in React.


I found this question when searching for a way to specify a default StyleComponents theme. This is how I did it:

interface ITheme {
  color: string;
}

const defaultTheme: ITheme = {
  color: 'blue';
}

interface IButton {
  theme?: Theme;
}

export const Button = styled('button')<IButton>`
  color: ${({ theme }) => theme.color};
`;

Prose.defaultProps = {
  theme: defaultTheme,
};

Using the above example I can use the button with and without a StyledComponents ThemeProvider.

With ThemeProvider:

<ThemeProvider theme={{ color: 'red' }}>
  <Button>I am a red button</Button>
</ThemeProvider>

Without ThemeProvider:

<Button>I am a blue button</Button>

There is an example of this -- though not in TypeScript -- in the Advanced docs's "Theming" code example: https://styled-components.com/docs/advanced#theming


Also -- unless the example is contrived to use defaultProps -- there is a simpler way to have a default color in the question's example without using defaultProps:

export interface IButton {
  variant: 'action' | 'secondary';
}

export const Button = styled('button')<IButton>`
  background-color: #fff;
  color: ${({ variant }) => (variant === 'secondary') ? 'gray' : 'blue' };
`;
Jada answered 21/12, 2022 at 23:4 Comment(0)
K
0

For most cases I use a wrapper component with default values:

interface IProps {
    // not optional
    variant: 'action' | 'secondary';
}

const SButton = styled.button<IProps>`
    ...styles
`

// make optional
export const Divider = (props: Partial<IProps>) => {
    // set default value
    const { variant = 'action', ...rest } = props;

    // insert default value
    return <SButton variant={variant} {...rest}>{children}</SButton>;
};

Also better to separate styled props and main component props

interface IProps extends Partial<IStyledProps> {
    // component props
}

// or if you have composed component with multiple styled components
interface IProps extends Partial<IStyledProps1>, Partial<IStyledProps2>, Partial<IStyledProps3> {
    // component props
}
Kore answered 24/8, 2022 at 21:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.