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.

Feature Request: Option to pass "as" prop down to wrapped component when using styled(Comp)

See original GitHub issue

First 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:closed
  • Created 5 years ago
  • Reactions:24
  • Comments:27 (10 by maintainers)

github_iconTop GitHub Comments

5reactions
dlebedynskyicommented, Nov 5, 2018

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 that as will be actually used on bottom base component.

Here is example of code that does not work:

   <LabelText as="span" bold>
      Status:&nbsp;
    </LabelText>
    <StatusText as={tag.span} id="dashboard-card-status" bold topStatus={topStatus}>
      {topStatus}
    </StatusText>

What works actually are these 2 cases

const Magictext = React.forwardRef((props, ref) => (
  <Text {...props} as={tag.span} ref={ref} />
));

const StatusText = styled(Magictext)`
  ${({ theme, topStatus }) => {
 // rest of styling
}

export const StatusWrapper = ({ topStatus }: Props) => (
  <Wrapper middle>
    <StatusText id="dashboard-card-status" bold topStatus={topStatus}>
      {topStatus}
    </StatusText>
  </Wrapper>
);

or this

const LabelText = styled(Text)`
  color: ${({ theme }) => theme.colors.grayscale.base};
`;

export const StatusWrapper = ({ topStatus }: Props) => (
  <Wrapper middle>
    <Text as={LabelText} bold>
      Status:&nbsp;
    </Text>
 </Wraper>
);

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.

4reactions
kizucommented, Mar 25, 2019

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 pass as 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 the as prop by itself.

In my practice you never want to redefine the component via as when used with styled(MyComponent), but in most cases you want to do so with styled.div etc.

Read more comments on GitHub >

github_iconTop Results From Across the Web

API Reference - styled-components
API Reference of styled-components. ... This is an ES6 language feature. ... prop, use "forwardedAs" to pass along the desired prop to the...
Read more >
styled-components is saying wrapped styled() around your ...
It looks like you've wrapped styled() around your React component (Component), but the className prop is not being passed down to a child....
Read more >
How to Memoize Components in React | by Ross Bulat - Medium
It is common practice to refer to a component wrapped in a HOC as WrappedComponent . The memoized component is defined separately and...
Read more >
styled-components | Yarn - Package Manager
styled -components. Visual primitives for the component age. Use the best bits of ES6 and CSS to style your apps without stress
Read more >
How To Use Styled-Components In React - Smashing Magazine
disableVendorPrefixes is passed as a prop to <StyleSheetManager> . So, the styled components wrapped by <StyleSheetManager> would be disabled, ...
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