Implementing Custom 404 Pages in Sitecore JSS Next.js Multisite

Overview

This guide explains how to implement and customize 404 error pages in a Sitecore JSS Next.js multisite environment. We’ll cover both the Sitecore configuration and the Next.js implementation.

Sitecore Configuration

1. Create the 404 Page

First, create a custom 404 page in Sitecore Content Editor:

  1. Navigate to the page section in Content Editor
  2. Create a new page named _404 (Note: Avoid using “404” as the page name per Sitecore JSS guidelines)
  3. Set the display name to “404”
  4. Add desired components to the page

2. Configure Error Handling

Configure your site to use the new 404 page:

  1. Navigate to your site’s Settings node
  2. Scroll to the Error Handling section
  3. Set the “Page not found” link to your newly created 404 page
  4. Publish all changes

Next.js Implementation

Basic 404 Page Setup

Create a 404.tsx file in your Next.js app’s pages directory:

import {
  ComponentPropsCollection,
  ComponentPropsContext,
  SitecoreContext,
} from '@sitecore-jss/sitecore-jss-nextjs';
import { componentBuilder } from 'temp/componentBuilder';
import Layout from 'src/Layout';
import { GetStaticProps } from 'next';
import { sitecorePagePropsFactory } from 'lib/page-props-factory';
import { SitecorePageProps } from 'lib/page-props';

type PageProps = SitecorePageProps & {
  data: ComponentPropsCollection;
};

const Custom404: React.FC<PageProps> = ({ layoutData, componentProps }) => {
  // Return static NotFound component if data is missing
  if (!layoutData || !componentProps) {
    return <NotFound />;
  }

  return (
    <ComponentPropsContext value={componentProps}>
      <SitecoreContext
        componentFactory={componentBuilder.getComponentFactory()}
        layoutData={layoutData}
      >
        <Layout layoutData={layoutData} headLinks={[]} />
      </SitecoreContext>
    </ComponentPropsContext>
  );
};

export const getStaticProps: GetStaticProps = async (context) => {
  if (process.env.DISABLE_SSG_FETCH || context?.locale?.toLocaleLowerCase() === 'default') {
    return { props: {} };
  }

  const props = await sitecorePagePropsFactory.create({
    params: {
      path: ['/_404'],
    },
    locale: context?.locale,
  });

  return { props };
};

export default Custom404;

Fallback Static Component

Create a NotFound.tsx component for cases where layout data is unavailable:

import Head from 'next/head';

const NotFound = (): JSX.Element => (
  <>
    <Head>
      <title>404: NotFound</title>
    </Head>
    <div style={{ padding: 10 }}>
      <h1>Page not found</h1>
      <p>This page does not exist.</p>
      <a aria-label="Go to the Home page" href="/">
        Go to the Home page
      </a>
    </div>
  </>
);

export default NotFound;

Multisite Configuration

1. Create API Route for Error Pages

Create pages/api/error/content.ts to handle multisite 404 content:

import {
  ComponentPropsService,
  GraphQLErrorPagesService,
  LayoutServiceData,
} from '@sitecore-jss/sitecore-jss-nextjs';
import { siteResolver } from 'lib/site-resolver';
import { NextApiRequest, NextApiResponse } from 'next';
import config from 'temp/config';
import clientFactory from 'lib/graphql-client-factory';
import { moduleFactory } from 'temp/componentBuilder';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  // Get site name based on domain
  const domain = req.headers.host;
  let siteName = config.sitecoreSiteName;
  
  try {
    const site = siteResolver.getByHost(domain ?? '');
    siteName = site.name;
  } catch (error) {
    // Fallback to default site name
  }

  const errorPagesService = new GraphQLErrorPagesService({
    clientFactory,
    siteName,
    language: (req.headers.locale as string) ?? config.defaultLanguage,
    retries: parseInt(process.env.GRAPH_QL_SERVICE_RETRIES ?? '0', 10),
  });

  try {
    // Fetch error pages content
    const resultErrorPages = await errorPagesService.fetchErrorPages();

    // Fetch component props
    const context = {
      defaultLocale: 'en',
      locale: 'en',
      locales: ['en'],
    };

    const layoutService = await new ComponentPropsService().fetchStaticComponentProps({
      context,
      layoutData: resultErrorPages?.notFoundPage?.rendered as LayoutServiceData,
      moduleFactory,
    });

    // Set cache headers
    res.setHeader('CDN-Cache-Control', `s-maxage=60, stale-while-revalidate=${60 * 60}`);
    res.setHeader('Cache-Control', `max-age=60, stale-while-revalidate=${60 * 60}`);

    res.status(200).json({ ...resultErrorPages, componentProps: layoutService });
  } catch (error) {
    console.error('Error fetching error pages:', error);
    res.status(500).json({ message: 'An error occurred' });
  }
}

2. Update 404 Page for Multisite Support

Modify your 404.tsx to use the new API endpoint:

import {
  ComponentPropsCollection,
  ComponentPropsContext,
  ErrorPages,
  LayoutServiceData,
  SitecoreContext,
} from '@sitecore-jss/sitecore-jss-nextjs';
import { componentBuilder } from 'temp/componentBuilder';
import Layout from 'src/Layout';
import { useEffect, useState } from 'react';
import NotFound from 'src/NotFound';
import { SitecorePageProps } from 'lib/page-props';
import { ComponentProps } from '@sitecore-feaas/clientside';

type PageProps = SitecorePageProps & {
  data: ComponentPropsCollection;
};

const Custom404: React.FC<PageProps> = () => {
  const [layoutData, setLayoutData] = useState<LayoutServiceData>();
  const [componentProps, setComponentProps] = useState<ComponentProps>();
  const [fetchError, setFetchError] = useState(false);

  useEffect(() => {
    if (!layoutData) {
      fetch('/api/error/content')
        .then(async (res) => {
          const data = (await res.json()) as ErrorPages | null;
          if (data?.notFoundPage.rendered) {
            setComponentProps((data as any)?.componentProps);
            setLayoutData(data?.notFoundPage.rendered);
          } else {
            setFetchError(true);
          }
        })
        .catch(() => {
          setFetchError(true);
        });
    }
  }, []);

  if (fetchError) {
    return <NotFound />;
  }

  if (!layoutData || !componentProps) {
    return <></>;
  }

  return (
    <ComponentPropsContext value={componentProps}>
      <SitecoreContext
        componentFactory={componentBuilder.getComponentFactory()}
        layoutData={layoutData}
      >
        <Layout layoutData={layoutData} headLinks={[]} />
      </SitecoreContext>
    </ComponentPropsContext>
  );
};

export default Custom404;

Important Notes

  1. The DISABLE_SSG_FETCH environment variable must be unset or false for local development to fetch static pages. (when you fetch data using getStaticProps)
  2. The multisite implementation uses an API route instead of getStaticProps to fetch layout data based on the hostname.
  3. Cache headers are implemented for browser optimization.
  4. A fallback static NotFound component is provided for cases where the layout data cannot be fetched.
  5. Make sure to publish all Sitecore changes after configuration.

Troubleshooting

  • If the 404 page isn’t appearing, verify that:
    • The page is published in Sitecore
    • The Error Handling settings are correctly configured
    • The DISABLE_SSG_FETCH environment variable is not set to true (when you fetch data using getStaticProps).
    • The API route is returning the correct data for your hostname

Leave a comment

I’m Ravi

Site Logo

a passionate software engineer focused on building modern digital experiences using technologies like Sitecore, Next.js, and modern JavaScript frameworks.

Let’s connect

Design a site like this with WordPress.com
Get started