DEV Community

Cover image for React Theme Switcher

React Theme Switcher

holdmypotion profile image Rahul ・5 min read

In this article, we'll be creating a theme switcher using styled-components, context API, and of course, react.

Live Example:

Github Repository:


Run the following commands to initiate a react app.

npx create-react-app theme-switcher
cd theme-switcher
yarn add styled-components styled-normalize
Enter fullscreen mode Exit fullscreen mode

Thus, you have a react app powered by styled-components.

Now, In the src folder create

  1. a components folder and then a Layout.js file within.
  2. a context folder and then a globalContext.js file within
  3. a pages folder and then a Home.js file within
  4. a styles folder and then a globalStyles.js file ad a homeStyles.js file.

The end structure should look something like this.

Component tree structure

Creating a context for current theme state

Inside the globalContext.js file, paste the below code.


import React, { useState } from "react";

export const GlobalContext = React.createContext({
  currentTheme: "",
  themeSwitchHandler: () => {},

const GlobalContextProvider = (props) => {
  const [currentTheme, setCurrentTheme] = useState(
    window.localStorage.getItem("theme") == null
      ? "light"
      : window.localStorage.getItem("theme")

  const themeSwitchHandler = (themeType) => {

  return (
        theme: currentTheme,
        themeSwitchHandler: themeSwitchHandler,

export default GlobalContextProvider;
Enter fullscreen mode Exit fullscreen mode

The code above creates a context with a state and state changing function.

In the end, properties mentioned in the value prop will be available to the components using the useContext() hook.

    theme: currentTheme,
    themeSwitchHandler: themeSwitchHandler,
Enter fullscreen mode Exit fullscreen mode

Using the Context:
Paste the code below into the index.js file.

// index.js

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";

import GlobalContextProvider from "./context/globalContext";

        {/* Wrap the App component with the GlobalContextProvider
                created in the previous code snippet */}
      <App />

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more:
Enter fullscreen mode Exit fullscreen mode

Setting up a Layout Wrapper for pages

Paste the code below in the "src/components/Layout.js" file.

// Layout.js

import React, { useContext } from "react";
import { createGlobalStyle, ThemeProvider } from "styled-components";
import { normalize } from "styled-normalize";

import { GlobalContext } from "../context/globalContext";

// 1.
const GlobalStyle = createGlobalStyle`

  * {
    text-decoration: none;

  html {
    box-sizing: border-box;
    -webkit-font-smoothing: antialiased;
    font-size: 16px;

  body {
    font-family: 'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;;
    background: ${(props) => props.theme.background};

// 2.
const Layout = ({ children }) => {
  const darkTheme = {
    background: "#111827",
    secondaryBackground: "#374151",
    text: "#F9FAFB",
    button: "#E5E7EB",

  const lightTheme = {
    background: "#F9FAFB",
    secondaryBackground: "#E5E7EB",
    text: "#111827",
    button: "#374151",

    // 3.
  const currentTheme = useContext(GlobalContext);

    // 4.
  let theme;
  switch (currentTheme.theme) {
    case "dark":
      theme = darkTheme;
    case "light":
      theme = lightTheme;
      theme = lightTheme;

  return (
    <ThemeProvider theme={theme}>
      <GlobalStyle />

export default Layout;
Enter fullscreen mode Exit fullscreen mode

Let's break it down

  1. The GlobalStyle constant defines the base styles that are generally defined in index.css file.
  2. The Layout component has two constants, darkTheme and lightTheme and we'll be creating a toggle button to switch between the two. Using the same strategy, you can create as many themes as you would like.
  3. Next we are fetching the currentTheme from the globalContext.
  4. The switch case statement populates the "theme" variable which is later passed into the ThemeProvider component provided by styled-components.

Creating the styles for the home page

I like to divide my styled-components on the basis of pages. As some of the styles are common among the pages, I also create a globalStyles.js file to define those.

// globalStyles.js

import styled, { css } from "styled-components";

export const Container = styled.div`
  margin: 0 auto;
  padding: 0 32px;
  width: auto;
  height: 100%;

  ${(props) =>
    props.fluid &&
      padding: 0;
      margin: 0;
      max-width: 100%;

export const Flex = styled.div`
  display: flex;
  align-items: center;
  ${(props) => &&
      justify-content: center;
  ${(props) =>
    props.column &&
      flex-direction: column;
Enter fullscreen mode Exit fullscreen mode

Page-specific styles

// homestyles.js

import styled from "styled-components";

export const NavBar = styled.div`
  background-color: ${(props) => props.theme.secondaryBackground};
  padding: 20px 0;

export const SwitchButton = styled.label`
  position: relative;
  display: inline-block;
  width: 60px;
  height: 34px;

  input {
    opacity: 0;
    width: 0;
    height: 0;

  span {
    position: absolute;
    cursor: pointer;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: ${(props) => props.theme.button};
    -webkit-transition: 0.4s;
    transition: 0.4s;
    border-radius: 34px;

  span:before {
    position: absolute;
    content: "";
    height: 26px;
    width: 26px;
    left: 4px;
    bottom: 4px;
    background-color: ${(props) => props.theme.secondaryBackground};
    -webkit-transition: 0.4s;
    transition: 0.4s;
    border-radius: 50%;

  input:checked + span {
    background-color: ${(props) => props.theme.button};

  input:focus + span {
    box-shadow: 0 0 1px #2196f3;

  input:checked + span:before {
    -webkit-transform: translateX(26px);
    -ms-transform: translateX(26px);
    transform: translateX(26px);

export const Body = styled.div`
  padding: 3em 5em;

export const Heading = styled.div`
  font-size: 5em;
  font-weight: 800;
  color: ${(props) => props.theme.text};

export const SubPara = styled.p`
  font-size: 1.5em;
  color: ${(props) => props.theme.text};

export const Para = styled.p`
  font-size: 1.2em;
  line-height: 1.5;
  color: ${(props) => props.theme.text};
  width: 80%;

export const Content = styled.div`
  padding: 10em 0;
Enter fullscreen mode Exit fullscreen mode

Now that we are done creating our little styled-components. It is time to use them to create the final page

Home Page

Paste the code below in "src/components/Home.js" file

// Home.js

import React, { useContext, useEffect } from "react";
import { GlobalContext } from "../context/globalContext";

import Layout from "../components/Layout";
import { Container, Flex } from "../styles/globalStyles";
import {
} from "../styles/homeStyles";
const Home = () => {

    // 1.
  const { theme, themeSwitchHandler } = useContext(GlobalContext);

  useEffect(() => {
    window.localStorage.setItem("theme", theme);
  }, [theme]);

  return (
        <Container fluid>
          <Flex center>
                        {/* 2. */}
                onChange={() =>
                  themeSwitchHandler(theme === "dark" ? "light" : "dark")
            What's up! Toggle the switch above to change the theme
              <Flex center column>
                  Lorem, ipsum dolor sit amet consectetur adipisicing elit.
                  Reprehenderit quis ipsa, sunt, consectetur voluptate dolores
                  pariatur nisi distinctio iusto vero iure officia. Vero sunt,
                  ducimus sit eveniet dolor impedit itaque voluptate ipsam!
                  Omnis totam, beatae dicta fugit praesentium fugiat dolores
                  laborum, officiis, labore aperiam tempore! Debitis, provident!
                  Rem, exercitationem enim?

export default Home;
Enter fullscreen mode Exit fullscreen mode

Let's break it down:

  1. We are fetching our context from the globalContext using the useContext() hook.
  2. The "onChange" prop of the switch button toggles the theme between dark and light. (A better way would be to create a separate button to call for different themes as we already have a switch-case statement to select the theme in the Layout.js file.)

Finally, import this component into the App.js file

import Home from "./pages/Home";
function App() {
  return (
    <div className="App">
      <Home />

export default App;
Enter fullscreen mode Exit fullscreen mode

Further Ideas

This method of using the ThemeProvider component to set themes is not just limited to colors, as it is clearly visible that you can define any kind of styles, store them into a constant, and then pass it on as a theme.

Be creative, think of some use cases where you can maybe pass different margins or padding or perhaps width. You could also pass different fonts, maybe create a website like
This is super fun, so surely give it a try.

Thank you so much for reading.

I would love to hear your views. Be sure to comment below!

Discussion (2)

Editor guide
geobrodas profile image

Great work!
So much effort hats off!

holdmypotion profile image
Rahul Author

Thank you so much, Georgey.
I am glad you liked it.