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.

Targeting Of Child Component Instances

See original GitHub issue

It feels like styled-components examples and docs are missing support for a common pattern in styling. I’ve read through the docs and lots of related issues without finding an answer. Sorry if I’m missing something obvious. This subject is partially covered in the FAQs, but the discussion stops short of needing to target specific child instances.

Obviously one of the main goals of styled-components is to make these components as reusable as possible. Using Flexbox is really helpful for linear layouts and we can use descendent selectors so that a parent doesn’t need to know or care about what its children are:

> * + * {
  margin-left: ${h-spacing(1.5)};
}

However in many situations, a parent needs to be able to target individual children for layout as they need to be treated in a non-linear way. In such cases, I am aware that I can target classes of child components using interpolation:

> ${Example} { ... }

Although this is far preferable to targeting elements using descendant selectors, it is still quite brittle. If a child component is swapped out for another, the layout breaks. I’ve always followed a pattern where a component should only be responsible for laying out its children - not itself and I’m treating this as axiomatic. I find this makes components much more reusable, focussed, and pushes you into using specialised layout components (which don’t mix lipstick styles and layout styles). Using BEM, this works by classing the child components using a ‘slot’ class, using something like:

.Container-alpha { ... };
.Container-bravo { ... }:
.Container-charlie { ... };

With this pattern these classes are effectively declaring slots in the parent component. It doesn’t matter what the children are as long as they are classed with the appropriate slot as well.

<div class="Container">
  <div class="Container-alpha Delta">...</Container>
  <div class="Container-bravo Echo">...</Container>
  <div class="Container-charlie Foxtrot">...</Container>
</div>

This pattern can be improved upon at the expense of more markup by using a separate layer of elements:

<div class="Container">
  <div class="Container-alpha>
    <div class="Delta">...</div>
  </Container>
  <div class="Container-bravo">
    <div class="Echo">...</div>
  </Container>
  <div class="Container-charlie">
    <div class="Foxtrot">...</div>
  </Container>
</div>

When using styled components, the first variation is not possible. The second variation is possible, and allows children that are not also styled-components to be laid out. Effectively this means declaring a new styled component for each slot - and crucially it doesn’t matter whether or not Delta, Echo and Foxtrot are styled components or not:

<Container>
  <ContainerAlpha>
    <Delta />
  </ContainerAlpha>
  <ContainerBravo>
    <Echo />
  </ContainerBravo>
  <ContainerCharlie>
    <Foxtrot />
  </ContainerCharlie>
</Container>

Using this pattern, each of the Container sub-components act only as a means of the Container targeting them to lay them out - they don’t have any styles of their own:

const ContainerAlpha = styled.div``;
const ContainerBravo = styled.div``;
const ContainerCharlie = styled.div``;

const Container = styled.div`
   > ${ContainerAlpha} { ... }; 
   > ${ContainerBeta} { ... }; 
   > ${Containerharlie} { ... }; 
`;

Given that these children are without styles of their own, they are effectively completely generic. Their entire purpose is to act as a hook for the container. If they are renamed as such, and we are careful to always use direct descendant selectors in our containers to avoid inadvertently selecting grandchildren, we can potentially use these slot components where ever we like:

<ContainerAlpha>
  <SlotOne>
    <Delta />
  </SlotOne>
  <SlotTwo>
    <Echo />
  </SlotTwo>
  <SlotThree>
    <Foxtrot />
  </SlotThree>
</ContainerAlpha>
<ContainerBeta>
  <SlotOne>
    <Delta />
  </SlotOne>
  <SlotTwo>
    <Echo />
  </SlotTwo>
</ContainerBeta>

Whilst this cuts down on the number of styled components, it also makes the relationship between container and slots much less obvious.

So what is the ‘styled-components way’ of achieving the styling of subcomponents in situations where the following are true?

  • A generic descendant selector cannot be used because finer-grained control is needed.
  • The children are not styled components (they are vanilla React components), or are the same styled component but need to be laid out differently from one-another (therefore precluding an interpolation).

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Reactions:7
  • Comments:17 (7 by maintainers)

github_iconTop GitHub Comments

13reactions
mxstbrcommented, Nov 8, 2017

FWIW, we highly discourage targetting specific children from a parent. As you can see in the docs too, we recommend targetting yourself from specific parents, so all the styling for a component stays at the same place.

That being said, if you’re set on sticking to your old ways why not just keep using classes like you did anyway?

<Container>
  <Comp1 className="Container-alpha">
    <Comp2 className="Delta">...</Comp2>
  </Comp1>
  <Comp1 className="Container-bravo">
    <Comp2 className="Echo">...</Comp2>
  </Comp1>
  <Comp1 className="Container-charlie">
    <Comp2 className="Foxtrot">...</Comp2>
  </Comp1>
</Container>

Doesn’t matter wether they’re styled or normal React components, you can now do:

const Container = styled.div`
   > .Container-alpha { ... }; 
   > .Container-bravo { ... }; 
   > .Container-charlie { ... }; 
`;
8reactions
Undistractioncommented, Nov 8, 2017

@mxstbr I’m sorry if I came off as brusque. I’ve only been using styled-components for a few days and this is the only thing that I’m struggling to understand how to approach. I know it might be a matter of changing how I do things and my post was a bi-product of the process of tying to understand the system.

I’ll try and make the case better as I think this is a potentially useful discussion. It feels like there is a distinction between layout properties and aesthetic properties (color, background, font etc) and though I can absolutely see the benefit of using the reverse-selector pattern for these aesthetic properties, when it comes to layout I think this no longer holds true. The special case of layout and the fact it straddles styling and markup, parent and child, has always been a troublesome area for UI frameworks and libraries going back as far as I can remember. In the past I have found using very focused components that are solely responsible for laying out their children to be a good way of solving some of these problems. As long as these layout components don’t need to know exactly what their children are and don’t reach too far, I think this can provide predictability in knowing what is handling layout. It can also potentially allow layouts to be reusable. Finally, it can keep related layout logic in the same place, for example in a flexbox layout, all styles related to layout are found in a single place (the container) rather than being split between parent and child which makes understanding layout much harder.

I think I probably need to spend some more time with styled-components and see if I can come up with some more practical use cases and actionable issues.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Managing Actions of the Child Components in React
When you pass down props from your parent component to the child components, you essentially flow your data down the hierarchy.
Read more >
React - How to target a specific component instance
I have dynamically created functional component instances, how do I alter state on one specific instance? Say the third one, change "rat" to ......
Read more >
Call Methods on Child Components - Salesforce Developers
To communicate down the containment hierarchy, owner and parent components can call JavaScript methods on child components.
Read more >
Styled-Components: Extending Children - DEV Community ‍ ‍
Even when styled-components often export different variations of the component it can still be useful to control the styles from the parent.
Read more >
Solved: Instances of the same component have child ...
Instances of the same component have child components in different positions. I think I've run into this in more than once.
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