question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Deprecate `defaultVariants` in favour of `props` and `defaultProps`

See original GitHub issue

After a long discussion internally and with many of our users, we’ve decided to allow users to create default props when creating a Stitches Component.

Reasons:

  • People want the ability to define custom class names
  • People want the ability to set default attributes

As a result, we’ll rename defaultVariants to defaultProps, for the following reasons:

  • Variants are also props, so they can be set via defaultProps
  • Keep the API surface low
  • Allow people to define responsive default variants (this is currently not possible with defaultVariants)

Additionally, there seems to be a lot of confusion around the fact that defaultVariants get inherited by compositions. This breaks most people expectations. There’s an issue for it here https://github.com/modulz/stitches/issues/686.

By renaming from defaultVariants to defaultProps we believe it’ll make it clearer that the props will not be carried over - since each component can have different responsibilities and different type, meaning attributes have to be dependant on its type.

This would also allow people to define an as prop. Would this be problematic in terms of types?

For example:

const Button = styled('button', {})
const Link = styled(Button, {
  defaultProps: {
    as: "a",
    href: '' // <- would we get polymorphism here?
  }
})

About polymorphism, we need to understand how much this will stall the TS engine.

Another solution to keep in mind, is to prevent the as prop in defaultProps, and instead force it to be defined as the type (1st argument)

const Button = styled('button', {})
const Link = styled('a', Button, {
  defaultProps: {
    href: '' // <- would we get polymorphism here?
  }
})

Are there any pros and cons?


UPDATE:

After some thought, I think the right course of action is to restrict the usage of asindefauultProps`.

It creates too many layers of polymorphism, and requires a lot of mental model to understand what’s going on. Additionally, it opens up many doors to complex and confusing architecture.

The idea here is that the as prop is only available on components returned by styled().


UPDATE 2:

After even more thoughts, chats, and calls, we’ve arrived at the following conclusion:

  • Carrying over props (variants, etc) to compositions by default is confusing and leads to unexpected expectations
  • Users want the ability decide which props do get carried over, as it can be a powerful architecture decision when done on purpose
  • Users want the ability to define props within the styled function (this is something supported by all styled()-like APIs, so it makes sense we support it too, for better interoperability and hopefully more users wanting to migrate over - with more ease)

So if we consider this for a moment, I think we could make this work with the following objects:

  • props: your component will be initialised with these props and they will not carry over to compositions
  • defaultProps: your component will be initialised with these props and they will carry over to compositions
const Button = styled('button', {
  variants: {
    outlined: {
      true: {},
    },
    shape: {
      square: {},
      round: {}
    },
    size: {
      small: {}
    }
  },

  props: {
    shape: 'square',
    outlined: true
  },

  defaultProps: {
    size: 'small'
  }
})

() => <Button />
// square, outlined and small
const RoundedButton = styled(Button, {
  ...styles,
  defaultProps: {
    shape: 'round'
  }
})

() => <RoundedButton />
// small and square
const IconButton = styled(RoundedButton, {
  ...styles,
  props: {
    outlined: true
  }
})

() => <IconButton />
// small, square and outlined

So, the actions for this issue are:

  • Deprecate defaultVariants
  • Introduce a props object: the component will be initialised with this, and this will not get carried over to compositions
  • Introduce a defaultProps object: the component will be initialised with this, and this will get carried over to compositions
  • Ensure as key is not allowed in props and defaultProps

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:16
  • Comments:19 (16 by maintainers)

github_iconTop GitHub Comments

12reactions
rafgraphcommented, Jul 30, 2021

Though I think it’s fair to say that there are scenarios where carrying over props do make sense, and others where it doesn’t. As long as it’s opt-in, and not the default behaviour, I think I’m cool with it.

That makes sense to me! E5xjfo0XsAIENzD

9reactions
peduartecommented, Jul 30, 2021

Thanks for that.

It’s a fine line when using examples to back up a feature because we can always make examples that can play in favour or against it. haha

Though I think it’s fair to say that there are scenarios where carrying over props do make sense, and others where it doesn’t. As long as it’s opt-in, and not the default behaviour, I think I’m cool with it.

So if we consider this for a moment, I think we could make this work with the following objects:

  • props: your component will be initialised with these props and they will not carry over to compositions
  • defaultProps: your component will be initialised with these props and they will carry over to compositions
const Button = styled('button', {
  variants: {
    outlined: {
      true: {},
    },
    shape: {
      square: {},
      round: {}
    },
    size: {
      small: {}
    }
  },

  props: {
    shape: 'square',
    outlined: true
  },

  defaultProps: {
    size: 'small'
  }
})

() => <Button />
// square, outlined and small
const RoundedButton = styled(Button, {
  ...styles,
  defaultProps: {
    shape: 'round'
  }
})

() => <RoundedButton />
// small and square
const IconButton = styled(RoundedButton, {
  ...styles,
  props: {
    outlined: true
  }
})

() => <IconButton />
// small, square and outlined

Names can change, if these arent clear. But I’m curious about the approach.

Thoughts?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Deprecate defaultVariants in favour of props and defaultProps ...
After a long discussion internally and with many of our users, we've decided to allow users to create default props when creating a...
Read more >
Stop using defaultProps - Notes and Anecdotes
And because Dan says defaultProps will be deprecated from React, which is the only reason we use them to begin with.
Read more >
reactjs - React - defaultProps vs ES6 default params when ...
Because defaultProps and classes are deprecated in favour of functional components (w/o hooks), this answer isn't as useful as before.
Read more >
Stitches — CSS-in-JS with near-zero runtime
Design composable component APIs with variantsVariants are a first-class citizen of Stitches. With multiple variants, compound variants, and default variants, ...
Read more >
Stop using defaultProps (React 18.3 preview) - YouTube
Become a TypeScript Wizard with Matt's upcoming TypeScript Course:https://www.totaltypescript.com/Follow Matt on ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found