Creating a website that can addapt to users screen preference is getting more and more popular. When a website has this feature, it gives it a more polished feeling.
In this post, I'd like to document how I achieve this on this website with Tailwindcss in NextJS. Like many things, I learned how to implement this feature by following an online tutorial. So, credit goes to Avneesh Agarwal for creating this tutorial. In all honesty, you can better follow his tutorial to get exactly the same result :).
Anyway, let's just jump into it.
Create a NextJS Project
1npx create-next-app project-name
This will create a NextJs project with a 'project-name' folder name. You can change the project name to however you like by changing the last argument.
When this process finished, go ahead and run the NextJS development server.
1cd project-name && npm run dev
This will do 2 things;
cd project-name
will change the directory to the 'project-name'npm run dev
will start the NextJS development server.
If all goes well, you should see a localhost URL, usually http://localhost:3000, that you can visit to view your NextJS app.
Install Tailwindcss in your project
1npm install -D tailwindcss postcss autoprefixer 2npx tailwindcss init -p
This will install tailwindcss, postcss to cleanup unused css, and autoprefixer to add vendor prefixer. The second command will create tailwind.config.js. See the full guide.
Open the tailwind.config.js and add content to the config to make the purging of unused css works as follow:
1/** @type {import('tailwindcss').Config} */ 2module.exports = { 3 content: ["./pages/**/*.{js,ts,jsx,tsx}", "./components/**/*.{js,ts,jsx,tsx}"], 4 theme: { 5 extend: {}, 6 }, 7 plugins: [], 8};
The next thing is to add darkMode setting to the config as follow:
1/** @type {import('tailwindcss').Config} \*/ 2module.exports = { 3 content: ["./pages/**/_.{js,ts,jsx,tsx}", "./components/\*\*/_.{js,ts,jsx,tsx}"], 4 darkMode: "class', 5 theme: { 6 extend: {}, 7 }, 8 plugins: [], 9}; 10
Modify the global.css in the styles folder as follow:
1@tailwind base; 2@tailwind components; 3@tailwind utilities;
Create custom React Hook to add and remove class of the HTML element
Create a new folder, I call this folder 'lib'. In this folder create a new file called useDarkMode.js
1import { useEffect, useState } from "react"; 2 3function useDarkMode() { 4 const [theme, setTheme] = useState( 5 typeof window !== "undefined" ? localStorage.theme : "dark" 6 ); 7 const colorTheme = theme === "dark" ? "light" : "dark"; 8 9 useEffect(() => { 10 const root = window.document.documentElement; 11 12 root.classList.remove(colorTheme); 13 root.classList.add(theme); 14 15 if (typeof window !== "undefined") { 16 localStorage.setItem("theme", theme); 17 } 18 }, [colorTheme, theme]); 19 20 return [colorTheme, setTheme]; 21} 22 23export default useDarkMode;
Create mode switcher component
Let's now create a mode switcher component where we can use the newly created custom hook.
Since I use Bootstrap Icons with the help of react-bootstrap-icons package, let's install that first.
1npm i react-bootstrap-icons --save
Now that we have that installed, we can create a new file called ThemeSwitcher.js inside the components folder. We can then import the icons as well as the custom hook.
1import useDarkMode from "../lib/useDarkMode"; 2import { BrightnessHigh, Moon } from "react-bootstrap-icons"; 3 4export default function ThemeSwitcher() { 5 const [colorTheme, setTheme] = useDarkMode(); 6 7 return ( 8 <> 9 {colorTheme === "light" ? ( 10 <button 11 className="group cursor-pointer p-3 rounded-full transition-all duration-300 ring-1 hover:ring-2 hover:bg-white" 12 onClick={() => setTheme("light")} 13 > 14 <BrightnessHigh size={18} className="group-hover:text-black" /> 15 </button> 16 ) : ( 17 <button 18 className="group cursor-pointer p-3 rounded-full transition-all duration-300 ring-1 hover:ring-2 hover:bg-black" 19 onClick={() => setTheme("dark")} 20 > 21 <Moon size={18} className="group-hover:text-white" /> 22 </button> 23 )} 24 </> 25 ); 26}
That is it! Now we can use this component to any page or other components where we want the theme switcher to appear. In my case, I'd like to show this component on the entire website. To do that, we need to create a layout component. Read more about Layout component in NextJS in this guide.
Create Layout component
In the components folder, create Navbar.js and Layout.js. We will use the ThemeSwitcher component inside the Navbar component. We will then use the Navbar inside the Layout component.
Navbar.js
1import ThemeSwitcher from "./ThemeSwitcher"; 2import Image from "next/future/image"; 3import Link from "next/link"; 4 5const logoUrl = "path/to/logo.png"; 6export default function Navigation() { 7 return ( 8 <header className="border-b border-neutral-200 dark:border-neutral-800"> 9 <div className="max-w-4xl mx-auto flex justify-between items-center py-4 px-6 lg:px-0"> 10 <nav className=""> 11 <Link href="/"> 12 <a> 13 <Image src={logoUrl} width={32} height={32} alt="Logo" /> 14 </a> 15 </Link> 16 </nav> 17 <div className=""> 18 <ThemeSwitcher /> 19 </div> 20 </div> 21 </header> 22 ); 23}
Layout.js
1import Navigation from "./Navigation"; 2 3export default function Layout({ children }) { 4 return ( 5 <> 6 <Navigation /> 7 {children} 8 </> 9 ); 10}
Now we need to modify the _app.js inside the pages folder to use the Layout component.
1import "../styles/globals.css"; 2import Layout from "../components/Layout"; 3 4function MyApp({ Component, pageProps }) { 5 return ( 6 <Layout> 7 <Component {...pageProps} /> 8 </Layout> 9 ); 10} 11 12export default MyApp;
Here is how it looks like on this website.