Engineering

Composition known as Slots in React

Composition known as Slots in React - AppUnite Blog

Introduction

As a react developer you probably experienced countless patterns to create reusable components due to the flexibility of React and probably used Slots without even noticing.

What's the purpose of Slots?

Originally, Slots were invented by Vue Developers. React in its nature is very flexible, so there were no contraindications to adapt this pattern to our codebases. The whole idea of Slots is to create reusable components with shared logic where the whole positioning is directly handled by the one main component that wraps the smaller ones and renders content by passing components/functions(render props)/data via props. That's all! It's really not a complex idea to use in your code and you probably even used it without knowing that!

Slots in practice

For example, we have a component called ItemCard. We are using it in:

  • Cart to list what the user has added to it.
  • Listing to show all available offers.
  • User profile on the bought items subpage.

The difference between them is that the ItemCard in User profile has a footer that shows

bought on: "Date"

and only the cart and listing has a subtitle with the short item description, this case is perfect to show how nice slots are in use.

Let's create the base for that component

const ItemCardHeader = ({children}) => (
  <header>{children}</header>
)

const ItemCardSubtitle = ({children}) => (
  <span>{children}</span>
)

const ItemCardBody = ({children}) => (
  <div>{children}</div>
)

const ItemCardFooter = ({children}) => (
  <footer>{children}</footer>
)

const ItemCard = ({header, subtitle, body, footer}) => (
  <div>
    {header && <ItemCardHeader>{header}</ItemCardHeader>}
    {subtitle && <ItemCardSubtitle>{subtitle}</ItemCardSubtitle>}
    {body && <ItemCardBody>{body}</ItemCardBody>}
    {footer && <ItemCardFooter>{footer}</ItemCardFooter>}
  </div>
)

We created 4 components: Header, Subtitle, Body, Footer, and then composed them in one component ItemCard. Now, add some CSS and we are done!

//Cart
const Cart = () => {
    //Your logic
    return (
        <ItemCard header="Boots" subtitle="Nice looking boots"
         body={
            <CardButtons />
        }
    )
}
//Listing
const Listing = () => {
    //Your logic
    return (
        <ItemCard header="Boots" subtitle="Nice looking boots"
         body={
            <BuyNowButton />    
        }
    )
}
//User bought subpage
const UserBoughtSubpage = () => {
    //Your logic
    return (
        <ItemCard header="Boots" body={
            <ChatWithSeller />
        }
        footer='Bought on "09.08.2021"'
    )
}

Of course, the example is really simplified, but I hope you get what I've tried to show you. Thanks to slots, we could just reuse one component with different content (in more complicated cases even logic) inside, without recreating it from scratch and passing it as children for each component usage. Now, we have complete control over what we want to display to the user. Instead of components, we can pass functions. This pattern is called render props (so that you could theoretically pass some props/arguments from the ItemCard component to the footer contents, by executing a function passed to the slot).

Another good example to show the true power of Slots is a Modal. But hey, try to do it yourself!

When should you use them?

To be honest, it's really easy to overuse them and make your code irritating to work with in the future. I made two rules for them. If my design system has that component in 2/3 different variants and it's reusable across the WHOLE app not only page specific, I will make it as a Component with Slots.

Conclusions

Slots is an awesome pattern to build reusable components in React because it offers you a lot of flexibility and fits perfectly if you have a proper design system in your application. I highly recommend at least trying to use them in your projects!

See you in the next article. AUUUU!