0
votes

Take this simple situation:

I have a container, with an h1 nested inside of it. I want to pass some props into both elements that will change their colour depending on a light / dark theme; eg: white text / black bg || black text / white bg

Problem: it would appear the props (color & text) being passed to the styled components are not cascading in the way that I would assume they would.

Simple example:

export function Heading(props) {
return (
    <Container {...props}> // I would expect the props obj to be available by nested Heading el
      <Heading>
        {props.text}
      </Heading>
    </Container>
  )
}

// styled components 

export const Container = styled.div`
  padding: 20px;
  background-color: ${props => props.color === dark ? "black" : "white";
`;

export const Heading = styled.h1`
  background-color: ${props => props.color === dark ? "white" : "black"; // does not take affect
`;

Instead I have to do:

    <Container {...props}> // Pass to container
      <Heading {...props}> // Pass to heading el
        {props.text}
      </Heading>
    </Container>

Question:

Can I get the Heading to pick up the {...props} passed to Container without declaring it twice?
Or am I misunderstanding how styled components are supposed to behave?

3

3 Answers

1
votes

Your example code is confusing because you have Heading inside of itself. So let's call your function component MyHeading and the styled.h1 Heading.

Props do not cascade from a parent to a child. That's nothing to do with styled-components, that's just the way React works. If you want to pass a prop to a child then you have to do it yourself like you have done in your second snippet.

That said, I don't think that passing down the props is the way to go in this particular situation. The light or dark theme is something that is global in nature, so rather than passing it down from component to component, we want to make the theme available through React contexts. styled-components has some built-in support for this. You can put the entire content of your app inside a ThemeProvider. It makes it so that all styled components in the hierarchy beneath it will get access to a theme prop.

1
votes

so the best way to solve this is using themes. Since your container is a styled-component it has the built-in capability.

So if you add a theme prop to it, it will cascade the theme down to every child that is also a styled-component:

export function Heading(props) {
return (
    <Container theme={{mode: 'light'}}>
      <Heading> // this heading would receive this theme object as a prop.
        {props.text}
      </Heading>
    </Container>
  )
}

export const Heading = styled.h1`
  background-color: ${({theme}) => theme.mode === "dark" ? "white" : "black"};
`;

export const ButtonInsideHeading = styled.h1`
  background-color: ${({theme}) => theme.mode === "light" ? "black" : "white"};
`;

The same goes for that button you mentioned in the comment. Styled-components will inject this theme prop, not matter how many levels deep down they are. So your Button styled-component would inherit theme from the Heading that inherited itfrom Container. It can get a bit verbose, but not necessarily messy.

You can also use css variables as described in this post by Kent C. Dodds. Works pretty well too, depending on your use case.

0
votes

It is not possible, props in styled components should be passed directly to the component.