Featured image

When creating and managing a project, it is important to take into account the moments when the application will be unavailable due to maintenance. In the case of CMS systems, such as WordPress, there are various plug-ins that allow you to veil your site while you make changes or test new functionality.

But how to implement maintenance mode in Next.js? Is it as easy as configuring a plugin on WordPress for a few minutes?

Of course it is!

Challenge

While searching for a solution for maintenance mode in Next.js applications, I came across a lack of available tools that met my expectations in terms of speed, flexibility and ease of use.

Solution

To start, we create a route.ts file in the app -> api root folder, which will be responsible for handling the request for our maintenance mode:

import { serialize } from "cookie";

export async function POST(request: Request, params: { slug: string }) {
 const data: { password: string } = await request.json();
 const password = data.password;

 const expirationTime = 4 * 60 * 60; // 4 hours
 const cookie = serialize(process.env.PASSWORD_COOKIE_NAME!, "true", {
   httpOnly: true,
   path: "/",
   maxAge: expirationTime,
 });

 if (process.env.PAGE_PASSWORD !== password) {
   return new Response("incorrect password", {
     status: 401,
   });
 }

 return new Response("password correct", {
   status: 200,
   headers: {
     "Set-Cookie": cookie,
   },
 });
}

The next step will be to create a component, the appearance of which, the user will see immediately after entering the site. Below is a sample design that can be customized:

"use client";

import React, { useState, useEffect } from "react";

const PasswordPromptDialog = () => {
 const [password, setPassword] = useState("");
 const [passwordIncorrect, setPasswordIncorrect] = useState(false);
 const [noPassword, setNoPassword] = useState(false);

 const handleSubmit = async (e: React.FormEvent) => {
   e.preventDefault();

   const request = await fetch(`/api`, {
     body: JSON.stringify({ password }),
     headers: { "Content-Type": "application/json" },
     method: "post",
   });

   if (password) {
     if (request.status !== 200) {
       return setNoPassword(false), setPasswordIncorrect(true);
     } else {
       window.location.reload();
     }
   } else {
     return setPasswordIncorrect(false), setNoPassword(true);
   }
 };

 useEffect(() => {
   setPassword("");
 }, [passwordIncorrect]);

 return (
   <div>
     <p>Maintenance mode is enabled!</p>
     <div>
       <form onSubmit={handleSubmit}>
         <label htmlFor="password">Password:</label>
         <input
           type="password"
           id="password"
           value={password}
           onChange={(e) => setPassword(e.target.value)}
         />
         <button type="submit">
           Log In
         </button>
       </form>
       {noPassword && (
         <p
           style={{
             color: "red",
             fontSize: "14px",
           }}
         >
           Enter the password.
         </p>
       )}
       {passwordIncorrect && (
         <p
           style={{
             color: "red",
             fontSize: "14px",
           }}
         >
           The entered password is incorrect.
         </p>
       )}
     </div>
   </div>
 );
};

export default PasswordPromptDialog;

The last step, will be to call the PasswordPromptDialogcomponent in the layout.tsx file as follows:

  • in order to display this mode, we check the value of the environment variable MAINTENANCE_MODE,
  • if the maintenance mode is to be enabled, we verify the contents of the cookies to determine user authentication and then display our maintenance mode content — otherwise, the default page content is displayed.
import { cookies } from "next/headers";
import PasswordPromptDialog from "@/components/PasswordPromptDialog/PasswordPromptDialog";

import "@/styles/globals.scss";

export default async function RootLayout({
 children,
}: {
 children: React.ReactNode;
}) {
 const isMaintenanceMode = process.env.MAINTENANCE_MODE === "true";

 if (isMaintenanceMode) {
   const cookieStore = cookies();
   const loginCookies = cookieStore.get(
     `${process.env.PASSWORD_COOKIE_NAME!}`
   );
   const isLoggedIn = !!loginCookies?.value;

   if (!isLoggedIn) {
     return (
       <html lang="pl">
         <body>
           <PasswordPromptDialog />;
         </body>
       </html>
     );
   }
 }

 return (
   <html lang="pl">
     <body>{children}</body>
   </html>
 );
}

At the very end, just add the following environment variables to the project (.env) with fixed values:

MAINTENANCE_MODE=FALSE
PASSWORD_COOKIE_NAME=authorized
SEARCH_QUERY_NAME=password
PAGE_PASSWORD=YourPasswordForWebsite

Summary

Implementing maintenance mode in Next.js applications, contrary to appearances, is crucial when developing and managing a project. Although CMS systems, such as WordPress, offer a variety of plugins that allow you to obscure your site during maintenance work, implementing maintenance mode in Next.js may seem more challenging.

However, with the solution presented here, which relies on the use of cookies to store authentication information, creating maintenance mode becomes as simple an operation as configuring a plug-in in a CMS. Thanks to the use of cookies, we not only maintain flexibility and speed, but also increase the level of security by preventing JavaScript scripts from reading data (setting the httpOnly option).

In summary, the above solution allows quick and easy implementation of maintenance mode in Next.js applications, while giving the user full control over the appearance of the "veil" and authentication. In addition, the ability to quickly switch maintenance mode by changing the value of an environment variable makes our solution extremely flexible and adaptable to different design needs.

Other blog posts

Next.js + Vercel, the Best Solution for the Frontend of an Online Store

Finding the right frontend solution for your online store is crucial. Enter Next.js and Vercel, a dynamic duo that promises unmatched performance, scalability, and developer experience for frontend developers...

Frontend Developer: Master of Efficiency and UX — Key Strategies for Optimal Application Performance

In today's ever-evolving digital world, where users expect speed, performance and a seamless web application experience, the role of the Frontend Developer becomes extremely important...

Tell us about your project

Got a project in mind? Let's make it happen!

By clicking “Send Message” you grant us, i.e., Rigby, consent for email marketing of our services as part of the communication regarding your project. You may withdraw your consent, for example via hello@rigbyjs.com.
More information
placeholder

Grzegorz Tomaka

Co-CEO & Co-founder

LinkedIn icon
placeholder

Jakub Zbaski

Co-CEO & Co-founder

LinkedIn icon