Github repo & Live project here.
Let’s start -
Run the following command to initialize a next.js appnpx create-next-app nextjs-dark-mode
Now installnpm i use-dark-mode @emotion/react @emotion/styled
Delete the styles directory, we will be using emotion styled for styling. Also delete everything on index.js file and paste the following code in it.
1
2
3
4
5
6
7
export default function Home() {
return (
<div>
Home Page
</div>
)
}
Now run the commandnpm run dev
Go to http://localhost:3000 and you should see a blank page with Home Page written on it.
Create a new file named theme.js in the root directory and paste the following code in it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
export const darkTheme = { color: { primary: 'red', darkIndigo: '#071530', paleGray: '#e5e5e8', background: '#252525', text: '#ffffff', }, }; export const lightTheme = { color: { primary: 'red', darkIndigo: '#071530', paleGray: '#e5e5e8', background: '#efefef', text: '#252525', }, };
These themes are simple Javascript objects and can be extended as we want.
Since we have two themes we will need to switch between them and persist state, for that we will be using React Context. Create a folder named contexts in root, then create a file named ThemeToggleContext and paste the following code in it
1 2 3 4 5
import React from 'react'; const ThemeToggleContext = React.createContext({}); export default ThemeToggleContext;
Let’s create a Navbar component, create a components folder in root, then inside it create a Navbar.js file and paste the following code in it.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import React from 'react';
import styled from '@emotion/styled';
import ThemeToggleContext from '../../contexts/ThemeToggleContext';
const StyledNavbar = styled.nav`
height: 5em;
border-bottom: 0.1em solid gray;
color: #fff;
background: #fff;
.container {
// padding: 1em;
display: flex;
justify-content: space-between;
align-items: center;
height: 100%;
button {
margin: 0 2em;
padding: 0.5em 1em;
font-size: 1em;
background: transparent;
color: #252525;
border: 0.1em solid #252525;
}
}
`;
const Navbar = (props) => {
const {
isDarkTheme,
toggleTheme
} = React.useContext(ThemeToggleContext);
return (
<StyledNavbar className={props.className}>
<div className={'container'}>
<h1>
{props.title}
</h1>
<button onClick={toggleTheme}>
Switch to {isDarkTheme ? 'Light': 'Dark'} mode
</button>
</div>
</StyledNavbar>
);
};
export default Navbar;
Let’s also create a layout for the entire application. Inside the components folder create a layouts folder, then create a file named mainLayout.js and paste the following code in it.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import React from 'react';
import Head from 'next/head';
import {
ThemeProvider
} from '@emotion/react';
import useDarkMode from 'use-dark-mode';
import ThemeToggleContext from '../../contexts/ThemeToggleContext';
import {
darkTheme,
lightTheme
} from '../../theme';
import Navbar from './Navbar';
import styled from '@emotion/styled';
const StyledMain = styled.main`
height: calc(100vh - 5em);
width: 100%;
`;
const MainLayoutContainer = styled.div`
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
`;
function MainLayout(props) {
const darkMode = useDarkMode(true);
const currentTheme = darkMode.value ? darkTheme : lightTheme;
const [isMounted, setIsMounted] = React.useState(false);
React.useEffect(() => {
setIsMounted(true);
}, []);
return (
<>
<Head>
<title>Dark Mode demo</title>
</Head>
<ThemeProvider theme={currentTheme}>
{isMounted && (
<ThemeToggleContext.Provider
value={{
isDarkTheme: darkMode.value,
toggleTheme: darkMode.toggle,
}}
>
<MainLayoutContainer>
<Navbar title="Dark Mode Demo" />
<StyledMain>{props.children}</StyledMain>
</MainLayoutContainer>
</ThemeToggleContext.Provider>
)}
</ThemeProvider>
</>
);
}
export default MainLayout;
Here you will notice we are setting isMounted inside the effect hook so as to make sure the theme is fetched before rendering the component and we don’t see the flicker.
Now let’s use this layout. Go to _app.js file and import then wrap Component with MainLayout like so:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import MainLayout from "../components/layouts/mainLayout"
function MyApp({
Component,
pageProps
}) {
return (
<MainLayout>
<Component {...pageProps} />
</MainLayout>
)
}
export default MyApp
Now we can start using the theme via emotion styled. Go to Navbar and replace the previous StyledNavbar const with the following.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
const StyledNavbar = styled.nav` height: 5em; border-bottom: 0.1em solid gray; color: (props) => props.theme.color.text; background: (props) => props.theme.color.background; .container { // padding: 1em; display: flex; justify-content: space-between; align-items: center; height: 100%; button { margin: 0 2em; padding: 0.5em 1em; font-size: 1em; background: transparent; color: (props) => props.theme.color.text; border: 0.1em solid (props) => props.theme.color.text; } } `;
Notice how we replaced the previously hard-coded colors with colors from the theme. Inside of emotion styled we have access to the theme via props.
Similarly, go to index.js file and create a ContainerDiv element like so:
1 2 3 4 5
const ContainerDiv = styled.div` background: (props) => props.theme.color.background; color: (props) => props.theme.color.text; height: 95vh; `
Replace the previous div with this element. Now the section element should also have background color and we can toggle the theme using the button on the navbar.
Hope you learned something from this.