---
title: Bitovi Academy - Using React Context
description: Learn how to share a common theme by creating a React Context.
---

- ![AI implementation](https://www.bitovi.com/hubfs/AIConsultingIcon.svg)
  
  [AI implementation](https://www.bitovi.com/services/ai-consulting?hsLang=en-us)
- ![Systems engineering](https://www.bitovi.com/hubfs/icon%20-%20backend.svg)
  
  [Systems engineering](https://www.bitovi.com/services/systems-engineering-consulting?hsLang=en-us)
- ![Project management](https://www.bitovi.com/hubfs/icon%20-%20PM.svg)
  
  [Project management](https://www.bitovi.com/services/agile-project-management-consulting?hsLang=en-us)
- ![Product design](https://www.bitovi.com/hubfs/icon%20-%20design.svg)
  
  [Product design](https://www.bitovi.com/services/product-design-consulting?hsLang=en-us)
- ![Frontend development](https://www.bitovi.com/hubfs/icon%20-%20frontend.svg)
  
  [Frontend development](https://www.bitovi.com/services/frontend-development-consulting?hsLang=en-us)
- [View more
  
  →
  
  ](https://www.bitovi.com/digital-consulting-services?hsLang=en-us)

We're Experts in...

- [JavaScript](https://www.bitovi.com/services/frontend/javascript-consulting?hsLang=en-us)
- [AI training](https://www.bitovi.com/ai-training-for-software-engineers?hsLang=en-us)
- [Angular](https://www.bitovi.com/services/frontend/angular-consulting?hsLang=en-us)
- [Design Systems](https://www.bitovi.com/services/axure-figma-migration?hsLang=en-us)
- [React](https://www.bitovi.com/services/frontend/react-consulting?hsLang=en-us)
- [Temporal](https://www.bitovi.com/services/backend/temporal-consulting?hsLang=en-us)
- [React Native](https://www.bitovi.com/services/frontend/react-consulting/react-native?hsLang=en-us)
- [Node.js](https://www.bitovi.com/services/backend/nodejs-consulting-1?hsLang=en-us)

Showcase

![Yum! Brands](https://www.bitovi.com/hubfs/yum-showcase-link-1.png)

[View case study](https://www.bitovi.com/en/bitovi-yum-case-study?hsLang=en-us)

More Projects

- [![Levi's](https://www.bitovi.com/hubfs/levis.svg)](https://www.bitovi.com/web-application-consulting-work/levis-ecommerce-responsive-redesign?hsLang=en-us)
- [![Christie's International Real Estate](https://www.bitovi.com/hubfs/christies.svg)](https://design.bitovi.com/christies)
- [![BAFS](https://www.bitovi.com/hubfs/bafs.svg)](https://www.bitovi.com/ux-design-consulting/ux-case-studies/bafs-ppp?hsLang=en-us)
- [View more
  
  →
  
  ](https://www.bitovi.com/our-software-consulting-work?hsLang=en-us)

Open Source Tools

We build powerful tools and open source them to support the community.

[See what we've built →](https://www.bitovi.com/open-source?hsLang=en-us)

- [![Blog](https://www.bitovi.com/hubfs/icon%20-%20blog.svg)
  
  BlogWe post about perfecting software delivery
  
  ](https://bitovi.com/blog)
- [![Partnerships](https://www.bitovi.com/hubfs/Handshake-1.svg)
  
  PartnershipsLearn about Bitovi's technology partners
  
  ](https://www.bitovi.com/partnerships?hsLang=en-us)
- [![Academy](https://www.bitovi.com/hubfs/limbo/icons/icon-academy.svg)
  
  AcademyFree courses to build delivery skills
  
  ](https://www.bitovi.com/academy/?hsLang=en-us)
- [![Open source tools](https://www.bitovi.com/hubfs/icon%20-%20open%20source.svg)
  
  Open source toolsUse our tools or contribute to our open source mission.
  
  ](https://www.bitovi.com/open-source?hsLang=en-us)

Let's Connect

- [![Discord](https://www.bitovi.com/hubfs/DiscordLogo.svg)
  
  Discord
  
  ](https://discord.gg/J7ejFsZnJ4)
- [![LinkedIn](https://www.bitovi.com/hubfs/LinkedinLogo.svg)
  
  LinkedIn
  
  ](https://www.linkedin.com/company/bitovi/)
- [![GitHub](https://www.bitovi.com/hubfs/GithubLogo.svg)
  
  GitHub
  
  ](https://github.com/bitovi/)

![Eggbot](https://www.bitovi.com/hubfs/build_assets/bitovi-limbo-cms-react/338/js_client_assets/assets/eggbot-LTGhdSGL.png)

Name *

Work Email *

Phone

What's your project?

Send

### Contact Us

(312) 620-0386contact@bitovi.com

- [ Advanced JavaScript ](https://www.bitovi.com/academy/learn-advanced-javascript.html?hsLang=en-us)
- [ DOM and jQuery ](https://www.bitovi.com/academy/learn-dom-jquery.html?hsLang=en-us)
- [ Debug JavaScript ](https://www.bitovi.com/academy/learn-to-debug-javascript.html?hsLang=en-us)
- [ Learn TypeScript ](https://www.bitovi.com/academy/learn-typescript.html?hsLang=en-us)
- [ Learn React Native ](https://www.bitovi.com/academy/learn-react-native.html?hsLang=en-us) 
    - [ Introduction to React Native ](https://www.bitovi.com/academy/learn-react-native/intro-to-react-native.html?hsLang=en-us)
    - [ Setting Up Your Environment ](https://www.bitovi.com/academy/learn-react-native/setting-up-your-environment.html?hsLang=en-us)
    - [ Creating a New Application ](https://www.bitovi.com/academy/learn-react-native/creating-a-new-app.html?hsLang=en-us)
    - [ Introduction to JSX ](https://www.bitovi.com/academy/learn-react-native/intro-to-jsx.html?hsLang=en-us)
    - [ Introduction to Testing ](https://www.bitovi.com/academy/learn-react-native/intro-to-testing.html?hsLang=en-us)
    - [ Building Custom Components ](https://www.bitovi.com/academy/learn-react-native/building-custom-components.html?hsLang=en-us)
    - [ Debugging and DevTools ](https://www.bitovi.com/academy/learn-react-native/debugging-devtools.html?hsLang=en-us)
    - [ Styling in React Native ](https://www.bitovi.com/academy/learn-react-native/styling.html?hsLang=en-us)
    - [ Using React Context ](https://www.bitovi.com/academy/learn-react-native/react-context.html?hsLang=en-us)
    - [ Managing State with useState ](https://www.bitovi.com/academy/learn-react-native/managing-state.html?hsLang=en-us)
    - [ Navigation in React Native ](https://www.bitovi.com/academy/learn-react-native/navigation.html?hsLang=en-us)
    - [ Storing State in Navigation Parameters ](https://www.bitovi.com/academy/learn-react-native/navigation-params.html?hsLang=en-us)
    - [ Making HTTP Requests ](https://www.bitovi.com/academy/learn-react-native/making-http-requests.html?hsLang=en-us)
    - [ Handling User Inputs ](https://www.bitovi.com/academy/learn-react-native/user-inputs.html?hsLang=en-us)
    - [ Using Async Storage ](https://www.bitovi.com/academy/learn-react-native/using-asyncstorage.html?hsLang=en-us)
    - [ Security and Authentication ](https://www.bitovi.com/academy/learn-react-native/security-and-auth.html?hsLang=en-us)
    - [ Adding Offline Support ](https://www.bitovi.com/academy/learn-react-native/offline-support.html?hsLang=en-us)
    - [ Integrating Maps ](https://www.bitovi.com/academy/learn-react-native/google-maps.html?hsLang=en-us)
    - [ Performance and Optimization ](https://www.bitovi.com/academy/learn-react-native/performance-optimization.html?hsLang=en-us)
    - [ Building React Native Apps ](https://www.bitovi.com/academy/learn-react-native/building.html?hsLang=en-us)
    - [ Publishing and Updating ](https://www.bitovi.com/academy/learn-react-native/publishing-and-updating.html?hsLang=en-us)
- [ Learn Web Components ](https://www.bitovi.com/academy/learn-web-components.html?hsLang=en-us)
- [ Agile Program Management With Jira ](https://www.bitovi.com/academy/learn-agile-program-management-with-jira.html?hsLang=en-us)
- [ Learn RxJS ](https://www.bitovi.com/academy/learn-rxjs.html?hsLang=en-us)
- [ Giving a Training ](https://www.bitovi.com/academy/training.html?hsLang=en-us)
- [ Learn Technology Consulting ](https://www.bitovi.com/academy/learn-technology-consulting.html?hsLang=en-us)
- [ Learn React ](https://www.bitovi.com/academy/learn-react.html?hsLang=en-us)
- [ Learn NgRx ](https://www.bitovi.com/academy/learn-ngrx.html?hsLang=en-us)
- [ Learn Graphql ](https://www.bitovi.com/academy/learn-graphql.html?hsLang=en-us)
- [ Learn Docker ](https://www.bitovi.com/academy/learn-docker.html?hsLang=en-us)
- [ Learn CanJS ](https://www.bitovi.com/academy/learn-canjs.html?hsLang=en-us)
- [ Learn Angular ](https://www.bitovi.com/academy/learn-angular.html?hsLang=en-us)
- [ Advanced TypeScript ](https://www.bitovi.com/academy/advanced-typescript.html?hsLang=en-us)

[View More Courses](https://www.bitovi.com/academy/#courses) [ ]

# Using React Context `page`

Learn how to share a common theme by creating a React Context.

## Overview

In this section, you will:

- Create a theme with shared values.
- Understand the what, why, and how of React Context.
- Create a design system to unify the application.

## Objective 1: Create a theme with shared values

Now that we understand how to style our components, let’s style everything!

Styling components individually can quickly become a maintenance nightmare. For instance, if we use `#007980` for most highlights but occasionally use `#ca2f35`, what happens if we decide to change `#007980` to `#00a5ad`?

To manage this efficiently, we need a shared theme. A shared theme helps us:

- Keep styles in sync.
- Avoid repetition.
- Use common language to describe colors and other styling parameters.

Before we get too far into our style system though, we need to talk about how we will share it. While we could put it in a module that we import any time we need it, this would make it difficult to make dynamic changes to our styles, like dark mode. Instead, we can use **React Context**.

### What is React Context?

Context is a feature in React that allows you to share data between components without having to explicitly pass props through every level of the component tree. It’s particularly useful for passing down global data (such as themes, user authentication, or language preferences) to components deep in the tree.

### How do you use React Context?

Context consists of three parts:

1. Context: Think of the context as a box of things. The box needs to be available to all who want to use it.
2. Provider: The provider puts things into the box. Whatever data it handles is only available to its children.
3. Consumer: The consumer takes things out of the box. It can only access the providers above it in the component hierarchy.

#### Creating the Context

First we create a definition of what information will be stored, then we get the object that we’ll use to store it. The `createContext` function also accepts a default value, to be provided whenever we access the context without a provider. In this case, we’ve decided to not specify a default value.

```
import {
  createContext,
  useCallback,
  useContext,
  useMemo,
  useState,
} from "react"
import { Text } from "react-native"

interface User {
  username: string
  token: string
}

interface AuthContext {
  signIn: (username: string, password: string) => Promise<void>
  signOut: () => Promise<void>
  user?: User
}

const Context = createContext<AuthContext | undefined>(undefined)

const App: React.FC = () => {
  const [user, setUser] = useState<User>()

  const signIn = useCallback(async (username: string, password: string) => {
    setUser({ username, token: password })
  }, [])

  const signOut = useCallback(async () => {
    setUser(undefined)
  }, [])

  const value = useMemo(
    () => ({ signIn, signOut, user }),
    [signIn, signOut, user],
  )

  return (
    <Context.Provider value={value}>
      <User />
    </Context.Provider>
  )
}

const User: React.FC = () => {
  const context = useContext(Context)

  return (
    <Text>
      {context?.user ? `Welcome ${context.user.username}!` : "Please sign in."}
    </Text>
  )
}

export default App

```

In the code above, we create an `AuthContext` that will have `signIn` and `signOut` methods, as well as a `user` object.

#### Returning the Provider

The Provider is how we actually tell React to store our data. We can render the provider anywhere in our tree, and the data will be accessible anywhere inside. To keep things performant, we will usually want to memoize these values. [Don’t worry if `useMemo` looks odd, we’ll be covering this shortly.]

```
import {
  createContext,
  useCallback,
  useContext,
  useMemo,
  useState,
} from "react"
import { Text } from "react-native"

interface User {
  username: string
  token: string
}

interface AuthContext {
  signIn: (username: string, password: string) => Promise<void>
  signOut: () => Promise<void>
  user?: User
}

const Context = createContext<AuthContext | undefined>(undefined)

const App: React.FC = () => {
  const [user, setUser] = useState<User>()

  const signIn = useCallback(async (username: string, password: string) => {
    setUser({ username, token: password })
  }, [])

  const signOut = useCallback(async () => {
    setUser(undefined)
  }, [])

  const value = useMemo(
    () => ({ signIn, signOut, user }),
    [signIn, signOut, user],
  )

  return (
    <Context.Provider value={value}>
      <User />
    </Context.Provider>
  )
}

const User: React.FC = () => {
  const context = useContext(Context)

  return (
    <Text>
      {context?.user ? `Welcome ${context.user.username}!` : "Please sign in."}
    </Text>
  )
}

export default App

```

#### Consuming the Context

We can `useContext` to access the data from our closest Provider.

```
import {
  createContext,
  useCallback,
  useContext,
  useMemo,
  useState,
} from "react"
import { Text } from "react-native"

interface User {
  username: string
  token: string
}

interface AuthContext {
  signIn: (username: string, password: string) => Promise<void>
  signOut: () => Promise<void>
  user?: User
}

const Context = createContext<AuthContext | undefined>(undefined)

const App: React.FC = () => {
  const [user, setUser] = useState<User>()

  const signIn = useCallback(async (username: string, password: string) => {
    setUser({ username, token: password })
  }, [])

  const signOut = useCallback(async () => {
    setUser(undefined)
  }, [])

  const value = useMemo(
    () => ({ signIn, signOut, user }),
    [signIn, signOut, user],
  )

  return (
    <Context.Provider value={value}>
      <User />
    </Context.Provider>
  )
}

const User: React.FC = () => {
  const context = useContext(Context)

  return (
    <Text>
      {context?.user ? `Welcome ${context.user.username}!` : "Please sign in."}
    </Text>
  )
}

export default App

```

### Future-proofing your React Context

So far, we’ve covered the bare minimum for getting started with React Context. However, this simple approach poses a few problems:

- We can’t store private information in the context.
- Every time the provider is used, the component must be careful to create the memoized (and potentially very complicated) value.
- If the structure of the data changes, you will have to update the code every place you used the provide and every place you used the consumer.

To help keep things clean in the future, it is best practice to keep this React Context object contained within this file, to avoid exporting it, and instead to export custom wrappers around each piece.

#### Future-proofing the Provider

We can create a custom `AuthProvider` that hides all the complicated logic from other components. Sometimes a custom provider will even take props (such as an access token), though this one doesn’t require any.

```
import {
  createContext,
  useCallback,
  useContext,
  useMemo,
  useState,
} from "react"

interface User {
  username: string
  token: string
}

interface AuthContext {
  signIn: (username: string, password: string) => Promise<void>
  signOut: () => Promise<void>
  user?: User
}

const Context = createContext<AuthContext | undefined>(undefined)

const AuthProvider: React.FC<{ children: React.ReactNode }> = ({
  children,
}) => {
  const [user, setUser] = useState<User>()

  const signIn = useCallback(async (username: string, password: string) => {
    setUser({ username, token: password })
  }, [])

  const signOut = useCallback(async () => {
    setUser(undefined)
  }, [])

  const value = useMemo(
    () => ({
      signIn,
      signOut,
      user,
    }),
    [signIn, signOut, user],
  )

  return <Context.Provider value={value}>{children}</Context.Provider>
}

export default AuthProvider

export function useUser() {
  const { user } = useContext(Context)

  return user
}

export function useAuthentication() {
  const { signIn, signOut } = useContext(Context)

  return { signIn, signOut }
}

```

#### Future-proofing the Consumer

Instead of calling `useContext` everywhere and needing to understand the structure of the context, we can provide custom Hooks that return the specific parts of the context that we need. If we change the context structure, all we have to do is update these hooks to return the same data as before and no other code has to change.

```
import {
  createContext,
  useCallback,
  useContext,
  useMemo,
  useState,
} from "react"

interface User {
  username: string
  token: string
}

interface AuthContext {
  signIn: (username: string, password: string) => Promise<void>
  signOut: () => Promise<void>
  user?: User
}

const Context = createContext<AuthContext | undefined>(undefined)

const AuthProvider: React.FC<{ children: React.ReactNode }> = ({
  children,
}) => {
  const [user, setUser] = useState<User>()

  const signIn = useCallback(async (username: string, password: string) => {
    setUser({ username, token: password })
  }, [])

  const signOut = useCallback(async () => {
    setUser(undefined)
  }, [])

  const value = useMemo(
    () => ({
      signIn,
      signOut,
      user,
    }),
    [signIn, signOut, user],
  )

  return <Context.Provider value={value}>{children}</Context.Provider>
}

export default AuthProvider

export function useUser() {
  const { user } = useContext(Context)

  return user
}

export function useAuthentication() {
  const { signIn, signOut } = useContext(Context)

  return { signIn, signOut }
}

```

### Setup 1

✏️ Create **src/shared/design/theme/theme.ts** and update it to be:

```
import { TextStyle } from "react-native"

interface Palette {
  main: string
  soft: string
  strong: string
  contrast: string
}

export interface Theme {
  palette: {
    screen: Palette
    primary: Palette
    secondary: Palette
  }
  spacing: {
    none: 0
    xs: number
    s: number
    m: number
    l: number
    xl: number
  }
  typography: {
    title: TextStyle
    heading: TextStyle
    body: TextStyle
    label: TextStyle
  }
}

export type ThemeMargin =
  | keyof Theme["spacing"]
  | [keyof Theme["spacing"], keyof Theme["spacing"]]
export type ThemePadding =
  | keyof Theme["spacing"]
  | [keyof Theme["spacing"], keyof Theme["spacing"]]

const theme: Theme = {
  palette: {
    screen: {
      main: "#ffffff",
      soft: "#e0e0e0",
      strong: "#ffffff",
      contrast: "#222222",
    },
    primary: {
      main: "#007980",
      soft: "#00a5ad",
      strong: "#006166",
      contrast: "#ffffff",
    },
    secondary: {
      main: "#ca2f35",
      soft: "#d93237",
      strong: "#a3262b",
      contrast: "#ffffff",
    },
  },
  spacing: {
    none: 0,
    xs: 4,
    s: 8,
    m: 16,
    l: 24,
    xl: 40,
  },
  typography: {
    title: {
      fontSize: 21,
      fontWeight: "500",
    },
    heading: {
      fontSize: 24,
      fontWeight: "500",
    },
    body: {
      fontWeight: "normal",
    },
    label: {
      fontSize: 12,
      fontWeight: "bold",
    },
  },
}

export default theme

```

✏️ Create **src/shared/design/theme/ThemeProvider.tsx** and update it to be:

```
import { createContext, useContext, useMemo } from "react"

import theme, { Theme } from "./theme"

interface ThemeContext {
  theme: Theme
}

// Exercise: Update `ThemeProvider` to provide data according to the provided `ThemeContext`.

const ThemeProvider: React.FC = () => {
  // Exercise: Implement the ThemeProvider.
}

export default ThemeProvider

export function useTheme(): Theme {
  // Exercise: Consume the Context.
}

```

✏️ Create **src/shared/design/theme/index.ts** and update it to be:

```
export type * from "./theme"

export { default } from "./ThemeProvider"
export * from "./ThemeProvider"

```

✏️ Update **src/App.tsx** to be:

```
import ThemeProvider from "@shared/design/theme"
import { SafeAreaView } from "react-native"

import StateList from "./screens/StateList"

const App: React.FC = () => {
  return (
    <SafeAreaView style={{ flex: 1 }}>
      <ThemeProvider>
        <StateList />
      </ThemeProvider>
    </SafeAreaView>
  )
}

export default App

```

✏️ Update **src/screens/StateList/components/ListItem/ListItem.tsx** to be:

```
import { Theme, useTheme } from "@shared/design/theme"
import { StyleSheet, Text } from "react-native"

export interface ListItemProps {
  name: string
}

const ListItem: React.FC<ListItemProps> = ({ name }) => {
  // Exercise: Update `ListItem` to use the values from our context.
  const styles = getStyles(theme)

  return <Text style={styles.text}>{name}</Text>
}

function getStyles(theme: Theme) {
  return StyleSheet.create({
    text: {
      fontSize: 21,
      color: "#ffffff",
      backgroundColor: "#007980",
      padding: 16,
      marginVertical: 8,
    },
  })
}

export default ListItem

```

### Verify 1

✏️ Create **src/shared/design/theme/ThemeProvider.test.tsx** and update it to be:

```
import { render, screen } from "@testing-library/react-native"
import { View, Text } from "react-native"

import ThemeProvider, { useTheme } from "./ThemeProvider"

describe("Design/Theme", () => {
  describe("ThemeProvider", () => {
    it("renders children", async () => {
      render(
        <ThemeProvider>
          <Text>Hello!</Text>
        </ThemeProvider>,
      )

      expect(screen.getByText(/Hello/)).toBeOnTheScreen()
    })
  })

  describe("ThemeContext", () => {
    const TestComponent: React.FC = () => {
      const theme = useTheme()

      return (
        <View>
          <Text>{theme.palette.primary.main}</Text>
        </View>
      )
    }

    it("exposes context properties", async () => {
      render(<TestComponent />)

      expect(screen.getByText(/#007980/)).toBeOnTheScreen()
    })
  })
})

```

### Exercise 1

- Update `ThemeProvider` to provide data according to the provided `ThemeContext`.
- Update `ListItem` to use the values from our context.

### Solution 1

If you’ve implemented the solution correctly, the tests will pass when you run `npm run test`!

Click to see the solution

✏️ Update **src/shared/design/theme/ThemeProvider.tsx** to be:

```
import { createContext, useContext, useMemo } from "react"

import theme, { Theme } from "./theme"

interface ThemeContext {
  theme: Theme
}

const Context = createContext<ThemeContext>({ theme })

export interface ThemeProviderProps {
  children: React.ReactNode
}

const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {
  const value = useMemo(() => ({ theme }), [])

  return <Context.Provider value={value}>{children}</Context.Provider>
}

export default ThemeProvider

export function useTheme(): Theme {
  const { theme } = useContext(Context)

  return theme
}

```

✏️ Update **src/screens/StateList/components/ListItem/ListItem.tsx** to be:

```
import { Theme, useTheme } from "@shared/design/theme"
import { StyleSheet, Text } from "react-native"

export interface ListItemProps {
  name: string
}

const ListItem: React.FC<ListItemProps> = ({ name }) => {
  const theme = useTheme()
  const styles = getStyles(theme)

  return <Text style={styles.text}>{name}</Text>
}

function getStyles(theme: Theme) {
  return StyleSheet.create({
    text: {
      fontSize: theme.typography.title.fontSize,
      color: theme.palette.primary.contrast,
      backgroundColor: theme.palette.primary.main,
      padding: theme.spacing.m,
      marginVertical: theme.spacing.s,
    },
  })
}

export default ListItem

```

## Objective 2: Begin creating a design system to unify your application

If we continue down this path, our entire codebase will become littered with `theme.typography.*`, `theme.palette.*`, etc. What if we decide to change the color pattern of one of our components? We'd have to update every place we used that pattern. Instead, we’ll create a design system.

### What is a design system?

A design system is a collection of reusable components, guidelines, and principles that together help ensure consistency, efficiency, and scalability in designing and building digital products. It serves as a single source of truth for design and development teams, providing them with a set of predefined rules and assets to create cohesive user experiences across different platforms and devices.

### Organizing a design system

If you ask three designers and three developers how best to organize a design system, you’ll get 9 different answers…

#### Atomic Design Methodology

The most prolific organization method is Atomic Design. If you want to learn more about design systems, [Atomic Design by Brad Frost](https://atomicdesign.bradfrost.com/chapter-2/) is the perfect place to start.

#### But… let’s keep things simple

Atomic Design is a great system, but it can be overkill for many projects. For this project, we’re going to keep things much more simple: We’ll have the shared theme we created in the previous objective, and we’ll create a small collection of exported components.

### Setup 2

✏️ Create **src/shared/design/Box/Box.tsx** and update it to be:

```
import {
  Theme,
  ThemeMargin,
  ThemePadding,
  useTheme,
} from "@shared/design/theme"
import {
  ViewProps,
  StyleSheet,
  ScrollView,
  View as StaticView,
} from "react-native"

export interface BoxProps extends ViewProps {
  scrollable?: boolean
  margin?: ThemeMargin
  padding?: ThemePadding
}

const Box: React.FC<BoxProps> = ({
  scrollable = false,
  margin,
  padding,
  style,
  children,
  ...props
}) => {
  const theme = useTheme()
  const styles = getStyles(theme, { margin, padding })

  const View = scrollable ? ScrollView : StaticView

  return (
    <View style={StyleSheet.compose(styles.container, style)} {...props}>
      {children}
    </View>
  )
}

export default Box

function getStyles(
  theme: Theme,
  {
    margin,
    padding,
  }: {
    margin?: ThemeMargin
    padding?: ThemePadding
  },
) {
  return StyleSheet.create({
    container: StyleSheet.flatten([
      {
        display: "flex",
      },
      typeof margin === "string" && { margin: theme.spacing[margin] },
      Array.isArray(margin) && {
        marginVertical: theme.spacing[margin[0]],
        marginHorizontal: theme.spacing[margin[1]],
      },
      typeof padding === "string" && { padding: theme.spacing[padding] },
      Array.isArray(padding) && {
        paddingVertical: theme.spacing[padding[0]],
        paddingHorizontal: theme.spacing[padding[1]],
      },
    ]),
  })
}

```

✏️ Create **src/shared/design/Box/index.ts** and update it to be:

```
export { default } from "./Box"
export * from "./Box"

```

✏️ Create **src/shared/design/Typography/Typography.tsx** and update it to be:

```
import { Theme, useTheme } from "@shared/design/theme"
import { TextProps, Text, StyleSheet } from "react-native"

export interface TypographyProps extends TextProps {
  variant?: keyof Theme["typography"]
}

const Typography: React.FC<TypographyProps> = ({
  style,
  variant = "body",
  children,
  ...props
}) => {
  // Exercise: Finish the Typography component using the same patterns used on the 'Box' component.
}

export default Typography

function getStyles(theme: Theme, variant: keyof Theme["typography"]) {
  return StyleSheet.create({
    text: {
      ...theme.typography[variant],
      color: theme.palette.screen.contrast,
    },
  })
}

```

✏️ Create **src/shared/design/Typography/index.ts** and update it to be:

```
export { default } from "./Typography"
export * from "./Typography"

```

✏️ Update **src/screens/StateList/StateList.tsx** to be:

```
import Box from "@shared/design/Box"
import Typography from "@shared/design/Typography"
import { ScrollView, Text, View } from "react-native"

import ListItem from "./components/ListItem"

const states = [
  {
    name: "Illinois",
    short: "IL",
  },
  {
    name: "Wisconsin",
    short: "WI",
  },
]

const StateList: React.FC = () => {
  return (
    <ScrollView>
      <View>
        <Text>Place My Order: Coming Soon!</Text>
      </View>
      <View>
        {states?.length ? (
          states.map((state) => (
            <ListItem key={state.short} name={state.name} />
          ))
        ) : (
          <Text>No states found</Text>
        )}
      </View>
    </ScrollView>
  )
}

export default StateList

```

### Verify 2

✏️ Create **src/shared/design/Box/Box.test.tsx** and update it to be:

```
import Typography from "@shared/design/Typography"
import { render, screen } from "@testing-library/react-native"

import Box from "./Box"

describe("Design/Box", () => {
  it("renders", () => {
    render(
      <Box padding="s" margin="s">
        <Typography>Hello!</Typography>
      </Box>,
    )

    expect(screen.getByText(/Hello/)).toBeOnTheScreen()
  })
})

```

✏️ Create **src/shared/design/Typography/Typography.test.tsx** and update it to be:

```
import { render, screen } from "@testing-library/react-native"

import Typography from "./Typography"

describe("Design/Typography", () => {
  it("renders", () => {
    render(<Typography variant="body">Hello!</Typography>)

    expect(screen.getByText(/Hello/)).toBeOnTheScreen()
  })
})

```

### Exercise 2

We’ve provided a basic `Box` component for you; because of the flexibility of the margin, padding, etc, a component like this can get complicated very quickly.

- Finish the `Typography` component using the same patterns.
- Update `StateList` to use `Box` and `Typography` instead of `View` and `Text`.

### Solution 2

If you’ve implemented the solution correctly, the tests will pass when you run `npm run test`!

Click to see the solution

✏️ Update **src/shared/design/Typography/Typography.tsx** to be:

```
import { Theme, useTheme } from "@shared/design/theme"
import { TextProps, Text, StyleSheet } from "react-native"

export interface TypographyProps extends TextProps {
  variant?: keyof Theme["typography"]
}

const Typography: React.FC<TypographyProps> = ({
  style,
  variant = "body",
  children,
  ...props
}) => {
  const theme = useTheme()
  const styles = getStyles(theme, variant)

  return (
    <Text style={StyleSheet.compose(styles.text, style)} {...props}>
      {children}
    </Text>
  )
}

export default Typography

function getStyles(theme: Theme, variant: keyof Theme["typography"]) {
  return StyleSheet.create({
    text: {
      ...theme.typography[variant],
      color: theme.palette.screen.contrast,
    },
  })
}

```

✏️ Update **src/screens/StateList/StateList.tsx** to be:

```
import Box from "@shared/design/Box"
import Typography from "@shared/design/Typography"
import { ScrollView } from "react-native"

import ListItem from "./components/ListItem"

const states = [
  {
    name: "Illinois",
    short: "IL",
  },
  {
    name: "Wisconsin",
    short: "WI",
  },
]

const StateList: React.FC = () => {
  return (
    <ScrollView>
      <Box padding="s">
        <Typography variant="heading">Place My Order: Coming Soon!</Typography>
      </Box>
      <Box>
        {states?.length ? (
          states.map((state) => (
            <ListItem key={state.short} name={state.name} />
          ))
        ) : (
          <Typography>No states found</Typography>
        )}
      </Box>
    </ScrollView>
  )
}

export default StateList

```

## Objective 3: Expand your design system

Now that we have the basics of a design system, we can expand it however we need. There are some basic components that most design systems will need:

1. Page/Screen/etc. A main view of the app, which will often have a prop like `title`.
2. Form components like TextInput, Select, Button. Almost every app will need forms of some kind.
3. Typography. The basic text component, to make it easier to standardize text size and color.
4. Grid. Most apps will need to control placement of elements on the screen.
5. Card. While this is a specific design pattern, it has become very ubiquitous lately.

Beyond these, you’ll find all kinds of UI components like Tabs, Modal, Icon, Navigation, Badge, Progress, etc.

### Setup 3

✏️ Create **src/shared/design/Button/Button.tsx** and update it to be:

```
import { Theme, useTheme } from "@shared/design/theme"
import {
  PressableProps,
  TextStyle,
  StyleSheet,
  Pressable,
  Text,
} from "react-native"

type Variant = "primary" | "secondary" | "outline"

export interface ButtonProps extends PressableProps {
  variant?: Variant
  margin?: keyof Theme["spacing"]
  padding?: keyof Theme["spacing"]
  fontSize?: TextStyle["fontSize"]
  fontWeight?: TextStyle["fontWeight"]
  disabled?: boolean
  children: string
}

const Button: React.FC<ButtonProps> = ({
  variant = "primary",
  margin,
  padding,
  fontSize = 20,
  fontWeight = "400",
  disabled,
  children,
  ...props
}) => {
  const theme = useTheme()
  const styles = getStyles(theme, variant)

  return (
    <Pressable
      style={StyleSheet.compose(styles.pressable, {
        ...(margin ? { margin: theme.spacing[margin] } : {}),
        ...(padding ? { padding: theme.spacing[padding] } : {}),
        opacity: disabled ? 0.5 : 1,
      })}
      disabled={disabled}
      {...props}
    >
      <Text
        style={StyleSheet.compose(styles.text, {
          fontSize,
          fontWeight,
        })}
      >
        {children}
      </Text>
    </Pressable>
  )
}

export default Button

function getStyles(theme: Theme, variant: Variant) {
  if (variant === "primary") {
    return StyleSheet.create({
      pressable: {
        margin: theme.spacing.s,
        padding: theme.spacing.m,
        borderRadius: 5,
        backgroundColor: theme.palette.primary.main,
      },
      text: {
        fontSize: 21,
        color: theme.palette.primary.contrast,
      },
    })
  }

  if (variant === "secondary") {
    return StyleSheet.create({
      pressable: {
        margin: theme.spacing.s,
        padding: theme.spacing.m,
        borderRadius: 5,
        backgroundColor: theme.palette.secondary.main,
      },
      text: {
        fontSize: 21,
        color: theme.palette.secondary.contrast,
      },
    })
  }

  if (variant === "outline") {
    return StyleSheet.create({
      pressable: {
        margin: theme.spacing.s,
        padding: theme.spacing.m - 1,
        borderRadius: 5,
        borderWidth: 1,
        backgroundColor: theme.palette.screen.main,
        borderColor: theme.palette.screen.contrast,
      },
      text: {
        fontSize: 21,
        color: theme.palette.screen.contrast,
      },
    })
  }

  throw new Error(`Button: Unknown variant: ${variant}`)
}

```

✏️ Create **src/shared/design/Button/Button.test.tsx** and update it to be:

```
import { fireEvent, render, screen } from "@testing-library/react-native"

import Button from "./Button"

describe("Design/Button", () => {
  it("renders", () => {
    const handleChangeMock = jest.fn()

    render(<Button onPress={handleChangeMock}>Hello!</Button>)

    expect(screen.getByText(/Hello/)).toBeOnTheScreen()
    fireEvent.press(screen.getByText(/Hello/i))
    expect(handleChangeMock).toHaveBeenCalled()
  })
})

```

✏️ Create **src/shared/design/Button/index.ts** and update it to be:

```
export { default } from "./Button"
export * from "./Button"

```

✏️ Create **src/shared/design/Card/Card.tsx** and update it to be:

```
import Box, { BoxProps } from "@shared/design/Box"
import { Theme, useTheme } from "@shared/design/theme"
import Typography from "@shared/design/Typography"
import { StyleSheet } from "react-native"

export interface CardProps extends BoxProps {
  title?: string
}

const Card: React.FC<CardProps> = ({ title, children, ...props }) => {
  const theme = useTheme()
  const styles = getStyles(theme)

  return (
    <Box margin={["m", "none"]} style={styles.container} {...props}>
      {title && (
        <Box padding="m" style={styles.title}>
          <Typography variant="title">{title}</Typography>
        </Box>
      )}

      <Box padding="m">{children}</Box>
    </Box>
  )
}

export default Card

function getStyles(theme: Theme) {
  return StyleSheet.create({
    container: {
      width: "100%",
      shadowOffset: { width: 0, height: 2 },
      shadowOpacity: 0.25,

      backgroundColor: theme.palette.screen.main,
      shadowColor: theme.palette.screen.contrast,
    },
    title: {
      borderBottomWidth: 1,
      borderBottomColor: theme.palette.screen.contrast,
    },
  })
}

```

✏️ Create **src/shared/design/Card/Card.test.tsx** and update it to be:

```
import Typography from "@shared/design/Typography"
import { render, screen } from "@testing-library/react-native"

import Card from "./Card"

describe("Design/Card", () => {
  it("renders", () => {
    render(
      <Card title="Hello!">
        <Typography variant="body">How are you?</Typography>
      </Card>,
    )

    expect(screen.getByText(/Hello/)).toBeOnTheScreen()
    expect(screen.getByText(/How are you?/)).toBeOnTheScreen()
  })
})

```

✏️ Create **src/shared/design/Card/index.ts** and update it to be:

```
export { default } from "./Card"
export * from "./Card"

```

✏️ Create **src/shared/design/Screen/Screen.tsx** and update it to be:

```
import Box, { BoxProps } from "@shared/design/Box"
import { useTheme } from "@shared/design/theme"

export interface ScreenProps extends BoxProps {
  noScroll?: boolean
}

const Screen: React.FC<ScreenProps> = ({
  noScroll = false,
  style,
  children,
  ...props
}) => {
  const theme = useTheme()

  return (
    <Box
      scrollable={!noScroll}
      style={{
        flex: 1,
        backgroundColor: theme.palette.screen.soft,
      }}
      {...props}
    >
      {children}
    </Box>
  )
}

export default Screen

```

✏️ Create **src/shared/design/Screen/Screen.test.tsx** and update it to be:

```
import Typography from "@shared/design/Typography"
import { render, screen } from "@testing-library/react-native"

import Screen from "./Screen"

describe("Design/Screen", () => {
  it("renders", () => {
    render(
      <Screen>
        <Typography variant="body">How are you?</Typography>
      </Screen>,
    )

    expect(screen.getByText(/How are you?/)).toBeOnTheScreen()
  })
})

```

✏️ Create **src/shared/design/Screen/index.ts** and update it to be:

```
export { default } from "./Screen"
export * from "./Screen"

```

✏️ Update **src/screens/StateList/StateList.tsx** to be:

```
import Box from "@shared/design/Box"
import Card from "@shared/design/Card"
import Screen from "@shared/design/Screen"
import Typography from "@shared/design/Typography"
import { ScrollView } from "react-native"

import ListItem from "./components/ListItem"

const states = [
  {
    name: "Illinois",
    short: "IL",
  },
  {
    name: "Wisconsin",
    short: "WI",
  },
]

const StateList: React.FC = () => {
  return (
    <ScrollView>
      <Box padding="s">
        <Typography variant="heading">Place My Order: Coming Soon!</Typography>
      </Box>
      {states?.length ? (
        states.map((state) => <ListItem key={state.short} name={state.name} />)
      ) : (
        <Typography>No states found</Typography>
      )}
    </ScrollView>
  )
}

export default StateList

```

### Exercise 3

We’ve provided three new design components for you: `Button`, `Card`, and `Screen`.

- Update `StateList` to use our new `Screen` and `Card` components. (We’ll use `Button` later.)

### Solution 3

If you’ve implemented the solution correctly, the tests will pass when you run `npm run test`!

Click to see the solution

✏️ Update **src/screens/StateList/StateList.tsx** to be:

```
import Card from "@shared/design/Card"
import Screen from "@shared/design/Screen"
import Typography from "@shared/design/Typography"

import ListItem from "./components/ListItem"

const states = [
  {
    name: "Illinois",
    short: "IL",
  },
  {
    name: "Wisconsin",
    short: "WI",
  },
]

const StateList: React.FC = () => {
  return (
    <Screen>
      <Card>
        <Typography variant="heading">Place My Order: Coming Soon!</Typography>
      </Card>
      {states?.length ? (
        states.map((state) => <ListItem key={state.short} name={state.name} />)
      ) : (
        <Typography>No states found</Typography>
      )}
    </Screen>
  )
}

export default StateList

```

## Next steps

Next, let’s learn about how we can add dark mode with [useState](https://www.bitovi.com/academy/learn-react-native/managing-state.html?hsLang=en-us).

###### On this page...

###### Get help

If you need [help](https://discord.gg/J7ejFsZnJ4) with an exercise, please reach out to us on [Discord](https://discord.gg/J7ejFsZnJ4)

If you find a bug, please [create an issue](https://github.com/bitovi/academy/issues/new) or email [contact@bitovi.com](mailto:contact@bitovi.com?Subject=Training%20Suggestion)

## Join the Bitovi Newsletter

 Do you want to receive updates about new Bitovi Academy courses, Bitovi open source projects, and dev community news? If so, please provide your email. We won’t spam you or give it away for any reason!  
*By providing your email you agree to receive communications from Bitovi*

![](https://bitovi.github.io/academy/static/img/close.svg)

```json
{
     "@context": "http://schema.org",
     "@type": "Organization",
     "mainEntityOfPage":{
          "@type":"WebPage",
          "@id":"https://www.bitovi.com/academy/learn-react-native/react-context.html",
          "description": "Learn how to share a common theme by creating a React Context. 
"
     },
     "url": "http://bitovi.com",
     "logo": "https://www.bitovi.com/hubfs/bitovi-logo-23-1.svg",
     "image": "https://www.bitovi.com/hubfs/bitovi-logo-x2.png",
     "name": "Bitovi Web App Consulting",
     "alternateName": "Bitovi",
      "naics": "541511",
      "description": "Bitovi is a UX, UI design and front-end JavaScript development consulting company",
     "address": {
          "@type": "PostalAddress",
          "streetAddress": "1134 Pine Tree Lane ",
          "addressLocality": "Libertyville",
          "addressRegion": "IL",
          "addressCountry": "United States",
          "postalCode": "60048"
     },
     "areaServed": {
          "@type": "GeoCircle",
          "geoMidpoint": {
               "@type": "GeoCoordinates",
               "latitude": "41.8781",
               "longitude": "87.6298"
          },
          "geoRadius": "5000 km"
     },
     "telephone": "312-620-0386",
     "email": "contact@bitovi.com",
     "sameAs": [
          "https://www.facebook.com/BitoviLLC/",
          "https://twitter.com/bitovi",
          "https://www.linkedin.com/company/bitovi"
     ]
}
```