Search code examples
javascriptsveltesveltekit

SvelteKit Maintenance Mode


Is there a good way to do display a maintenance page when visiting any route of my SvelteKit website?

My app is hosted on Vercel, for those who want to know.

What I've tried so far:

  • Set an environment variable called MAINTENANCE_MODE with a value 1 in Vercel.
  • For development purposes I've set this in my .env file to VITE_MAINTENANCE_MODE and called with import.meta.env.VITE_MAINTENANCE_MODE.

Then inside +layout.server.js I have the following code to redirect to /maintenance route

import { redirect } from "@sveltejs/kit";

export async function load({ url }) {
  const { pathname } = url;

  // Replace import.meta.env.VITE_MAINTENANCE_MODE with process.env.MAINTENANCE_MODE in Production
  if (import.meta.env.VITE_MAINTENANCE_MODE == 1) {
    if (pathname == "/maintenance") return;
    throw redirect(307, "/maintenance");
  } else {
    if (pathname == "/maintenance") {
      throw redirect(307, "/");
    };
  };
};

What I've also tried is just throwing an error in +layout.server.js with the following:

import { error } from "@sveltejs/kit";

export async function load() {
  if (import.meta.env.VITE_MAINTENANCE_MODE == 1) {
    throw error(503, "Scheduled for maintenance");
  };
};

However this just uses SvelteKit's static fallback error page and not +error.svelte. I've tried creating src/error.html in the hope to create a custom error page for +layout.svelte but couldn't get it to work. I would like to use a custom page to display "Down for maintenance", but I don't want to create an endpoint for every route in my app to check if the MAINTENANCE_MODE is set to 1.

Any help is appreciated


Solution

  • You could use a handle server hook, e.g. src/hooks.server.ts:

    import { env } from '$env/dynamic/private';
    import type { Handle } from '@sveltejs/kit';
    
    export const handle: Handle = async ({ event, resolve }) => {
        if (env.MAINTENANCE_MODE == '1' && event.route.id != '/maintenance')
            return new Response(undefined, { status: 302, headers: { location: '/maintenance' } });
    
        // <other logic>
        
        // Default response
        return await resolve(event);
    }
    

    And on the maintenance page you can prevent all further navigation:

    import { beforeNavigate } from '$app/navigation';
    
    beforeNavigate(async ({ cancel }) => {
        cancel();
    });
    

    (Possibly add some periodic checks via fetch calls to navigate elsewhere once the site is back online.)