Feature Request: Option to pass "as" prop down to wrapped component when using styled(Comp)
See original GitHub issueFirst off, thanks again for your hard work on sc4! I’m really enjoying the new features it introduced, the as
prop in particular. I got really excited when I read about it first, as we had just introduced a Base
component to our UI library that would enable the same functionality for all components that used it. We even chose the same prop name.
But as we know the devil’s in the details, and so I noticed during migration to v4 that there’s an important difference between letting a Base
component handle the as
prop and using s-c’s own prop: The first styled component to receive the as
prop uses it directly, and there’s no way other than using a different prop name to prevent this. This is not a problem when all components wrapped with styled()
are styled components themselves, but when they’re not (and quite a few of our UI elements aren’t), it leads to veeeery unexpected behaviour.
It’s basically the same issue that the ReaKit people are facing: https://github.com/reakit/reakit/issues/226
Now, our codebase is already using the as
prop and we’d strongly prefer not to change it. Not just because it’d be a huge amount of work. It would also be a shame since there should be a consistent API for changing the rendered element/component of both “plain” sc components and our own custom ones. I WANT to use s-c’s as
prop. But due to the issue mentioned above, achieving this is very complicated at the moment.
Consider this innocent piece of code for example:
const CustomNavBarTab = styled(NavBarTab)`
/* ... styles ... */
`;
render(
<CustomNavBarTab as="a" ref={this.refHandler} ... >
);
where NavBarTab is NOT a primitive styled-component. It does take the as
prop (and ref) and forwards it to a nested styled-component. This used to work with the Base
component, but breaks with sc’s ‘as’ prop, since as="a"
effectively changes the above component definition to const CustomNavBarTab = styled('a')
, disregarding all of NavBarTab
’s styles & behaviour.
To work around this fully while trying to keep the outer component API the same, something crazy like the below would have to be written:
const StyledCustomNavBarTab = styled(React.forwardRef(({innerAs, ...props}, ref) =>
<NavBarTab {...props} as={innerAs} ref={ref} />
))`
/* ... styles ... */
`;
const CustomNavBarTab = React.forwardRef(({as, ...otherProps}, ref) => {
return (
<StyledCustomNavBarTab
ref={ref}
innerAs={as}
{...otherProps}
/>
);
});
You’ll probably agree that this really shouldn’t be necessary just to keep refs and the “as” prop working in a consistent way.
As you know from my persistent lobbying for an sc-specific API for filtering props near the component definition, the goal of all of this is to keep styled-components a “transparent” layer. I don’t want to require component users to know whether a component is a primitive styled-component or not.
I WISH I could just write the component somewhat like this, indicating to styled-components that the as
prop should be passed down to the wrapped component:
const CustomLinkTab = styled(NavBarTab, {ignoreAs: true})`
/* ... styles ... */
`;
I don’t care much about the specifics of the API as long as it doesn’t require piling on wrapper components just for passing on or tranforming or filtering props.
Is this something you’d consider adding to styled-components? I imagine it would be fairly easy to implement, but the hard part would be settling on an API for it.
Issue Analytics
- State:
- Created 5 years ago
- Reactions:24
- Comments:27 (10 by maintainers)
Top GitHub Comments
We are seeing same problem with our adoption of ‘as’.
Attempt we had was to use BaseComponent that will handle ‘as’ and use next level components to add
styled
. Expectation was thatas
will be actually used on bottom base component.Here is example of code that does not work:
What works actually are these 2 cases
or this
We were really excited about using native styled-components
as
property. but it looks like what we are trying to build is closer to ‘is’ from styled-system.I’m wondering is we can actually enable passing “as” to bottom component and actually allow to compose styles using it.
Wanted to chime in: this is an issue that I have with styled-components as well. Basically, I want to create a component that should have a certain HTML structure, and its root element should be configurable via a prop, and
as
seems to be the perfect fit.But then it would be impossible to style it with styled-components without losing this feature.
The perfect solution, in my opinion, would be if styled-components would look at the wrapped component’s propTypes, and if it would see that this component handles the
as
prop itself, styled-components would just passas
to this component.What all of you think of a solution like this?
Alternatively, in most cases it would be safe for styled-components to use
as
prop only if it was initialised with an HTML tag for a component, and for any custom components it could assume that that component could handle theas
prop by itself.In my practice you never want to redefine the component via
as
when used withstyled(MyComponent)
, but in most cases you want to do so withstyled.div
etc.