The problem

When we initially start a React project we define a fixed set of colors in a colors.ts file. These color constants could then be used throughout the app and changing their values when a design is updated goes pretty easy. But then we need to change.

In the meanwhile, the app kept growing and we started working on a 'mini-app' within the main app, which had to use a whole new and different set of colors. Instead of dynamically changing colors in the components we already built we decided to go for something flexible: the theming solution.

The fix

Thibault found a small library we could use that allows us to create a custom UI theme, much like React-Native's Stylesheet API and hooks: react-native-themed-styles. Since it's so small we decided to copy this code and created a custom useTheme hook instead of using the library itself (reduces dependencies and fixed a typing issue the original repo had). If you're familiar with styled-components this approach will look normal to you, since the way of interpolating dynamic properties in the theme is roughly the same!

First of all we defined our themes that will be used and the default theme:

const mainTheme = {
colors: {
colorPrimary400: 'rgba(4,15,18,1)',
},
};

const miniApp = {
colors: {
colorPrimary400: 'rgba(5,200,199,1)',
},
};

const ThemedStyle = registerThemes({ mainTheme, miniApp }, () => 'mainTheme');

Once defined, in our components we could replace the original StyleSheet styles with our ThemedStyle:

const styles = StyleSheet.create({
container: {
color: Colors.colorPrimary400,
},
});

became:

const styles = ThemedStyle((theme) => ({
container: {
color: theme.color.colorPrimary400,
},
}));

Notice that the color no longer comes from our colors constants file, but is injected from the theme that is used within the screen/navigation. Now in our component itself we initialize the useTheme hook and inject the custom styles.

const [styles, theme] = useTheme(styles, 'miniApp');

We now can use our custom styles and can even access other theme specific properties from the theme object itself. As a second parameter in our hook, we added miniApp which tells the hook to initialize the other theme. If not set our default theme will be used.

Summary

We spent some time researching the best way to:

  • set up multiple themes
  • stay close to RN's StyleSheet API
  • allow for easy usage in our components

Ran into some merge conflicts updating all already styled components to use these new theme colors, but now we now fully functionable and themeable components throughout the app.

What's very interesting in the approach here is that it could work great with a dark/light theme to improve accessibility in your app, and is the least intrusive in an existing codebase/design system/component library.