Night mode has become popular with Progressive Web Apps (PWAs). They are popular with social networking websites and one in particular, Twitter. Night mode was introduced to ease a reader's eyes while looking on a web page that has a white background.

Let's take a look at how we can put in place a night mode feature to ease our reader's eyes in a Next.js application. We will focus on adding a class name on our document body rather than implementing the actual css.

Our Client-side

We won't be using a toggle switch to toggle our night mode, we'll be using keyboard commands ctrl + n. Pressing both keys on any section of our website, we should be able to call a specified function and the best place to do that, I hope, is in the _app.js file situated in /pages/.

Normally we'd save some data in localStorage but we are using a Server-side Rendered, SSR, application which means we won't have any access to localStorage on the server. In our case, though, we'll make use of a little cookie package called js-cookie. This one works on client-side only. Sort of.

# npm i 'js-cookie'
$ yarn add 'js-cookie'

Then we implement:

// /pages/_app.js

import Cookie from 'js-cookie'

[...]

// A little state to keep track on the night 
// mode toggle: ctrl + n
state = {
  night: false,
}

componentDidMount() {
  const theme = Cookie.get('theme')
  const night = (theme === 'night')

  // At this point, night will be
  // either true or false
  this.setState({ night })
  
  // We'll get to this later
  // Uncomment after function is implemented
  // this.listenForNightModeCommands()
}

[...]

Here we import our cookie function then after our component has loaded we check if a cookie is present. If present, we set a state property to be true. This is only required for the toggle.

[...]

listenForNightModeCommands() {

  // https://keycode.info/
  document.addEventListener("keydown", event => {
  
    // The keyCode for "n" is 78
    if (event.ctrlKey && event.keyCode == 78) {
      this.setState({ night:  !this.state.night })
      
      // Your choice of naming convention goes here
      Cookie.set('theme', this.state.night ? 'night' : 'day', { expires: 365 })
      
      // Uncomment after function is implemented
      // this.toggleNightMode()
      event.preventDefault();
    }

  })
}

[...]

When you press both ctrl + n on your keyboard and inspect your cookie properties, you should see a cookie value for day or night (change values to your preference). 😉. A second toggle would give you "day", you can call this whatever you like but for this tutorial, we'll keep it as is.

[...]

// This feels so wrong on many levels.
// Maybe explore how to pass data to _document.js? 🤷🏽‍♂️
toggleNightMode() {
  const { night } = this.state
  const { body } = window.document

  if (night) {
    return body.classList.add('night')
  }
    
  body.classList.remove('night')
}

[...]

With this bit of code, we should have functioning night mode toggle. Inspect the body element of your DOM and you should see a body class being applied.

Our Server-side

Depending on how you've implemented your Next.js application, you'd notice that your body markup can be found inside _document.js. _document.js is server-side rendered so therefore we need to to pass data to this document. How do we do it? At first, we thought we'd have access to localStorage but sadly we won't be able to. The way to do is with cookies. Cookies are always get sent in an http request so it is possible to access theme cookie on the request header. To achieve this, we need to install another module, cookie.


// Inside pages/_document.js
// Special thanks to 
// https://github.com/zeit/next.js/issues/2252#issuecomment-308238001

[...]

import jsHttpCookie from 'cookie'

static async getInitialProps(ctx) {
  const initialProps = await Document.getInitialProps(ctx)

  if (ctx.req && ctx.req.headers) {
    const cookies = ctx.req.headers.cookie
    if (typeof cookies === 'string') {
      
      // cookiesJSON will hold all cookies that were sent
      // We only need 'theme'
      const cookiesJSON = jsHttpCookie.parse(cookies);
      initialProps.theme = cookiesJSON.theme;
    }
  }

  return { ...initialProps }
}

render() {
  return (
    <html lang="en">
      <Head>
        <style>{`body { margin: 0 } /* custom! */`}</style>
      </Head>
      <body className={ this.props.theme /* the beauty 🥰 */ }>
        <Main />
        <NextScript />
      </body>
    </html>
  )
}

This simple setup gives you a toggling night mode. You can now implement your very own switch to toggle night mode on or off ✊🏽