Sitecore Next.js Netlify Hosting

Anton Tishchenko
Anton Tishchenko
Cover Image for Sitecore Next.js Netlify Hosting

We continue our journey with Sitecore Headless and different hostings. Today we will talk about Netlify and we will find out how it fits Sitecore Next.js.

Last time I considered Netlify as hosting platform for Sitecore Next.js Headless more than a year ago. The normal mode was working, but the editing did not at that time. Many things changed from that time: new versions of sitecore-jss, new versions of @netlify/plugin-nextjs, new features on Netlify, and finally Netlify and Sitecore announced partnership. It is time to check one more time how everything works together.

There is no doubt that Netlify works fine with Next.js sites. It is the closest Vercel competitor for time being. There are only few disadvantages, like not full Next.js App Router support or worse ISR support(no on-demand and presence delay during revalidation), etc. But Netlify will do what you need for 90% of Next.js use cases.

We don’t need App Router for Sitecore and requirements to ISR are not usually strict. That is why, usage Netlify is good option for hosting your Sitecore Next.js in normal mode.

Editing Mode

How about Experience Editor mode? Knowing how Experience Editor mode works with Next.js, I had doubts. But partnership should mean support of all features. Right? Not really… It didn’t work out of the box. Having an experience with different hosting platforms and different frameworks makes us capable to make it work with Netlify. But it will require some effort. It worth to mention that Sitecore XM Cloud offer comes with hosted Next.js Rendering Host. And you don’t need to support editing endpoints on Netlify side. If you use XM Cloud, this article can be useless for you. Please look into official documentation and watch Thomas Desmond video.

Netlify Blobs

We need some place for storing intermediate editing state on the server. It is sync-disk-cache for Node.js. It is KV cache for Vercel. It can be Redis or Cosmos DB for Azure Static Web Apps. We faced with issue using sync-disk-cache. Netlify functions don’t share the same disk space. It means that if one function put data to the disk another can’t read it. KV cache is an implementation only for Vercel. It will not work with Netlify, because it is not part of Next.js, it is part of Vercel. There is only one Netlify feature that sounds suitable for this purpose: Netlify Blobs. It is still in Beta but there are a lot of information about it. Also it has open source code, making it possible to figure out if something goes wrong.

Next.js Netlify Plugin

Sitecore Next.js Experience Editor relies on Next.js API Routes. Netlify converts Next.js API routes to Netlify functions. There are two version of Netlify functions: V1 and V2. Next.js Netlify plugin uses “old” V1 functions. It has worse support of Netlify Blobs. Blobs will not be initialized automatically as it is written in the documentation.

this.store = getStore({ name: 'editing-data' });

You will need to pass additional parameters to initialize it: SITE_ID and TOKEN:

    this.store = getStore({
      name: 'editing-data',
      siteID: process.env.SITE_ID,
      token: process.env.TOKEN,
      consistency: 'strong',
    });

Environment Variables

Netlify has tricky environment variables scope. And it becomes even more tricky for Next.js sites, when you can save part of your variables in .env file. Also, depending on Netlify function versions, some variables, like NETLIFY_BLOBS_CONTEXT and NETLIFY_PURGE_API_TOKEN will not be available for you. Passing additional variable to initialize Blob store fixes this issue, but you need to have it in mind, when troubleshooting some issues.

Netlify Functions Overoptimization

Netlify team made a great job making Netlify functions code bundle as small as possible. But sometime it doesn’t include required NPM modules to the bundle. It happens with @netlify/blobs.

When I used import syntax

import { getStore } from '@netlify/blobs';

it had thrown file not found exception.

I made it work only with require syntax:

const blobs = require('@netlify/blobs');
const { getStore } = blobs;

Implementation

Full code for Netlify Editing Data Cache will next next:

const blobs = require('@netlify/blobs');
const { getStore } = blobs;
import type { Store } from '@netlify/blobs';

import { EditingDataCache } from './editing-data-cache';
import { EditingData } from './editing-data';
import { debug } from '@sitecore-jss/sitecore-jss';

/**
 * Implementation of editing cache for Netlify deployments
 * Uses Netlify Blobs to store data
 */
export class NetlifyEditingDataCache implements EditingDataCache {
  protected store: Store;

  constructor() {
    debug.editing('Netlify blobs...', process.env.NETLIFY_BLOBS_CONTEXT);
    this.store = getStore({
      name: 'editing-data',
      siteID: process.env.SITE_ID,
      token: process.env.TOKEN,
      consistency: 'strong',
    });
  }

  set(key: string, editingData: EditingData): Promise<void> {
    debug.editing(`Putting editing data for ${key} into Blob storage ...`);
    return new Promise<void>((resolve, reject) => {
      this.store
        .set(key, JSON.stringify(editingData))
        .then(() => resolve())
        .catch((err) => reject(err));
    });
  }

  get(key: string): Promise<EditingData | undefined> {
    debug.editing(`Getting editing data for ${key} from Blob storage...`);
    return new Promise<EditingData | undefined>((resolve, reject) => {
      this.store
        .get(key)
        .then((entry) => {
          const result = JSON.parse(entry) as EditingData;
          resolve(result);
        })
        .catch((err) => reject(err))
        .finally(() => this.store.delete(key));
    });
  }
}

/** NetlifyEditingDataCache singleton */
export const netlifyEditingDataCache = new NetlifyEditingDataCache();

Absence of Plugin Functions for Editing Data Cache

Now, we need to use our cache implementation. Unfortunately, Sitecore JSS implementation doesn’t have plugin architecture for Editing Data Cache. It has switch between two hardcoded implementations: for Next.js running by Node and Next.js running on Vercel. If you want to use Netlify, you need to copy all editing code from JSS source code. And modify 2 files

  1. \src\lib\editing\editing-data-service.ts

    Either hardcode it to netlifyEditingDataCache usage instead of editingDataDiskCache. Or add logic to switch between two implementations.

  2. \src\lib\page-props-factory\plugins\preview-mode.ts Use modified Editing Data Service instead of library implementation.

import { editingDataService } from 'lib/editing/editing-data-service';

And, of course you will need modify your render.ts API Page Route to use your Editing Data Middleware that is aware about Netlify.

Conclusion

Netlify is a great alternative to Vercel for Next.js hosting. It is a little behind the Vercel on the features support and has some limitations. However, this feature gap is not critical for hosting Sitecore Next.js sites. It works out of the box with Sitecore XM Cloud. It lacks out of the box editing capabilities for hosted Sitecore. You can fix it by using separate Next.js site for Experience Editor. Also, your developers can follow this article to add editing capabilities based on Netlify functions. And you can always contact us for help. We will help you find the balance between features and price, and build the most cost-efficient hosting solution for your case on any of the hosting platforms.