1. Sitecore JavaScript Services SDK (JSS)

Upgrade JSS 22.11 Next.js apps to version 22.12

Version:

This topic covers the changes you must make to your existing JSS 22.11 applications to upgrade to version 22.12. However, because of the nature of JavaScript and Next.js application development, this topic doesn't account for all the customization you might have in your existing application.

While upgrading, consider the JSS templates and add-ons you used when creating your Next.js application. You can find them in your package.json file. For example, a JSS 22.11 application included in the XM Cloud starter foundation uses the following templates and add-ons:

  • nextjs

  • nextjs-styleguide or nextjs-sxa

  • nextjs-multisite

XM Cloud is now SitecoreAI

Some code examples, images, and UI labels may still use XM Cloud while engineering assets are being updated.

Before you begin
  • If you haven't already done so, upgrade your app to JSS 22.11.

  • Familiarize yourself with the changelog. If your application is heavily customized, the changelog can provide guidance on what additional changes you need that are not covered in this topic.

This topic describes how to:

Update application dependencies in your existing app

For your upgraded application to work correctly, you will need to update dependencies.

To update your dependencies:

  1. In your existing application's package.json update every @sitecore-jss package to version ~22.12.0.

  2. Update the following dependencies to the specified versions:

    • Next.js has been updated to version 16. You may need to apply changes not mentioned here to your app based on the Next.js 16 upgrade guide.

      "next": "^16.1.1",
      Note

      If you see type errors related to JSX.Element (for example, in components like CdpPageView), add the following import where you use JSX.Element:

      import type { JSX } from 'react';
    • Node.js has been updated to version 24. You may need to apply changes not mentioned here to your app based on the Node.js 24 upgrade guide.

      "@types/node": "^24.10.4",
  3. Install the dependencies with the following command:

    npm install

Create a JSS 22.12 Next.js application

To simplify the upgrade process as much as possible, create a JSS 22.12 Next.js application. You can then copy some files from this app into your existing app.

To create a JSS 22.12 Next.js application:

  1. In a console, run the following command:

  2. If prompted to install the [email protected] package, answer y.

  3. Enter the folder path for the JSS 22.12 Next.js app. For example, enter ./jss2212, to create the app folder in your current working directory.

  4. Follow the remaining prompts, selecting the same options for data fetching (GraphQL or REST) and prerendering (SSG or SSR) as in your existing application.

  5. When asked if you are building for Sitecore XM Cloud, answer n.

  6. Select other add-ons used by your existing application and press Enter.

The script then installs the application dependencies.

XM Cloud is now SitecoreAI

Some code examples, images, and UI labels may still use XM Cloud while engineering assets are being updated.

Update the Next.js template files in your existing app

This explains how to synchronize files in your existing application with corresponding files from your new JSS 22.12 app.

To update the Next.js template files:

  1. Middleware has been renamed to proxy to better reflect its purpose. Rename src/middleware.ts to src/proxy.ts. To update your implementation:

    • Modify its export function so that it no longer accepts the ev parameter:

      export default async function proxy(req: NextRequest) {
        return middleware(req);
      }
    • Modify method signatures in /src/lib/middleware/index.ts so that it no longer uses NextFetchEvent:

      export interface MiddlewarePlugin {
        /**
         * Detect order when the plugin should be called, e.g. 0 - will be called first (can be a plugin which data is required for other plugins)
         */
        order: number;
        /**
         * A middleware to be called, it's required to return @type {NextResponse} for other middlewares
         */
        exec(req: NextRequest, res?: NextResponse): Promise<NextResponse>;
      }
      ...
      export default async function middleware(
        req: NextRequest
      ): Promise<NextResponse> {
      ...
      
      const finalRes = await (Object.values(plugins) as MiddlewarePlugin[])
          .sort((p1, p2) => p1.order - p2.order)
          .reduce((p, plugin) => p.then((res) => plugin.exec(req, res)), Promise.resolve(response));
      ...
      ...
    • Remove all imports of NextFetchEvent.

  2. By disabling React Suspense in placeholders, components render faster without the added Suspense overhead. However, if your app uses lazy-loaded components inside a <Placeholder/> and expects a loading fallback to appear, you need to perform the following steps while upgrading:

    • Audit <Placeholder/> usages where dynamic or lazy components are rendered.

    • Add disableSuspense={false} to any placeholder that depends on Suspense-based loading states.

Update the SXA add-on

To update the SXA add-on:

  1. Remove the sass-alias dependency:

     "sass-alias": "^1.0.5"
  2. Modify /src/lib/next-config/plugins/sass.js:

    • Find all the sass-alias imports:

      const SassAlias = require('sass-alias');
    • Replace it with the following:

      const fs = require('fs');
      const { pathToFileURL, fileURLToPath } = require('url');
    • Add a new function called createAliasImporter:

      /**
       * Custom Sass importer compatible with Next.js 16's Sass API
       * Implements canonicalize and load methods as required by the new Sass JS API
       */
      function createAliasImporter(aliases) {
        return {
          canonicalize(url, context) {
            // Check if the URL matches any of our aliases
            for (const [alias, aliasPath] of Object.entries(aliases)) {
              if (url.startsWith(alias)) {
                // Remove the alias prefix and resolve the path
                const relativePath = url.slice(alias.length);
                // Remove leading slash if present
                const cleanPath = relativePath.startsWith('/') ? relativePath.slice(1) : relativePath;
                const fullPath = path.resolve(aliasPath, cleanPath);
                
                // Try to find the file with common extensions
                const extensions = ['.scss', '.sass', '.css'];
                for (const ext of extensions) {
                  const filePath = fullPath + ext;
                  if (fs.existsSync(filePath)) {
                    return pathToFileURL(filePath);
                  }
                }
                
                // Try as a directory with index file
                if (fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory()) {
                  for (const ext of extensions) {
                    const indexPath = path.join(fullPath, 'index' + ext);
                    if (fs.existsSync(indexPath)) {
                      return pathToFileURL(indexPath);
                    }
                  }
                }
                
                // Try with underscore prefix (Sass partials)
                const dir = path.dirname(fullPath);
                const basename = path.basename(fullPath);
                for (const ext of extensions) {
                  const partialPath = path.join(dir, '_' + basename + ext);
                  if (fs.existsSync(partialPath)) {
                    return pathToFileURL(partialPath);
                  }
                }
                
                // Return the path even if file doesn't exist yet, let Sass handle the error
                return pathToFileURL(fullPath);
              }
            }
            
            // Return null if no alias matches, let other importers handle it
            return null;
          },
          
          load(canonicalUrl) {
            if (canonicalUrl.protocol === 'file:') {
              // Convert file:// URL back to file path
              const filePath = fileURLToPath(canonicalUrl);
              
              if (fs.existsSync(filePath)) {
                const contents = fs.readFileSync(filePath, 'utf8');
                // Determine syntax based on file extension
                const syntax = filePath.endsWith('.sass') ? 'indented' : 'scss';
                return {
                  contents,
                  syntax,
                };
              }
            }
            return null;
          },
        };
      }
    • Modify the sassPlugin export to use the new implementation:

      const sassPlugin = (nextConfig = {}) => {
        const aliases = {
          '@sass': path.join(__dirname, '../../../assets', 'sass'),
          '@fontawesome': path.join(__dirname, '../../../../node_modules', 'font-awesome'),
        };
        
        return Object.assign({}, nextConfig, {
          sassOptions: {
            importers: [createAliasImporter(aliases)],
            // temporary measure until new versions of bootstrap and font-awesome released
            quietDeps: true,    
            silenceDeprecations: ["import", "legacy-js-api"],
          },
          webpack: (config, options) => {
            // Exclude Node.js built-in modules used by this plugin from client bundle
            if (!options.isServer) {
              config.resolve.fallback = {
                ...config.resolve.fallback,
                fs: false,
                path: false,
                url: false,
              };
            }
            
            // Call existing webpack config if present
            if (typeof nextConfig.webpack === 'function') {
              return nextConfig.webpack(config, options);
            }
            
            return config;
          },
        });
      };

Next steps

To finalize the upgrade process, make sure you resolve any errors and warnings you encounter. Enable debug logging for JSS specific issues to assist you if necessary.

If you have suggestions for improving this article, let us know!