Customizing React components using a components provider

Asís García
Trabe
Published in
3 min readOct 21, 2019
Photo by Eric Prouzet on Unsplash

When we talk about component customization, we usually think of style theming: changing the way a component looks by adjusting its style properties, based on some global values (the theme).

In this story, however, I’ll be talking about a different level of customization. When you develop a UI library, some of the more complex components are created using other basic components (think a ConfirmationDialog using a Button). Sometimes letting the user change the style of the basic components deeply nested inside the more complex one is cumbersome; sometimes is just not enough: what if the user want’s to totally change the way a Button renders inside a ConfirmationDialog?

Providing components

You can use React’s Context functionality to create a provider of components for your library. This provider will act as a registry, where you associate each basic component with its implementation. You can then get those basic components from the registry, and use them to build the rest of the components. That way, you can change the look & feel or the behavior of a basic component, even when it is deeply nested inside other components.

A simple components provider

The following code snippet shows a simple components provider:

You can use the provider in your app like this:

The Page component in the previous code snippet uses a custom useComponents hook to get some components from the provider. In this case there is no ComponentsProvider around the Page component, so it will use the default Button and Icon.

You can add aComponentsProvider to the App and change the provided components (line 23). This in turn will change the components used by Page:

Cherry-pick component overrides

In the previous examples you had to provide all the components every time you choose to use ComponentsProvider. Of course, that is not the best of the APIs. You can use spreading to let the user override just some of the default components:

Now you can override the Icon component and use the default Button:

Meta-provided components

You can provide components that in turn use other provided components to render themselves. The provided Button in the previous examples could be defined as:

We’re creating a cyclic dependency here (components-provider.jsx depends on button.jsx to provide the default Button implementation, which in turn depends on components-provider.jsx to get the Icon implementation), but thanks to the way exports work in ES, the module system has no problem resolving the dependencies at build time.

Using a provider like this, you can create libraries of components that are customizable beyond its styles, letting your users change the way basic components look, and also how they behave.

In this story I’ve shown you a naïve implementation of the provider, to make the core idea easier to grasp. In future stories we’ll see how to create a provider that you could use in real applications. Stay tuned!

--

--