Note: This post is part 3 of three-parts Understanding react hooks post series. This learning post is still in active development and updated regularly.
In the previous learning posts, part 1 & part 2 in this learning series an overview of three basic react hooks, and of using UseStaticQuery hooks in a Gatsby Site were discussed.
Part 1: React Hooks – An Overview
Part 2: Using UseStaticQuery Hook in a Gatsby Site
Part 3: Using React Context API in Gatsby site (this post)
The main objective of this part 3 of this learning series is to learn using the React Context API hooks in Gatsby site to implement a dark mode theme functionality with a Theme Provider.
React Context API
A brief overview of React Context API was with its definition was discussed in the previous learning post. Lets revisit the three main parts of UseContext API mentioned in the post below:
React.createContextwhich is passed the initialvalue. This returns an object with aProviderand aConsumer. The contextvalueis determined by the value prop from the nearestproviderabove.- The
Providercomponent is used higher in the component hierarchy and accepts a prop calledvalue(which can be anything). - The
Consumercomponent is used anywhere below the provider in the component hierarchy and accepts a prop called “children” which must be a function that accepts the value and must return a react element (JSX).
To understand useContext hook, a basic understanding of React Context is essential, which will be discussed in another post separately.
Context API Use in Gatsby
React developer Muhammad Muhsin has been working on React Context API and recently he wrote a post Using React Context API with Gatsby in Gatsby blog. In the blog post, he describes a step by step protocol to implement a dark mode in Gatsby site using React Context and a Theme Provider.
Note: This post is based on Using React Context API with Gatsby by Muhammad Muhsin from React Blog tutorial.
Starting Point
As a starting point, the site setup described in the Styling Gatsby Site post was used. Using the site’s setup the Header and Layout components were refactored using Gatsby’s useStaticQuery hook as described below.
Step 1: Creating a Context File
A context folder was created first inside project’s src folder and a ThemeContext.js file was inside the context folder as described in the tutorial.
//src/context/ThemeContext.js
//Source: https://www.gatsbyjs.org/blog/
import React from 'react'
const defaultState = {
dark: false,
toggleDark: () => {},
}
const ThemeContext = React.createContext(defaultState)
// Getting dark mode information from OS!
const supportsDarkMode = () =>
window.matchMedia('(prefers-color-scheme: dark)').matches === true
class ThemeProvider extends React.Component {
state = {
dark: false,
}
toggleDark = () => {
let dark = !this.state.dark
localStorage.setItem('dark', JSON.stringify(dark))
this.setState({ dark })
}
componentDidMount() {
// Getting dark mode value from localStorage!
const lsDark = JSON.parse(localStorage.getItem('dark'))
if (lsDark) {
this.setState({ dark: lsDark })
} else if (supportsDarkMode()) {
this.setState({ dark: true })
}
}
render() {
const { children } = this.props
const { dark } = this.state
return (
<ThemeContext.Provider
value={{
dark,
toggleDark: this.toggleDark,
}}
>
{children}
</ThemeContext.Provider>
)
}
}
export default ThemeContext
export { ThemeProvider }
In the example above, the ThemeProvider component (line 16) wraps its children (line 47) with ThemeContext.Provider (lines: 41-48) and the component is exported as a named export (line 55).
The toggleDark() function (lines: 21-25) “gets the current state.dark value and switches the value to the opposite. It then stores the new value in localStorage before setting it back to state using the setState function, so that it persists over page refreshes. The dark value from state and the toggleDark function are passed to the Provider.”
Note: The above <ThemeProvider /> component example uses class-based component. It will be refactored into a functional component with React Hooks in a separate post.
Step 2: Modifying the Gatsby Browser File
In the project root folder create gatsby-browser.js file if it’s not already created.
// gatsby-browser.js
//Source: https://www.gatsbyjs.org/blog/
import React from 'react'
import { ThemeProvider } from './src/context/ThemeContext'
export const wrapRootElement = ({ element }) => (
<ThemeProvider>{element}</ThemeProvider>
)
In the above code adopted from Gatsby blog, first the ThemeProvider component was imported (line 5) from the ThemeContext.js created in the previous section (step 1). The ThemeProvider component wraps the root element (line 8) & exported to wrapRootElement (line 7).
Step 3: Adding Toggle Switch at the Header
The header.js file was re-factored to insert code snippets to add theme switch button (lines: 23-25)
// src/components/header.js
mport { Link } from 'gatsby'
import PropTypes from 'prop-types'
import React from 'react'
import "../styles/main.css"
import ThemeContext from '../context/ThemeContext'
const Header = ({ siteTitle }) => (
<ThemeContext.Consumer>
{theme => (
<section className="header">
<div className="nav-container">
<div className="brand">
<Link to="/">
{siteTitle}
</Link>
</div>
<ul className="main-nav">
<Link to="/">Home</Link>
<Link to="/about/">About</Link>
<Link to="/resources/">Resources</Link>
<Link to="/contact/">Contact</Link>
<button className="dark-switcher" onClick={theme.toggleDark}>
{theme.dark ? <span>☀</span> : <span>☾</span>}
</button>
</ul>
</div>
</section>
)}
</ThemeContext.Consumer>
)
Header.propTypes = {
siteTitle: PropTypes.string,
}
Header.defaultProps = {
siteTitle: ``,
}
export default Header
Step 4: Refactoring Layout Component File
The layout.js component was refactored to include React useStaticQuery hook as described in the previous post. This will be done in two steps: first, creating siteMetadata.js file, and then editing the layout.js file.
4a: Creating siteMetadata.js File. The siteMetadata.js component hook described in the previous post looks like below:
// src/components/siteMetadata.js
import { graphql, useStaticQuery } from "gatsby"
const useSiteMetadata = () => {
const data = useStaticQuery(
graphql`
query {
site {
siteMetadata {
title
pagetitle
}
}
}
`
)
return data.site.siteMetadata
}
export default useSiteMetadata
4b: Editing Layout Component File. As described in the previous post the useSiteMetadata hook was imported (line 4) & used in line 12.
//src/components/layout.js
import React from 'react'
import PropTypes from 'prop-types'
import useSiteMetadata from '../components/siteMetadata';
import Header from './header'
import Footer from "../components/footer"
import '../styles/layout.css'
import '../styles/header.scss'
import ThemeContext from '../context/ThemeContext'
const Layout = ({ children }) => {
const { title } = useSiteMetadata();
return (
<section>
<ThemeContext.Consumer>
{theme => (
<div className={theme.dark ? 'dark' : 'light'}>
<Header siteTitle={title} />
<div className="site-main">
<div className="site-content">
{children}
</div>
</div>
<Footer />
</div>
)}
</ThemeContext.Consumer>
</section>
)
}
Layout.propTypes = {
children: PropTypes.node.isRequired,
}
export default Layout
In the above code snippets, the class of the wrapper div (lines: 15-17) will change based on the context value of the dark variable set as state in the ThemeContext.js file.
Step 5: Viewing in a Browser
After restarting the development server and viewed in localhost:8000 showing (see screenshot below) the theme switch icon on the top navigation.

In the above example, when theme switcher sun icon in top navigation is clicked, its context value changes and displays the dark theme (left) and vice versa when yellow icon on the right image is clicked.
Wrapping Up
In this learning note post, how to develop a conditional dark mode theme using React Context API, with internal Gatsby APIs to wrap the code with a ThemeProvider was described. Other use cases of Context API will be described in separate learning posts.
Useful Resources
While preparing this post, I have referred the following references extensively. Please refer to original posts for more detailed information.
- The Context API | React Doc
- Using Context API in React (Hooks and Classes) | Tania Rascia
- A Quick Intro to React Hooks with useEffect, useState, and useContext | codeburst.io
- Demystifying React Context by Matt Claffey | ITNext
- Getting Started With The React Hooks API | Smashing Magazine
