In this article, we'll be creating a theme switcher using theme.json, context API, and of course, react.
And we can also able to switch between multiple themes and set the mode(light, dark) in corresponding theme.
No need any external npm package.
Git Repo: https://github.com/srmk/react-dynamic-theme-switch
Setup
Run the following commands to initiate a react app.
npx create-react-app react-dynamic-theme-switch
cd react-dynamic-theme-switch
Now, In the src folder create
a api folder and then a index.js file within.
a context folder and then a theme_context.js file within
a components folder and then a Header and Home pages file within
a themes folder and then theme.json file within
a styles folder and then a App.scss file within
The end structure should look something like this.
Create Theme JSON
Create a simple theme json file with required variables. And also you can create a different theme json in different name as per your requirement. (You can change this varibale names as any)
{
"light-theme": {
"primary-bg-color": "#114ba5",
"primary-bg-text-color": "#ffffff",
"primary-text-color": "#114ba5",
"secondary-bg-color": "#3297d3",
"secondary-bg-text-color": "#ffffff",
"secondary-text-color": "#3297d3",
"title": "#373636",
"paragraph": "#535353",
"subText": "#a4a6a9"
},
"dark-theme": {
"primary-bg-color": "#114ba5",
"primary-bg-text-color": "#ffffff",
"primary-text-color": "#2576f3",
"secondary-bg-color": "#3297d3",
"secondary-bg-text-color": "#ffffff",
"secondary-text-color": "#48b0ef",
"title": "#ffffff",
"paragraph": "#f0f0f0",
"subText": "#a4a6a9"
}
}
Creating a context for current theme state
Add theme context in 'src/context/theme_context.js'
import React from 'react';
export const themeContextDefaults = {
selectedTheme: '',
themeMode: 'light-theme',
themeOptions: [],
themes: [],
changeTheme: () => { },
toggleThemeMode: () => { }
};
export const ThemeContext = React.createContext(themeContextDefaults);
Using the Context:
Paste the code below into the App.js file.
import React, { useState, useMemo, useEffect } from 'react';
import { ThemeContext, themeContextDefaults } from './context/theme_context';
import { applyTheme } from './helper/theme_helper';
import { initialAPICalls } from './api';
import './style/App.scss';
import Header from './components/Header';
import Home from './components/Home';
function App() {
const [theme, setTheme] = useState({});
const [themeMode, setThemeMode] = useState(themeContextDefaults.themeMode);
const [themeOptions, setThemeOptions] = React.useState(themeContextDefaults.themeOptions);
const [selectedTheme, setSelectedTheme] = React.useState(themeContextDefaults.selectedTheme);
// theme update
const themeContext = useMemo(
() => ({
...themeContextDefaults,
changeTheme: (data) => {
setSelectedTheme(data);
applyTheme(theme[data][themeMode]);
},
toggleThemeMode: (checked) => {
let mode = checked ? 'dark-theme' : 'light-theme';
setThemeMode(mode);
applyTheme(theme[selectedTheme][mode]);
},
selectedTheme,
themes: theme,
themeMode,
themeOptions,
}),
[selectedTheme, themeMode, theme, themeOptions],
);
useEffect(() => {
fetchInitialDatas();
}, []);
async function fetchInitialDatas() {
const data = await initialAPICalls();
const { themes } = data;
if (themes) {
setThemeOptions(themes.themeOptions);
setSelectedTheme(themes.selectedTheme);
setTheme(themes.themeVariables);
applyTheme(themes.themeVariables[themes.selectedTheme][themeMode]);
}
}
return (
<ThemeContext.Provider value={themeContext}>
<div className={'app-container'}>
<div className="circle">
<div id={'crescent'} className="crescent"></div>
</div>
<Header />
<main className={'main-container'}>
<Home />
</main>
</div>
</ThemeContext.Provider>
);
}
export default App;
Create Pages
Create a new home page with Header Component
Header.js
import React from 'react';
import { ThemeContext } from '../../context/theme_context';
export default function Header() {
const toggleAction = (themeMode, toggleThemeMode) => {
let crescent = document.getElementById('crescent');
let toggle = document.getElementById('toggle');
let isActive = (themeMode === 'light-theme') ? true : false;
let xPosition = isActive ? '85%' : '0';
let scale = isActive ? '0.6' : '0';
toggleThemeMode(isActive);
crescent.style.transform = `scale(${scale})`;
toggle.style.transform = `translateX(${xPosition})`;
}
return (
<ThemeContext.Consumer>
{({ themeMode, themeOptions, changeTheme, toggleThemeMode }) => (
<header className={'app-header'}>
<h1><b>Theme Switcher</b></h1>
<div>
{/* <label htmlFor="themes">Change Theme:</label> */}
<select name="themes" id="theme" onChange={() => changeTheme(document.getElementById("theme").value)}>
{
themeOptions.map((val) => {
return <option key={val.key} value={val.key}>{val.text}</option>
})
}
</select>
</div>
<div className={'toggle-switch-container'}>
<input
type="checkbox"
id="switch"
checked={(themeMode === 'light-theme') ? true : false}
onChange={() => toggleAction(themeMode, toggleThemeMode)}
/>
<label htmlFor="switch">
<div id={'toggle'} className="toggle"></div>
<div className="names">
<p className="light">Light</p>
<p className="dark">Dark</p>
</div>
</label>
</div>
</header>
)}
</ThemeContext.Consumer>
)
}
Home.js
import React from 'react'
export default function Home() {
return (
<div>
<h2>Lorem Ipsum</h2>
<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
<h2>What is Lorem Ipsum?</h2>
<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
<h2>Why do we use it?</h2>
<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
<h2>Where can I get some?</h2>
<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
</div>
)
}
Apply theme
Finally apply themes for page using theme variables like below
body,
html {
margin: 0;
padding: 0;
width: 100%;
height: 100vh;
box-sizing: border-box;
color: var(--primary-bg-text-color);
background-color: var(--bg-color);
overflow-x: hidden;
overflow-y: auto;
scroll-behavior: smooth;
font-family: 'Rubik', -apple-system, BlinkMacSystemFont,
"Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans",
"Droid Sans", "Helvetica Neue", sans-serif;
;
}
Final output: https://react-dynamic-theme-switch.netlify.app
Please add a commets if you have any queris. Hope this is helpfull to you, Thanks for reading.