Component API Flexibility in React

06 Feb 2019

Pattern libraries, component-based design systems β€” whatever you want to call them β€” it’s no secret that they improve user experience by increasing visual and functional consistency. Most designers will tell you that consistency is a key design principle, so how much flexibility, if any, should be afforded to users who want to customize the components in your design system?

In this post I will discuss an area of component API design where the balance between consistency and flexibility comes into play. The approach you take will likely depend on the level of flexibility you intend to provide.

Child Composition

In React, there are several ways to compose child elements. Some solutions provide the consumer with more flexibility than others. Let’s look at a few options.

Array or object prop

Flexibility: πŸ§˜β€β™‚οΈ

Using an array or object prop to compose child elements is the least flexible option because the component itself controls the type of children that are rendered and their props. This approach is suitable for component libraries that prioritize consistency over flexibility because the component controls all aspects of rendering.

Here the component is saying: Just give me the data β€” I’ll take care of rendering the children.

See the Pen React Child Composition Methods - Array Prop by Peter Hrynkow (@peterhry) on CodePen.

In this example, the consumer provides an array of items and the Tabs component decides what to render. This makes it difficult for the consumer to override the appearance and behavior of the tabs.

Children Prop

Flexibility: πŸ§˜β€β™‚οΈ πŸ§˜β€β™‚οΈ πŸ§˜β€β™‚οΈ

Using the children prop to compose child elements is idiomatic in React. This approach is a bit more flexible because it lets the consumer render the children. However, the result is a tight coupling between the component and its children. For the component to control its children, they must expose the necessary props.

Here the component is saying: Give me the children and I’ll slot them in somewhere. Just make sure they expose the required props so I can set them.

See the Pen React Child Composition Methods - Children Prop by Peter Hrynkow (@peterhry) on CodePen.

In this example, the consumer wants to use FancyTab components instead of Tab components. The only reason this works is because FancyTab exposes the required props selected and onClick.

Render Prop

Flexibility: πŸ§˜β€β™‚οΈ πŸ§˜β€β™‚οΈ πŸ§˜β€β™‚οΈ πŸ§˜β€β™‚οΈ πŸ§˜β€β™‚οΈ

Using a render prop to compose child elements is an advanced pattern that delegates all aspects of rendering to the consumer. Furthermore, it results in a loose coupling between the component and its children, allowing the consumer to render children with an otherwise incompatible API.

Here the component is saying: Render the children when I call this function. I’ll expose an API to help you integrate your super-custom child components.

See the Pen React Child Composition Methods - Render Prop by Peter Hrynkow (@peterhry) on CodePen.

In this example, the consumer wants to use custom HoverTab components, but there’s one problem: The HoverTab component exposes an onMouseEnter prop instead of onClick and an active prop instead of selected. The consumer needs a way to adapt one API to another. This is where render props come in handy.

When the Tabs component is rendering, it invokes the children prop (now a function) and passes it selectedIndex and setSelectedIndex. This allows the consumer to map the HoverTab props to the API provided by the Tabs component.

See also: Inversion of Control

Which approach is best?

It depends whether your component library prioritizes consistency or flexibility. If you want to lock-down the design and behaviour of your components then an array prop might be best. If you want to provide your users with ultimate flexibility, then go for a render prop.

Discuss on Twitter β€’ Edit on GitHub


Keep Reading