Converting Sitecore MVC sites to Jamstack with Headless Services and JSS – Next.js

In this post I’ll be showing an approach to convert existing Sitecore MVC applications to the Jamstack architecture, it’s time to think about how to modernize our old-fashioned Sitecore apps to benefit from modern tech stacks capabilities like headless, SSG, ISR, multi-channel, etc.

Architecture

Jamstack architecture for existing Sitecore MVC sites is possible because of the ability of the Sitecore Layout Service to render MVC components to HTML, and include them in its output.

Jamstack architecture with Next.js. Ref: here

The publishing and rendering process consists of the following steps:

  1. The Layout Service outputs MVC components as HTML, embedded in its usual service output.
  2. The Layout Service output is published to the Content Delievry with each page/route, allowing it to be queried by Sitecore headless SDKs such as Next.js.
  3. The Next.js application queries the Layout Service output for the route and passes it into one or more placeholder components.
  4. Based on the lack of a componentName property in the layout data, the Placeholder component in the Sitecore Next.js SDK renders the Sitecore component directly as HTML into the pre-rendered document.

Prerequisites

  • Sitecore version 10.2+ – An upgrade of your MVC application would be needed.
  • Sitecore Headless Services module version 19+.

Demo!

To make things easier for this demo, I’m using the “Basic Company – Unicorn” site from the Sitecore Helix examples, you can find the repo here.

The first step is to upgrade the solution to 10.2, you can also find my open PR with the upgrade here.

Then, we need to add the Headless Services to our CM and CD images. You can find the final code here, which also adds on top of it a Next.js RH app image.

At this point, we have our MVC application up and running on Sitecore 10.2 and Headless Services are also installed. We are now ready to start making some changes to the app so we can make it work with JSS.

Prepare the MVC site to be compatible with JSS App

First of all, we need to create our API key in order to allow the Layout Service to communicate through our Sitecore instance. For that, we simply create an item under /sitecore/system/Settings/Services/API Keys

Make sure to keep CORS and controllers to allow * for this demo

To enable editing and static generation support in the JSS app, we have to make the site root to inherit from /sitecore/templates/Foundation/JavaScript Services/App template:

To enable editing support, we need the layout to inherit the template /sitecore/templates/Foundation/JavaScript Services/JSS Layout .

Now, we need to configure the Layout Service Placeholders field. This field determines which placeholder information to include in the Layout Service response data.

Inspect the Layout Service reponse

We can have a look now and analyze the Json we are getting from the Layout Service by visiting the endpoint:

https://www.basic-company-unicorn.localhost/sitecore/api/layout/render?item={97479C6B-BB30-4A15-AFD1-2C89F207E9D6}&sc_apikey={B10DB745-2B8A-410E-BDEC-07791190B599}

Layout Service response

We can see in the response, that we are getting the placeholders we included previously (main, header and footer).

Configure the Sitecore Layout Service to output HTML for MVC renderings

Let’s now go and configure the “Hero Banner” component to render HTML instead of Json:

Done, let’s publish this change and see what we get in the Layout Service response for this component:

So, here we go, we can find the HTML now in the contents. Let’s enable the HTML output on all the other MVC renderings and publish those changes, in the meantime, let’s create our JSS app.

Create the Nextjs JSS app

Let’s open a terminal and navigate to the src folder (..\examples\helix-basic-unicorn\src\Project\BasicCompany). We now run the JSS CLI command to create a new app, here we can choose if we want to fetch data with REST or GraphQL, also the prerendering on SSG or SSR:

jss create basic-company nextjs --empty --fetchWith GraphQL --prerender SSG

The JSS app is now created. Let’s set it up and connect it to our Sitecore instance. Run the following CLI command:

cd basic-company
jss setup

Provide the following values:

1- Is your Sitecore instance on this machine or accessible via network share? [y/n]: y
2- Path to the Sitecore folder (e.g. c:\inetpub\wwwroot\my.siteco.re): ..\examples\helix-basic-unicorn\docker\deploy\website
3- Sitecore hostname (e.g. http://myapp.local.siteco.re; see /sitecore/config; ensure added to hosts): https://www.basic-company-unicorn.localhost/
4- Sitecore import service URL [https://www.basic-company-unicorn.localhost/sitecore/api/jss/import]:
5- Sitecore API Key (ID of API key item): {B10DB745-2B8A-410E-BDEC-07791190B599}
6- Please enter your deployment secret (32+ random chars; or press enter to generate one):

Now we can deploy the config (check the files creates under sitecore/config). For this we run the following CLI command

jss deploy config

Prepare the NextJs App to render our content

Let’s update the Layout.tsx to add our placeholders (header, main, footer):

Also copy the “basic-company.css” from the website folder into the “src/assets” folder and update the _app.tsx with this :

All good, time to connect and test it! Run the following CLI command:

jss start:connected
HTTP://localhost:3000

Yay! visit http://localhost:3000 and you can see the Basic Company MVC site rendered as a JSS App, this is ready to be deployed and make it statically generated, but let’s move one step forward and start converting one of the components to React, as I see this approach to incrementally start your migration to JSS (React).

Experience Editor compatibility

Let’s double-check that our Experience Editor is still working as expected:

Start converting components from MVC (C#/Razor) to Next.js (JavaScript/React) incrementally

Let’s duplicate the “Hero Banner” rendering in Sitecore, change the template to make it a “Json Rendering”, rename it to “HeroBanner” to make it compliant with React naming conventions, and disable the “Render as HTML” checkbox. Also, make sure the “component name” field is set to “HeroBanner”. Then add this new component to the Homepage next to the MVC one.

Duplicated HeroBanner component

Publish the rendering and check again the Layout Service response, now, you should be able to see the two versions of the component, the one in HTML and the Json:

Good! We got the expected results on the Layout Service response, if we go now and refresh our JSS App, we will see that the component is added but still lacking its React implementation:

Create the React component through the component scaffolding

To create the React implementation of the component we created, just run the following in the terminal (always from the JSS App root):

jss scaffold BasicContent/HeroBanner 

Have a look at the files created, make some changes to the React implementation (BasicContent/HeroBanner.tsx)

import { Text, Field, ImageField } from '@sitecore-jss/sitecore-jss-nextjs';
export type HeroBannerProps = {
  fields: {
    Title: Field<string>;
    Subtitle: Field<string>;
    Image: ImageField;
  };
};
const HeroBanner = ({ fields }: HeroBannerProps): JSX.Element => {
  const bannerStyle = {
    backgroundImage: `url(${fields.Image?.value?.src})`,
  };
  return (
    <section className="hero is-medium is-black" style={bannerStyle}>
      <div className="hero-body">
        <div className="container">
          <h1>React!</h1>
          <Text field={fields.Title} tag="h1" className="title" />
          <Text field={fields.Subtitle} tag="h2" className="title" />
        </div>
      </div>
    </section>
  );
};
export default HeroBanner;

Now both MVC and React components are living on the same site, I kept both to make it more visual, but the proper way of migrating would be just replacing the MVC rendering.

I hope you find this interesting, you can find the complete solution here, it’s a fork of the Sitecore Helix Examples and added on top the headless services, Sitecore 10.2 upgrade, a NextJS rendering host, and app.

Thanks for reading!

Ref Official documentation

Sitecore JSS – NEXT.js – Exploring the Incremental Site Regeneration (ISR).

Next.js allows you to create or update static pages after you’ve built your site. Incremental Static Regeneration (ISR) enables developers and content editors to use static-generation on a per-page basis, without needing to rebuild the entire site. With ISR, you can retain the benefits of static while scaling to millions of pages.

Static pages can be generated at runtime (on-demand) instead of at build-time with ISR. Using analytics, A/B testing, or other metrics, you are equipped with the flexibility to make your own tradeoff on build times.

Consider an e-commerce store with 100,000 products. At a realistic 50ms to statically generate each product page, the build would take almost 2 hours without ISR. With ISR, we can choose from:

Faster Builds → Generate the most popular 1,000 products at build-time. Requests made to other products will be a cache miss and statically generate on-demand: 1-minute builds.

Higher Cache Hit Rate → Generate 10,000 products at build-time, ensuring more products are cached ahead of a user’s request: 8-minute builds.

Exploring ISR

In my previous post, I’ve created a JSS-Next.js app that we deployed to Vercel. I also created a WebHook to trigger a full rebuild in Vercel (SSG). Now, I’ll explain how the ISR works in this same app.

Fetching Data and Generating Paths

Data:

ISR uses the same Next.js API to generate static pages: getStaticProps.
By specifying revalidate: 5, we inform Next.js to use ISR to update this page after it’s generated.

Check the src/pages/[[…path]].tsx file and the getStaticProps function:

Paths:

Next.js defines which pages to generate at build-time based on the paths returned by
getStaticPaths. For example, you can generate the most popular 1,000 products at build-time by returning the paths for the top 1,000 product IDs in getStaticPaths.

With this configuration, I’m telling Next.js to enable ISR and to revalidate every 5 sec. After this time period, the first user making the request will receive the old static version of the page and trigger the revalidation behind the scenes.

The Flow

  1. Next.js can define a revalidation time per-page (e.g. 5 seconds).
  2. The initial request to the page will show the cached page.
  3. The data for the page is updated in the CMS.
  4. Any requests to the page after the initial request and before the 5 seconds window will show the cached (hit) page.
  5. After the 5 second window, the next request will still show the cached (stale) page. Next.js triggers a regeneration of the page in the background.
  6. Once the page has been successfully generated, Next.js will invalidate the cache and show the updated product page. If the background regeneration fails, the old page remains unaltered.

Page Routing

Here’s a high-level overview of the routing process:

In the diagram above, you can see how the Next.js route is applied to Sitecore JSS.

The [[…path]].tsx Next.js route will catch any path and pass this information along to getStaticProps or getServerSideProps on the context object. The Page Props Factory uses the path information to construct a normalized Sitecore item path. It then makes a request to the Sitecore Layout Service REST API or Sitecore GraphQL Edge schema to fetch layout data for the item.

Demo!

So, back to our previously deployed app in Vercel, login to Sitecore Content Editor and make a change on a field. I’m updating the heading field (/sitecore/content/sitecoreverceldemo/home/Page Components/home-jss-main-ContentBlock-1) by adding “ISR Rocks!”. We save the item and refresh the page deployed on Vercel. (Don’t publish! this will trigger the webhook that is defined in the publish:end event).

After refreshing the page, I can still see the old version:

But, if I keep checking what is going on in the ngrok, I can see the requests made to the layout service:

So, after refreshing again the page, I can see the changes there!

So, it got updated without the need of rebuilding and regenerating the whole site.

That’s it! I hope this post helps to understand how the ISR works and how to start with it on your Sitecore JSS implementation.

Thanks for reading and stay tuned for more Sitecore stuff!

Deploying a Sitecore JSS-Next.js App with SSG & ISR to Vercel (from zero to live)

In this post, I’ll share the steps to get our Next.js Sitecore App deployed into Vercel on some simple steps. Vercel is the creator of Next.js and now also a Sitecore partner. To avoid a huge and extensive post, I won’t be writing about Next.js, Vercel, JSS, etc. instead, please find some useful links with references to all those at the end of the blog post.

Getting the JSS app locally

The first step is to have the Sitecore JSS-Next.js app running locally. For simplifying things, we’ll be creating it with the help of JSS CLI. Before starting, make sure you’ve Node.js installed locally.

We just get started by running the following command to install the JSS CLI (more info here):

npm install -g @sitecore-jss/sitecore-jss-cli

Now, we can start to play with the CLI, so let’s create the app:

jss create sitecoreverceldemo nextjs

We give it a name (sitecoreverceldemo) and a framework (nextjs).

Connected Mode

The first thing we need to do is to create our API key so our JSS app can communicate to our Sitecore instance. In this demo, I’m running a local Sitecore instance, but it could be also a containerized one.

So, for doing that, we login into Sitecore and go to /sitecore/system/Settings/Services/API Keys and we create a new item, give it a name, and copy the ID somewhere, this gonna be our API key moving forward.

Now, we run the following command to start setting up it:

jss setup

We just follow the wizard and set the proper values for our Sitecore instance, API Key, import service URL, etc. If all went well, then you should see something like this:

Note that you’ve to add the recently created hostname to the hosts file in windows and ISS (sitecore.vercel.demo).

We’re now ready to deploy the configs, and right after that, the items, for doing that we simply run the following commands:

jss deploy config
jss deploy items --includeContent --includeDictionary

This will run the import to Sitecore and create the sample items. We can now build the app by running:

jss build

That’s it! We can now start our JSS App in connected mode:

jss start:connected

Code Repository

We now run some Git commands to push our code to GitHub. You can find the one I’m using for this demo here.

Vercel

Now, that we are done with our JSS Next.js app, we can have fun deploying it to Vercel. The first good news here, you can go and create your free account for testing purposes 🙂

Another good thing about Vercel is that it connects to GitHub, GitLab, and Bitbucket, so it makes things really easy. So, let’s go and import our GitHub repo there:

Let’s click on import and then configure our project. (Note: skip the Teams creation step to avoid having to get a trial account).

BUT as we have our Sitecore instance locally (or running on a container) we’ve to somehow expose our localhost to the internet. For that, we can use this amazing tool: Ngrok.

As I’m using the free version, it generates random URLs, but this is enough for our demo. Don’t forget to add those to the IIS binding and hostfile (if you’re running Sitecore locally).

Back to Vercel, we have to setup some environment variables:

SITECORE_API_KEY: The Sitecore API key we created in the previous step.
SITECORE_API_HOST: The URL generated by NGRock.
JSS_EDITING_SECRET: Your secret token. The JSS_EDITING_SECRET is optional for deployments but necessary if you want to use the Experience Editor with your Next.js Vercel deployment.

In next.config.js, replace:

const publicUrl = process.env.PUBLIC_URL;

with the following:

const publicUrl = process.env.VERCEL_URL ? https://${process.env.VERCEL_URL} : process.env.PUBLIC_URL;

So, it takes the URL we defined as an environment variable in Vercel.

Now everything is set and ready to be deployed. Let’s get back to Vercel, and deploy!

You’ll need to also update the hostname in sitecore/config/sitecoreverceldemo.config.

If everything was well configured, you should be able to see the requests to the headless services while Next.js is generating the static site during the building, something like this:

Et voila! The site is now live!

The publishing webhook

We need now to trigger the deployment if the content gets changed in the CMS. For that, we create a deploy hook in Vercel:

In the settings/Git section, we choose to create a deploy hook, we give it a name and a branch (develop in this case).

Create the hook and copy the URL. Let’s create now a config patch in Sitecore that will trigger it on publush:end

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/" xmlns:search="http://www.sitecore.net/xmlconfig/search/">
  <sitecore>
    <javaScriptServices>
      <publishWebHooks type="Sitecore.JavaScriptServices.AppServices.WebHooks.WebHooks, Sitecore.JavaScriptServices.AppServices">
        <hooks hint="list:AddWebHook">
          <hook type="Sitecore.JavaScriptServices.AppServices.WebHooks.WebHookDefinition, Sitecore.JavaScriptServices.AppServices">
            <name>Vercel - Publish Hook</name>
            <url>https://api.vercel.com/v1/integrations/deploy/prj_f4NcDuveH3zCP9a6pObWlaApER14/T7ATu61aej</url>
            <method>POST</method>
            <site>sitecoreverceldemo</site>
            <body></body>
          </hook>
        </hooks>
      </publishWebHooks>
    </javaScriptServices>
  </sitecore>
</configuration>
  • url: Required. The URL of the hook to be invoked.
  • method: Optional. The HTTP method for invoking webhook. Possible values are POST or GET. The default method is POST.
  • site: Optional. The sites which should trigger the webhook when published. By default, Sitecore will trigger the webhook for every published item. If you provide the site parameter, the webhook will be invoked if the published item root is an ancestor, descendant, or equal to the configured site’s root item.

Let’s test it, make a quick change in Sitecore and publish the item (heading field):

After publishing, we can see that the deploy hook got triggered in Vercel:

Refresh the site, and we can see our changes there:

In my next post I’ll explain a bit how the ISR works as it deserves specific writing about it.

That’s it! As you can see the steps to setup your CI/CD with Vercel is quite straightforward. I hope you find this post useful and helps you with the first steps of getting into Next.js and Vercel. Stay tuned for more Sitecore stuff!

References: