Search code examples
reactjsidioms

Is there an idiomatic way of accepting particular children in React?


Suppose you have a Card component that takes content for the header, body and footer. One way to design it would be for consumers to use it like this:

<Card
  header="foo"
  body="bar"
  footer="baz"
/>

But I often prefer something like this:

<Card>
  <CardHeader>foo</CardHeader>
  <CardBody>bar</CardBody>
  <CardFooter>baz</CardFooter>
</Card>

To get the latter, I'm currently doing something like this:

const cardHeader = children.find(
  (child) => child && child.type && child.type.name === "CardHeader",
);

But I wonder if there is a more idiomatic way of doing it.


Solution

  • For lack of an official "slot" solution, the most idiomatic built-in way to represent this in React is as you suggested:

    <Card
      header={<div>Header ...</div>}
      body={<div>Body ...</div>}
      footer={<div>Footer ...</div>}
    />
    

    The term "slots" is often used for placing particular child nodes into particular component locations, such as with Vue's slot feature or the Web Components <slot> definition in HTML. As of this writing, there is not an official idiomatic equivalent in React, but React RFC #223 ("Slots") proposes it as an extension in the future. That GitHub thread also describes some of the popular alternatives and the concerns in building this feature directly into React.

    You might also consult Sandro Roth's article "Building Component Slots in React", which helpfully linked the RFC above. In particular, it describes the type safety benefits and concerns of the above pattern in comparison to "compound components"—a term which does not appear in official React docs but describes the pattern of nested tightly-coupled components that you depict in your question. This pattern does appear in libraries like react-bootstrap (e.g. hasChildOfType and its consumption) but the pattern is just that—a common design pattern that is not described or endorsed in the React docs.