Use the React Context API to change the theme in an app! But first, some ! 🤣 context Ok terrible puns aside, let’s have a look at what the React Context API is for and what it does. There’s a great one-liner from the … React docs Context provides a way to pass data through the component tree without having to pass props down manually at every level. Or in other words, you can use the React Context API to avoid . If you need more detail on the concept then please do check out the links provided. prop drilling I’ve previously gone over implementing the React Context API in which I documented as I did it. You can see . my Gatsby blog how that went here A great resource on explaining the API can be found from @leighchalliday with a great use case on the subject. What we’re doing… For this post we’re going to extend the example we created for . As it has the majority of the code we’ll need to get started with the React Context API. styled-components getting started We’re going to extend that example to manage the theme state of the example application. So in summary: Scaffold out basic CreateReact App Use styled-components 💅 for styling Add themes to switch between with the React Context API Use the React Context API! What we’ll need… All we’ll be needing is an internet connection and a modern web browser! Because we’re going to do all of this online in the awesome ! CodeSandbox If you have a GitHub account or not, CodeSandbox will let you get started . coding straight away Versions: This guide is being used with the following dependency versions. react: 16.4.2 react-dom: 16.4.2 react-scripts: 1.1.4 styled-components: 3.4.5 Let’s start So let’s go over theming the basic create-react-app again. This time, instead of adding state into to the component, we will use the React Context API to manage the state for us. There will be people that will argue that this is a bit overkill for a theme switch. It is given as an example of in the React documentation, so I will let you decide on the validity of that point. when to use the Context API For this example, I hope it will give you a clearer picture of how to use the Context API in an application. Dependencies and add as a dependency: Open a React CodeSandbox styled-components File structure Another area for is file structure. In this scenario we’re adding folders for , and the . Please feel free to structure your files how you see fit, this is how we’re going to do it for this example ❤️ bikeshedding components contexts theme Add the directories into the folder so we can add in some components. The file structure should look something like this: src context-demo/├─ public/├─ src/│ └─ components/│ └─ contexts/│ └─ theme/└─ package.json Scaffold out a basic Create React App Ok, so, what we’re going to do is add in an component to the folder then use that in the file. App.js components src/index.js The component can be a as for this example as we’re going to be handling state with the Context API. App.js stateless functional component Here you can see my sketchy typing as I create the directories and add in the component. App.js We can then remove the file and reference in We’re going to be styling with styled-components 💅 and then use our component: style.css src/index.js. App.js Ok, so the reason why I have abstracted the component out of the file is so that when we come to using the Context API we can add it to the highest level in our app, which is . App.js src/index.js src/index.js What about the rest? So this isn’t really the Create React App, as we’re using CodeSandbox instead. I have gone over the basic styling used in the post. It’s time to refer to that to mimic the styles we need. styled-components getting started That means that, rather than going into depth on the styling of each of the component parts that make up the basic Create React App appearance, we’re going to re-use components. There’s going to be a bit of copy pasting involved now. The Create React App boilerplate code has one file that we go over styling in the post which is the file. The basic style of is: styled-components getting started App.js App.js **App.css** .App {text-align: center;} .App-logo {animation: App-logo-spin infinite 20s linear;height: 80px;} .App-header {background-color: #222;height: 150px;padding: 20px;color: white;} .App-title {font-size: 1.5em;} .App-intro {font-size: large;} @keyframes App-logo-spin {from {transform: rotate(0deg);}to {transform: rotate(360deg);}} Use styled components for styling Now we’re going to recreate the styles from the file with styled-components. Let’s list them out here and go through them: App.css AppWrapperAppHeaderAppTitlerotate360AppLogo _We’re adding our own styles for_AppIntro UnderlineStyledHyperLinkButton is the top level wrapper which in a larger component could be used for layout with CSS Grid or Flexbox. In our case we’re going to align the text center. AppWrapper Straightforward enough, right? Now the majority of the rest of the components will use the styled-components which is what we’re going to pass our theme to from the Context API. [ThemeProvider](https://www.styled-components.com/docs/advanced#theming) Add themes to switch between with the React Context API Ok, we need to define some themes to pass to the . We’re going to define several theme aspects we want to change, and these are going to be: ThemeProvider primary // _colour_secondary // _colour_danger // _colour_fontHeader // _font_fontBody // font Create a file to contain the theme object in the directory and call it . Add in the following: theme globalStyle.js { injectGlobal } 'styled-components' import from export const = { : { : '#ff0198', : '#01c1d6', : '#e50000', : 'Old Standard TT, sans, sans-serif', : 'Nunito, sans-serif'}, : { : '#6e27c5', : '#ffb617', : '#ff1919', : 'Enriqueta, sans-serif', : 'Exo 2, sans, sans-serif'}, : { : '#f16623', : '#2e2e86', : '#cc0000', : 'Kaushan Script, sans, sans-serif', : 'Headland One, sans-serif'}} themes theme1 primary secondary danger fontHeader fontBody theme2 primary secondary danger fontHeader fontBody theme3 primary secondary danger fontHeader fontBody injectGlobal`@import url('https://fonts.googleapis.com/css?family=Old+Standard+TT:400,700|Nunito:400,700'|Enriqueta:400,700|Exo+2:400,700|Kaushan+Script:400,700|Headland+One:400,700|'); body {padding: 0;margin: 0;}` Ok, so nothing really happening there apart from setting up the styles for use later. You will notice that is being used here. This is where we’re setting the fonts for use throughout the app. in an app to set global styles like this. injectGlobal ingectGlobal should be used once Onwards! Let us now focus on getting the basic app styles into the component. We can now start using the in . To do this, for now, to get some visual feedback we’re going to apply one of the themes from the object in . This is so, as we are adding in components we can see the theme being applied. App.js ThemeProvider App.js themes globalStyle.js We can do this now with the which is a styled div: AppHeader const = styled_.div_` ${({ }) => theme_.dark_} ${({ }) => theme_.primary_}_;_` AppHeader height: 12rem;padding: 1rem;color: theme ;background-color: theme You will notice here that we’re beginning to use the styled-components, props. If we paste this code in now there won’t be any change until the is passed the object. We’re going to wrap with the component so that any component encapsulated by the is able to receive props. theme ThemeProvider theme App.js ThemeProvider ThemeProvider theme is going to be a h1 so: AppTitle const = styled_.h1_` ${({ }) => theme_.fontHeader_}_;_` AppTitle font-family: theme For the spinning React logo we can use the asset used previously in the . We can add it in with the imports at the top of the component and add it into the styled component as an tag: styled-components getting started example App.js AppLogo img const = 'https://user-images.githubusercontent.com/234708/37256552-32635a02-2554-11e8-8fe3-8ab5bd969d8e.png' logo The helper will need to be imported alongside the for the animation on the React logo. keyframes ThemeProvider const = keyframes`_from {transform: rotate(0deg);}to {transform: rotate(360deg);}_` rotate360 const = styled_.img_` ${rotate360} ${rotate360} ` AppLogo animation: infinite 5s linear;height: 80px;&:hover {animation: infinite 1s linear;} Shared components Shared components are covered in the guide. If you need more information, for this example we’re going to bring in the final couple of components as shared ones for the and . In add the following: styled-components getting started StyledHyperlink Button src/Shared.js **src/Shared.js** styled, { css } ‘styled-components’export const = styled_.button_` ${({ }) => theme_.primary_} ${ => props_.border_} ${ =>props_.primary_ &&css` ${({ }) => theme_.primary_} ${({ }) => theme_.primary_} `} ${ =>props_.danger_ &&css` ${({ }) => theme_.danger_} ${({ }) => theme_.danger_} `}_;&:hover {transform: translateY(2px);box-shadow: 0 2px 3px rgba(0, 0, 0, 0.15);}_` import from Button padding: 0.5rem 1rem;margin: 0.5rem 1rem;color: theme ;font-size: 1rem;box-shadow: 0 3px 5px rgba(0, 0, 0, 0.1);cursor: pointer;border: 2px solid props ;background-color: Transparent;text-transform: uppercase;border-radius: 4px;transition: all 0.1s;&:hover {transform: translateY(1px);box-shadow: 0 2px 3px rgba(0, 0, 0, 0.15);} props background: theme ;border: 2px solid theme ;color: white; ; props background: theme ;border: 2px solid theme ;color: white; export const = styled_.a_` ${({ }) => theme_.primary_} ${({ }) => theme_.secondary_} ${({ }) => theme_.primary_}_;_` StyledHyperLink cursor: pointer;&:visited,&:active {color: theme ;}&:hover {color: theme ;}color: theme Then import the components like any other: The last three components for now, , and : AppIntro Underline StyledHyperLink const = styled_.p_` ${({ }) => theme_.dark_} ${({ }) => theme_.fontBody_}_;_` AppIntro color: theme ;font-size: large;code {font-size: 1.3rem;}font-family: theme const = styled_.span_` ${({ }) => theme_.secondary_}_;_` Underline border-bottom: 4px solid theme const = ` ${({ }) => theme_.fontBody_} ${({ }) => theme_.fontDark_}_;_` StyledHyperLink SHL.extend text-decoration: none;font-family: theme ;color: theme Add them in under the styled component. Then we can add the rest of the components into the function , so, ready for another copy paste? Here: AppLogo App return <AppIntro>Bootstrapped with{' '}<Underline><code><StyledHyperLink ={`https://github.com/facebook/create-react-app`} ="_blank" ="noopener">create-react-app</StyledHyperLink></code></Underline>.</AppIntro><AppIntro>Components styled with{' '}<Underline><code><StyledHyperLink ={`https://www.styled-components.com`} ="_blank" ="noopener">styled-components</StyledHyperLink></code></Underline>{' '}<span ="img" ="nail polish">💅</span></AppIntro><AppIntro>Fonts picked with{' '}<Underline><code><StyledHyperLink ={`https://fontjoy.com/`} ="_blank" ="noopener">fontjoy.com</StyledHyperLink></code></Underline></AppIntro><Button>Normal Button</Button><Button >Primary Button</Button><Button >Danger Button</Button> href target rel href target rel role aria-label href target rel primary danger Sorry for the code wall! Right paste that in under the closing tag and we should have the base of what we’re going to theme! </AppHeader> Ok? How’s it looking? Now we have a basic React app that uses styled-components! Use the React Context API Now for the main event! Here we’re going to cover: Making the theme context. Using the context API with a component. Consuming the Context API in multiple components. So, passing state needlessly through components is what we can use the Context API to avoid. If we take a look at the we can see the state being managed in the component. styled-components getting started example App.js The function has to be passed to the component much the same way as any props would need to be passed down. That is a simplified example. It’s quite easy to imagine that if that component lived on a footer component or a menu item, there would be several other components that would need to have the state passed through them that would not actually need that state or props. Make sense? handleThemeChange ThemeSelect example <App> {/* */}<Header> {/* */}<Navigation> {/* */}<ThemeSelect> {/* */}</Navigation></Header><Footer/></App> state begins here through here and here to be used here Add the site theme context In our directory we’re going to make our import React and define and export our context: src/contexts/ SiteThemeContext.js React 'react' import from export const = React_._createContext() SiteThemeContext So what is a context? A context is made up of two things, a provider and a consumer. You have a single provider which will sit up as high as possible in the component tree so that multiple consumers can get the state and props from the provider. Hopefully you recall the point at which we abstracted the component out of the file. This is so we could add in the context provider at the highest level of the app, in the file. This means that any consumer within the app, no matter how deep into the component tree it is, it can get the state and props from that top level. function App src/index.js src/index.js Now to create a provider, the provider is a regular React component, so: React 'react' import from export const = React_._createContext() SiteThemeContext export class SiteThemeProvider React_._Component { extends render() { (<SiteThemeContext.Provider ={}>{this_.props._children}</SiteThemeContext.Provider>)}} return value What is being returned by is the and the children of that component. The one prop you have to provide the provider is a prop. This is the variable that the consumer has access to. The consumer being (more on this shortly). <SiteThemeProvider> <SiteThemeContext.Provider> value <SiteThemeContext.Consumer> So what we can do now is have what is passed into be an object so it can store multiple properties of the state and the functions that are defined in . value value={{}} SiteThemeContt The state for the context needs to be the . We need to import the theme from and add that to the state. We’re going to default the theme (and state) to and add a copy of that into the prop by spreading into state [ ❤️]. It should look like this: theme src/theme/globalStyle theme1 value … React 'react' PropTypes 'prop-types' import from import from { themes } '../theme/globalStyle' import from export const = React_._createContext() SiteThemeContext export class SiteThemeProvider React_._Component {state = {theme: themes['theme1']} extends render() { (<SiteThemeContext.Provider ={{...this_. .props._children}</SiteThemeContext.Provider>)}} return value state}}>{this Ok, it’s been a while since I’ve added a gif, time for another one! And bring in the and add state: themes Now we can add in a function to the provider to change the theme state based on what has been selected via the event value: handleThemeChange handleThemeChange = e => {const = e_.target.value_ const = themes[key]this_._setState({ theme })} key theme This can then be consumed by any provider that wants to use it. We’re going to need to add it into the prop, like this: value React 'react' PropTypes 'prop-types' import from import from { themes } '../theme/globalStyle' import from export const = React_._createContext() SiteThemeContext export class SiteThemeProvider React_._Component {state = {theme: themes['theme1']} extends handleThemeChange = e => {const = e_.target.value_ const = themes[key]this_._setState({ theme })} key theme render() { (<SiteThemeContext.Provider ={{...this_. . .props._children}</SiteThemeContext.Provider>)}} return value state,handleThemeChange: this handleThemeChange}}>{this Ok, that is the site theme context component covered, pretty straightforward, right? What I should mention is that the in the function is going to be the event from the theme select box that we’re about to make. e handleThemeChange Let’s go through adding in the function and adding that to the state: And now we can add the theme provider to so anything lower in the dependency tree can access it via a consumer. src/index.js Add the theme select Now we want a want to call the function that is part of the via the . I’m sure this is all making perfect sense right now (🤣) so let’s get right in there and define the component that we’re going to use to consume the with a component! handleThemeChange SiteThemeProvider SiteThemeContext SiteThemeContext.Provider ThemeSelect In the directory add a new component. This is where we are going to consume the site theme context with a consumer. src/components ThemeSelect.js The child of a consumer isn’t a component — it’s a function. So what we’re going to need to do is have the theme select inside the return of that function. Let’s first set up the styled-components that will make up the select: a select box, some options, and a wrapper. First we’ll do it without the consumer, then we’ll add it in. **ThemeSelect.js** React 'react' styled 'styled-components' import from import from { themes } '../theme/globalStyle' import from const = styled_.div_`_margin: 0rem 0.5rem 0rem 0.25rem;padding: 0rem 0.5rem 0rem 0.25rem;_` SelectWrapper const = styled_.select_` ${({ }) => theme_.fontBody_} ${({ }) => theme_.secondary_} ${({ }) => theme_.foreground_}_;border-radius: 4px;_` Select margin: 1.5rem 0.5rem;padding: 0.25rem 0.5rem;font-family: theme ;border: 2px solid theme ;box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1);background: theme export const = styled_.option_` ${({ }) => theme_.fontBody_}_;_` SelectOpt font-family: theme const ThemeSelect = => { (<SelectWrapper><Select>{ (themes)_._map(( , ) => { (<SelectOpt ={index} ={theme}>Theme {index + 1}</SelectOpt>)})}</Select></SelectWrapper>)} props return Object.keys theme index return key value export default ThemeSelect So from this we can list the this themes available to us in the object. But that’s it, the function to handle the theme change lives on the . themes SiteThemeProvider Back to the . As I mentioned earlier, the child of a consumer is a function. The first section is the from the provider ( ) so let’s take a quick look at what we’ve previously defined in the provider: SiteThemeContext.Consumer ()=>() value <SiteThemeContext.Provider> value={{…this_. ._handleThemeChange}} state,handleThemeChange: this Available from is the state and a function. So any of those items we can extract and pass to the provider. Or to put it another way the consumer can access those values. <SiteThemeContext.Provider> Here we can use destructuring to pull the function we need to change the theme. handleThemeChange React 'react' import from { SiteThemeContext } '../contexts/SiteThemeContext' import from const ThemeSelect = => { (<SiteThemeContext.Consumer>{({ }) => ()}</SiteThemeContext.Consumer>)} props return handleThemeChange export default ThemeSelect Currently this isn’t going to change the theme because we have that hardcoded into the styled-components . What we want to do is use a consumer for the currently selected theme in the . ThemeProvider SiteThemeContext Before that, we’ll also need to add in the event we want to use to pass the event ( ) to the function on . onChange e handleThemeChange SiteThemeContext Then in the component we can import our to consume the on the state and pass that to the styled-components . App <SiteThemeContext.Consumer> theme SiteThemeContext ThemeProvider Want to know more? As mentioned at the start of this article, a great resource is and . You can find his for the React Context API. @leighchalliday his YouTube channel great use case There’s also the and . React community on spectrum styled-components on spectrum of the walkthrough is available on . Example code CodeSandbox Thanks for reading 🙏 If there is anything I have missed, or if there is a better way to do something, then please let me know. This was originally posted on my you can view it please take a look at my other content if you enjoyed this. blog here Follow me on or on GitHub. Twitter Ask Me Anything