Deprecate `defaultVariants` in favour of `props` and `defaultProps`
See original GitHub issueAfter 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
asin
defauultProps`.
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 allstyled()
-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 compositionsdefaultProps
: 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 inprops
anddefaultProps
Issue Analytics
- State:
- Created 2 years ago
- Reactions:16
- Comments:19 (16 by maintainers)
That makes sense to me!
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 compositionsdefaultProps
: your component will be initialised with these props and they will carry over to compositionsNames can change, if these arent clear. But I’m curious about the approach.
Thoughts?