Slots for react component

Slots for react component blog banner image

You have to create a reusable component. Once you built these, it is used at multiple places. Someone approaches you with the new use case. Your component doesn't support this, but it could with slight changes. You may introduce a new prop with default value set to false. Satisfy the new use case and done 🎉. After few days another use case arises and it continues, In no time 🎉 will change to 😭. Your component is full of boolean prop. Its maintenance is overhead for you. Component which is initially intend to solve one use case is now solving too many use cases. Definitely you have to prepare readme explaining each prop.

Inversion Of Control

Inversion of control helps to solve this problem. You may like to read Kent's blog on inversion of control for detailed understanding. Here, I am going to show you one more way of implementation 😉. I learned this from my friend and using this blog to document my learning.

Slotting Pattern

Let's say you want to build reusable Navbar component that has some actions on left side, some on right side. Common approach to build such component is to create NavbarContainer to render common actions visible on each page and render other actions based on prop. This gives you a lot of options to customize the Navbar. But what if there is need to render one more action in Navbar on some page. Would we have to add one more option for that action? What if one of the common actions that we decided earlier is no more common but only specific to some page. Would we have to remove that component from navbar, which is followed by one more prop to show that action?

Instead, lets give all the responsibility of rendering to the user of Navbar(inverting the control!!). Our NavbarContainer will only work as container of all the components in it and provide styles. It will create slots for the component. Here is the implementation of it.

1import React, { Children } from 'react';
2
3const Slot: React.FC<{
4 name: 'leftActions' | 'rightActions';
5}> = () => null;
6
7export const NavbarContainer = ({ children }: { children: Array<React.ReactElement> }) => {
8 const childrenArray = Children.toArray(children) as unknown as React.ReactElement[];
9 const leftActionsSlot = childrenArray.find(child => child?.props?.name === 'leftActions');
10 const rightActionsSlot = childrenArray.find(child => child?.props?.name === 'rightActions');
11
12 return (
13 <div className="flex justify-between items-center">
14 <div className="flex">{leftActionsSlot?.props?.children}</div>
15
16 <div className="flex">{rightActionsSlot?.props?.children}</div>
17 </div>
18 );
19};
20
21NavbarContainer.Slot = Slot;

Its very easy to use this component, and customize as per the requirements. Most importantly none of the use case will required change in this reusable component unless it is design fix at the top level. That you can take in as a className or style prop. User can manipulate the order, spacing and what not. Finally, lets check how one can use this.

1import React from 'react';
2
3export const MainNavbar = () => {
4 return (
5 <NavbarContainer>
6 <NavbarContainer.Slot name="leftActions">
7 <div className="text-subtle">Logo</div>
8 </NavbarContainer.Slot>
9 <NavbarContainer.Slot name="rightActions">
10 <Button>Login</Button>
11 </NavbarContainer.Slot>
12 </NavbarContainer>s
13 );
14};
Logo

We can create all the components required for different actions. And allow user to render it in the way they want.

Conclusion

I hope this is helpful to you. I have documented here one more method in the long list already out there for inverting the control instead of keep adding the if else statement.

Subscribe for the newsletter