Creating Scalable UIs: Theming & Configuration
How to Boost Your Productivity When Using React
When it comes to UIs, theming and configuration are both concerns that warrant being separated. This means that their nature should not depend too much on the host framework (e.g React). Ideally, I should be able to hook some existing theme or configuration into multiple projects that use different frameworks. The whole point of separating concerns is to increase scalability and portability (and hence, maintainability), so by treating our theming and configuration as separate concerns, we can do just that.
In modern UIs, you often have libraries of UI components. Themes can be thought of as the configuration for the UI as a whole, whilst your individual UI components can also have their own configuration. In this article, I’ll explore how to make the most out of theming and UI configuration when using React (though a lot of the explored ideas would apply across-the-board, regardless of the technologies you use).
The goal of a theme might be:
- share/reuse certain properties
- provide tools/utilities
- branded microsites
- apply cosmetic updates
Depending on your setup/goals (and whether you are even using themes), you may have just one theme, or you may have multiple themes. You may even have a foundation theme on which subsequent themes are based, preventing the need to duplicate properties that may not change between themes (for example, your theme may contain responsive breakpoint values, but these may be constant between your themes, so duplicating them would be a poor Developer-Experience).
Provide/Expose a Theme
After looking at the potential goals of a theme, it’s worth considering how a theme might be used. Given that our UI is a collection of components, we would perhaps naturally want to expose our theme(s) to our components. Now, at this point, the method of providing and consuming a theme may very well depend upon whatever framework you’re using, but if we were using React, I can see three ways to provide/expose a theme to some components:
- import manually into each component
- attach to
- create a
The first two methods could perhaps apply to any framework, but I’ve typically seen the third method used in other projects that use React and themes, which is to use a Provider component, utilising React’s context under the hood. But really, it doesn’t matter so much how a theme is exposed to your components, so long as it is.
Consume a Theme
Let’s look at an example of how a theme might be provided and consumed by a UI component. Firstly, we need a way to provide/expose it:
This would be the
<Provider> component to which we pass our theme. It would be used like so:
Access Theme Within JSX
To access the theme within your component’s JSX (i.e
MyComponent.jsx), you could now do (if using functional components + hooks):
Your theme is now available to your UI component. Assuming you are using a competent CSS-in-JS solution, you should be able to incorporate the theme into your styles. If we need any theme values to control our component rendering, at this point it would be no different to using props to control the rendering.
My only issue now is that if I want to use my theme in any component, I must manually import the context into it. If my project only had a single theme, this wouldn’t be much better than merely manually importing the theme in each component. We can fix this by creating a Higher-Order-Component or by using render props. Either way, we would need some sort of new wrapper component, and I’m personally a fan of the render props method (probably because I’ve only recently understood what they are).
To get from the previous example to this, we have:
- Swapped the
divwrapper element with a custom
- …allowing us to pass a
- …allowing us to access our theme
- Removed the
The current example could also be re-written as:
…which seems to be basically the same thing, except without using a
render prop (our custom Component element would handle the logic to execute the function either way).
Introducing a custom
Component element simply for this purpose alone would probably be overkill (if you have to import
Component into every UI component to get the theme, why not just import the theme or theme context directly?). However, having such a custom component will allow us to also provide styling and configuration to the component, and potentially other things down the line (essentially any shared logic between UI components). It’s important to note that this hypothetical Component would still render an element to the DOM, so does not add any clutter to your JSX — we are simply replacing whatever the wrapping HTML element would be for your UI component with a more useful element.
Since we acknowledged that UIs are a collection of components, and we’re using React, it actually makes sense to have some sort of custom Component to use instead of divs, at least for the wrapping element of our components. For example, if we were using just divs, we would have no control over the props passed to them, and wouldn’t be able to render anything from them.
Within the context of individual UI components, it’s more likely we will want to use our theme for styling purposes than for rendering purposes. Without there being a standard way to style React components currently, and with existing solutions offering radically different APIs/experiences, it makes it difficult to talk about abstract (but still general) concepts like relating theming to styling. To me, we should be in a state where we have UIs that are built from components, where each component can be fed a styles object (or fed some sort of stylesheet; 1 stylesheet per component). Many productive workflows currently offer this luxury, but given the disruptive nature of React we now have unusual concepts like Styled Components, stirring the pot.
Consume a Theme
theme parameter. Consistency is king, so it’s the same story here:
Without any specific solution in mind, this would be my ideal basis for having a theme in React and exposing it to my component’s styles. Given the JSX we have from previous examples, the natural way to go might be something like:
Using an existing solution such as React-JSS, without using a wrapper
Component, might leave you with something like:
…requiring you to handle certain things manually. When it comes to UI components within your project, without having some sort of HOC there will likely be certain things you end up repeating (use as the
useStyles calls), which is why having a HOC (
<Component> in this case) to use instead of a div to handle all this repetition can be really useful.
So far we’ve looked at how to provide a theme to your UI components and consume it within the styling and rendering realms (with the styling realm being the more useful/common realm). We acknowledged that a theme acts as the UI’s configuration, and that each individual UI component can have its own configuration. The configuration for a UI component should have access to the project’s theme (the UI’s configuration) and should also be exposed to its corresponding styles.
When you create UI components for projects at scale, it’s likely that you will want some of its aspects to be configurable. Configuration could relate to look and feel, functionality, accessibility, performance, literally any concerns that apply to UIs. It’s also unlikely that any component would become so large as to require multiple configurations (e.g a look-and-feel configuration, a functionality configuration etc.) — even at scale, all configuration pertaining to an individual UI component should be able to exist comfortably in a single object.
Keeping in-line with the other pseudo APIs whipped up in this article, to expose our project’s theme to our component’s configuration we will create a function that accepts a
Now we’re in a position to utilise these values within our component’s styles, so we can pass a
config argument alongside our
(Notice how we are passing the arguments to a destructured object — we don’t do this in the configuration because for this guide we only ever need to supply a single theme argument) Using our trusty
<Component> utility, our JSX could now exist as:
Just to take a reality check for a moment, without using the
<Component> HOC, at this point our JSX would resemble:
Access Config Within JSX
Earlier we looked at how to access a theme within your component’s JSX using render props as a potential solution. As our configuration exists as a function, we would need to access its evaluated values, so cannot simply pass the reference we import (in the previous example,
useStyles is a hypothetical hook that evaluates the configuration). When using our
<Component> HOC, we could stick with using a
useStyles hook, or we could use render props. Modifying the previous example to pass
theme would leave us with:
config argument in the render function will now be the evaluated configuration. It’s more likely you would want to access your component’s configuration within its JSX than its theme, as configuration doesn’t just pertain to the look and feel. If any part of your render logic was prone to changing, you could easily have it as part of the component’s configuration.
Again, this is all theoretical pseudo code, but our
<Component> HOC would look something like:
Top-tip: Make React and Component available globally
I realise many people are against “polluting” the global scope with anything, but when it comes to creating UI components in the fashion outlined by this article, constantly importing
Component in every file becomes very tedious, and feels WET. I personally like to try and keep the source code for my components as clean as possible. Adding references that are imported inside every file to the global scope, and refactoring the MyComponent example to utilise React’s defaultProps leaves my source code as:
Removing all the fluff keeps the component’s source code clean and concise, whilst also offering the potential for CSS-in-JS, configuration, and theming possibilities without tightly coupling the existence of a theme (which may or may not exist).
In this article we’ve looked at how to create UI components using React whilst hooking existing themes and configuration into them to facilitate things like styling and rendering, from both theoretical and technical/practical perspectives, and ended up with APIs that follow a good separation of concerns. The most impactful aspect discussed has been the creation of a HOC (i.e
<Component>) to handle rendering elements to the DOM instead of using raw HTML elements like divs. This HOC largely acts as a gatekeeper allowing you to map the pseudo-APIs outlined in this article to a real solution. The pseudo-APIs could probably be adjusted to work with existing solutions (such as React-JSS) without too much trouble.
If you’re interested in following my progress on creating a solution that is compatible with the APIs discussed in this article, checkout the project Lucid:
<Component> used in this article.
Some relevant concepts that are perhaps a bit too in-depth for this article include: