Simple Theme Provider for React Native

React Native Theme Provider

Installation

yarn add @pavelgric/react-native-theme-provider

Usage

Create themes

// themes.js

const blueTheme = {
  colors: {
    primary: 'blue'
  }
}

const redTheme = {
  colors: {
    primary: 'red'
  }
}

// you can have as many themes as you want
export const themes = {
  blue: blueTheme,
  red: redTheme,
};

Wrap app with ThemeProvider with created themes

// App.js

import React from 'react';
import { ThemeProvider } from '@pavelgric/react-native-theme-provider';
import { themes } from './themes';

export default App = () => {
  return(
    <ThemeProvider
      themes={themes}
      initialTheme="red"
    >
      <InnerComponent />
    </ThemeProvider>
  )
}

Access theme from any component

// InnerComponent.js

import React from 'react';
import { View } from 'react-native';
import { createStyle, useStyle } from '@pavelgric/react-native-theme-provider';

// create styleCreator, the passed 't' is current theme object
const styleCreator = createStyle((t) => ({
  container: {
    backgroundColor: t.colors.primary
  },
}));

export default InnerComponent = () => {
  // pass the styleCreator
  const styles = useStyle(styleCreator);

  return (
    <View style={styles.container} />
  )
}

Alternatively you can use createUseStyle helper function to remove few lines of code

// InnerComponent.js

import React from 'react';
import { View } from 'react-native';
import { createUseStyle } from '@pavelgric/react-native-theme-provider';

// createUseStyle basically returns (fn) => useStyle(fn)
const useStyle = createUseStyle((t) => ({
  container: {
    backgroundColor: t.colors.primary
  },
}));

export default InnerComponent = () => {
  const styles = useStyle();

  return (
    <View style={styles.container} />
  )
}

Lastly to change or access theme

// SomeComponent.js

import React from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { useThemeDispatch, useTheme } from '@pavelgric/react-native-theme-provider';

export default SomeComponent = () => {
  // selectedTheme is the key of selected theme
  // themes is the whole themes object
  // t is current theme object
  const { selectedTheme, themes, t } = useTheme();
  // to change theme
  const { setTheme } = useThemeDispatch();

  // to access current theme object, or use t
  const themeObj = themes[selectedTheme];

  return(
    <View>
      <Text>{`current theme is ${selectedTheme}`}</Text>
      <TouchableOpacity onPress={() => {
        const nextTheme = selectedTheme === 'red'? 'blue' : 'red'
        setTheme(nextTheme)
      }}>
        <Text>Change theme</Text>
      </TouchableOpacity>
    </View>
  )
}

Typescript usage

The library provides few functions to help passing down the Theme type

Define your themes and use creator functions

// themes.js
import {
  createStyleCreator,
  createUseStyleCreator,
  createUseTheme,
  createUseThemeDispatch,
  useStyle,
} from '@pavelgric/react-native-theme-provider';

const blueTheme = {
  colors: {
    primary: 'blue'
  }
}

const redTheme = {
  colors: {
    primary: 'red'
  }
}

// you can have as many themes as you want
export const themes = {
  blue: blueTheme,
  red: redTheme,
};

export type Themes = typeof themes;
// useStyle does not depend on Theme, this is just to make it also accessible from here
export { useStyle };
export const createStyle = createStyleCreator<Themes>();
export const createUseStyle = createUseStyleCreator<Themes>();
export const useTheme = createUseTheme<Themes>();
export const useThemeDispatch = createUseThemeDispatch<Themes>();

now instead of importing the functions directly from this package

import { createStyle, createUseStyle, useTheme, useThemeDispatch, useStyle } from '@pavelgric/react-native-theme-provider';

you import them from the themes.js file

import { createStyle, createUseStyle, useTheme, useThemeDispatch, useStyle } from './path/to/themes.js';

now these functions are typed

import { createStyle, /* or createUseStyle */ } from './path/to/themes.js'; 
// t is now of type Theme
const styleCreator = createStyle((t) => ({
  container: {
    backgroundColor: t.colors.primary
  },
}));

Recommendations

Use creator function to ensure all theme objects have same shape, otherwise keys that are not present in all theme objects will be excluded by Typescript.

// color pallete
const pallete = {
  red: 'red',
  blue: 'blue'
}

type Color =
  | 'primary'

type Props = {
  colors: Record<Color, string>;
};

export const createTheme = (props: Props) => ({
  colors: props.colors
})

const redThemeColors = {
  primary: pallete.red
};

const blueThemeColors = {
  primary: pallete.blue
};

export const redTheme = createTheme({colors: redThemeColors});
export const blueTheme = createTheme({colors: blueThemeColors});
export const themes = {
  redTheme,
  blueTheme
}

It would be nice to have function that would warn about missing keys, but I didn’t find way how to do that. Ideally something like:

const blueTheme = {
  colors: {
    primary: 'blue',
    secondary: 'yellow'
  }
}

const redTheme = {
  colors: {
    primary: 'red'
  }
}

export const themes = createThemes({
  blueTheme,
  redTheme, // warn red theme is missing key 'secondary'
})

Example

See example for semi-complete solution

to run example, you need to install dependencies on package level, so navigate to this package in terminal and run

yarn && cd example && yarn && cd ios && pod install;

Download Details:

Author: CptFabulouso

Source Code: https://github.com/CptFabulouso/react-native-theme-provider

#react-native #react #mobile-apps

Simple Theme Provider for React Native
9.55 GEEK