1. Sitecore Content SDK

Upgrade Content SDK 1.5.1 Next.js apps to version 2.0

Version:

This guide covers most of the changes you must make to your existing Content SDK 1.5.1 applications to take advantage of the new features and improvements in version 2.0.0. However, because this process also involves updating Next.js to version 16 and Node.js to version 24, you'll need to refer to the relevant guides for each framework to complete the upgrade process for your apps.

This version of the Content SDK introduces tracking, events, and personalization capabilities, custom query parameters in editing render handlers, custom hostname support, and more.

Because JavaScript and Next.js development is very flexible and customizable, this guide provides only general instructions and might not cover the specific configurations, custom code, or architectural decisions present in your existing application.

Before you begin
  • 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 must update dependencies.

To update your dependencies:

  1. In your existing application's package.json file, update every @sitecore-content-sdk package to version 2.0.0.

  2. Update the following dependencies to the specified versions:

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

      "@types/node": "^24.10.4",
    • Next.js has been updated to version 16. See the Upgrade Next.js to version 16 section for further steps to complete the update:

      "next": "^16.1.1",
    • Remove any dependencies that start with @sitecore-cloudsdk/ and add the following:

      "@sitecore-content-sdk/analytics-core": "^2.0.0", 
      "@sitecore-content-sdk/personalize": "^2.0.0",
      "@sitecore-content-sdk/events": " ^2.0.0",

      See the Cloud SDK integration section for further steps to complete the update.

  3. Install the dependencies with the following command:

    npm install

Create a template 2.0.0 Content SDK application for Next.js

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

To create a 2.0.0 Content SDK Next.js application:

  1. In a console, run the following command:

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

  3. When prompted, choose the same template as your existing app (nextjs for Pages Router and nextjs-app-router for App Router).

  4. Enter the folder path for the new app. For example, enter ./content-sdk-200, to create the app folder in your current working directory.

  5. When prompted, choose the same prerendering method (SSG or SSR) as used in your existing Content SDK application.

The script then installs the application dependencies.

Update your existing app

After updating your dependencies, you must synchronize files in your existing application. The following sections contains steps to complete the Next.js 16 upgrade, Cloud SDK integration, React 19 refactor, and updating other template files.

Upgrade Next.js to version 16

The Content SDK uses Next.js 16 starting from version 2.0. The following section describes the updates you must make for your app to work with Next.js 16:

  1. Middleware has been renamed to proxy to better reflect its purpose. Rename middleware.ts to proxy.ts or copy it from the template app. Instead of Next.js discovering your middleware automatically by filename, it now runs the proxy.ts file directly. To update your implementation:

    • Create src/proxy.ts or rename your current middleware.ts file to proxy.ts. Add your proxy logic here. The file must export a single function (default export or named proxy) that accepts a NextRequest and returns the result of the proxy chain as shown:

      export default function proxy(req: NextRequest) {
        // ... create locale, multisite, redirects, personalize instances ...
        return defineProxy(locale, multisite, redirects, personalize).exec(req);
      }
      export const config = { matcher: [...] };
    • Ensure that your proxy order is correct:

      • For Next.js apps using App Router - locale → multisite → redirects → personalize

      • For Next.js apps using Pages Router - multisite → redirects → personalize

    • When renaming your current middleware.ts file, you must replace the following import statements:

      import { 
      defineMiddleware,
      MultisiteMiddleware,
      AppRouterMultisiteMiddleware, 
      PersonalizeMiddleware, 
      RedirectsMiddleware, 
      LocaleMiddleware, } from '@sitecore-content-sdk/nextjs/middleware';
    • Replace it with the following:

      import { 
      defineProxy,
      MultisiteProxy,
      AppRouterMultisiteProxy,
      PersonalizeProxy,
      RedirectsProxy,
      LocaleProxy, } from '@sitecore-content-sdk/nextjs/proxy';

      Ensure that you use the updated names in the initialization logic.

  2. Individual API route files (for sitemap, robots, editing endpoints, health) still use the Middleware classes to implement their actual behavior for those specific routes. The proxy.ts file only handles edge requests and routing behavior.

    Item

    Description

    For sitemap and robots.txt

    Use SitemapMiddleware or RobotsMiddleware from @sitecore-content-sdk/nextjs/middleware

    Editing APIs

    Use EditingConfigMiddleware, EditingRenderMiddleware, or FEAASRenderMiddleware from @sitecore-content-sdk/nextjs/editing

    Healthcheck

    Use the HealthcheckMiddleware from @sitecore-content-sdk/nextjs/monitoring

  3. Next.js 16 expects you to define allowed external image sources using remotePatterns instead of images.domains. Remove all entries of images.domains and use remotePatterns to allow Sitecore/media image URLs. You can refer the template app's next.config.js|ts file for more information.

Important

Please refer to the official Next.js 16 upgrade guide to update other parts of your application that's not specific to the Content SDK.

Cloud SDK integration

The Content SDK has undergone a major architectural transformation to include functionality from the Cloud SDK. The Content SDK now includes tracking, events, and personalization.

To enable these changes:

  1. In Bootstrap.tsx:

    • Find the CloudSDK initialization logic:

      CloudSDK({ 
        sitecoreEdgeUrl: config.api.edge.edgeUrl, 
        sitecoreEdgeContextId: config.api.edge.clientContextId, 
        siteName: page.siteName || config.defaultSite, 
        enableBrowserCookie: true, 
        // Replace with the top level cookie domain of the website that is being integrated e.g ".example.com" and not "www.example.com" 
        cookieDomain: window.location.hostname.replace(/^www\./, ''), 
      })
        .addEvents() 
        .initialize(); 
    • Replace it with the following:

      initContentSdk({ 
        config: { 
          contextId: config.api.edge.clientContextId, 
          edgeUrl: config.api.edge.edgeUrl, 
          siteName: page.siteName || config.defaultSite, 
        }, 
        plugins: [ 
          analyticsPlugin({ 
            options: { 
              enableCookie: true, 
              cookieDomain: window.location.hostname.replace(/^www\./, ''), 
            }, 
            adapter: analyticsBrowserAdapter(), 
          }), 
          eventsPlugin(), 
        ], 
      }); 
    • Remove the following imports:

      import { CloudSDK } from '@sitecore-cloudsdk/core/browser';
      import { SitecorePageProps } from '@sitecore-content-sdk/nextjs'; 
      import '@sitecore-cloudsdk/events/browser'; 
    • Add the following imports:

      import { SitecorePageProps, initContentSdk } from '@sitecore-content-sdk/nextjs'; 
      import { eventsPlugin } from '@sitecore-content-sdk/events'; 
      import { analyticsBrowserAdapter, analyticsPlugin } from '@sitecore-content-sdk/analytics-core'; 
  2. In CdpPageView.tsx:

    • Find the following import:

      import { pageView } from '@sitecore-cloudsdk/events/browser'; 
    • Replace it with the following:

      import { pageView } from '@sitecore-content-sdk/events'; 

You might also have to make further changes to other files that contains older Cloud SDK logic based on your implementation. See Initializing tracking, events, and personalization in the Content SDK for more information.

Refactor aligned with React 19 best practices

React logic in the Content SDK has been refactored to follow React 19 best practices. The following section outlines the breaking changes introduced by this refactor and provides examples of the new implementations.

  1. The @sitecore-content-sdk/react package introduces the following updated types and signatures:

    • useSitecore(options?: UseSitecoreOptions): SitecoreProviderState

    • SitecoreProviderState now includes:

      • page: Page

      • api?: SitecoreProviderProps['api']

      • setPage?: (value: Page) => void (renamed from updatePage)

      • componentMap: ComponentMap (new)

      • loadImportMap: () => Promise<ImportMapImport> (new)

    • PlaceholderProps is expanded to explicitly list all supported properties:

      export interface PlaceholderProps {
        name: string;
        rendering: ComponentRendering | RouteData;
        componentMap?: ComponentMap;
        page?: Page;
        fields?: { [name: string]: Field | Item | Item[] };
        params?: { [name: string]: string };
        disableSuspense?: boolean;
        errorComponent?: React.ComponentClass<ErrorComponentProps> | React.FC<ErrorComponentProps>;
        hiddenRenderingComponent?: React.ComponentClass | React.FC;
        missingComponentComponent?: React.ComponentClass | React.FC;
        componentLoadingMessage?: string;
        renderEmpty?: (components: React.ReactNode[]) => React.ReactNode;
        renderEach?: (component: React.ReactNode, index: number) => React.ReactNode;
        render?: (components: React.ReactNode[], data: ComponentRendering[], props: PlaceholderProps) => React.ReactNode;
        passThroughComponentProps?: { [key: string]: unknown };
      }

    These updates make the context shape and placeholder behavior explicit and compatible with React 19 best practices, especially for server and client components. The additional fields (componentMap, loadImportMap) support more flexible composition and lazy loading. To update your code:

    • Review all imports and usages of SitecoreProviderState and PlaceholderProps.

    • If you relied on implicit properties, update your type annotations to the new explicit interface.

    • If you used updatePage, rename it to setPage.

    • If you previously inferred types from usage, consider importing the updated types from @sitecore-content-sdk/react to align with 2.0.

    No runtime changes are required in this step beyond adjusting renamed fields.

  2. The withPlaceholder higher-order component now only accepts the component as an argument and injects a placeholders prop derived from props.rendering.placeholders.

    To update your code:

    • Find implementations of a single placeholder:

      import { withPlaceholder, PlaceholderProps } from '@sitecore-content-sdk/react';
      type HomeProps = PlaceholderProps;
      const Home: React.FC<HomeProps> = ({ pageContent }) => (
        <div className="home-mock">{pageContent}</div>
      );
      export default withPlaceholder('page-content')(Home);
    • Replace it with the following:

      import { withPlaceholder } from '@sitecore-content-sdk/react';
      type HomeProps = {
        placeholders: Record<string, React.ReactNode>;
      };
      const Home: React.FC<HomeProps> = ({ placeholders }) => (
        <div className="home-mock">
          {placeholders['page-content']}
        </div>
      );
      export default withPlaceholder(Home);
      Note

      When rendering the wrapper on a server component in Next.js App Router while using withAppPlaceholder, you must still pass the rendering, page, and componentMap props:

      const WrappedHome = withAppPlaceholder(Home);
      <SitecoreProvider page={page} componentMap={componentMap} api={api}>
        <WrappedHome
          rendering={page.layout.sitecore.route}
          page={page}
          componentMap={componentMap}
        />
      </SitecoreProvider>;
    • If you rely on a multi-placeholder mapping as shown:

      type HomeProps = PlaceholderProps & {
        subProp?: React.ReactElement;
      };
      const Home: React.FC<HomeProps> = ({ subProp, pageContent }) => (
        <div>
          <header>{subProp}</header>
          <main>{pageContent}</main>
        </div>
      );
      export default withPlaceholder(
        [
          { placeholder: 'page-header', prop: 'subProp' },
          'page-content',
        ]
      )(Home);
    • Convert it as shown:

      type HomeProps = {
        placeholders: Record<string, React.ReactNode>;
      };
      const Home: React.FC<HomeProps> = ({ placeholders }) => (
        <div>
          <header>{placeholders['page-header']}</header>
          <main>{placeholders['page-content']}</main>
        </div>
      );
      export default withPlaceholder(Home);
  3. The withSitecore higher-order component is deprecated. The recommended alternative is the useSitecore hook.

    To update your code, find the current implementation:

    import {
      withSitecore,
      type SitecoreProviderState,
    } from '@sitecore-content-sdk/react';
    type Props = Partial<SitecoreProviderState> & { customProp: string };
    const MyComponent: React.FC<Props> = ({ page, api, setPage, customProp }) => {
      // ...
    };
    export default withSitecore({ updatable: true })(MyComponent);

    Replace it with the following:

    import { useSitecore } from '@sitecore-content-sdk/react';
    type Props = { customProp: string };
    const MyComponent: React.FC<Props> = ({ customProp }) => {
      const { page, api, setPage } = useSitecore({ updatable: true });
      // ...
    };
    Note

    If you continue using withSitecore, the updatePage prop it previously injected is now named setPage.

  4. Placeholder and AppPlaceholder are stricter and now pass only the following props to the components rendered for a placeholder:

    • Context properties (page, componentMap)

    • fields, params and rendering data

      fields: {
        [name: string]: Field<GenericFieldValue> | Item | Item[];
      };
      params: {
        [name: string]: string;
      };
      rendering: ComponentRendering;

    The modifyComponentProps callback is still available, but it should not be required for simple prop injection. For example, the following usage:

    <AppPlaceholder
      name="main"
      rendering={route}
      componentMap={components}
      page={page}
      modifyComponentProps={(props) =>
        { ...props, extraDiv: true }
      }
    />

    Can be streamlined using passThroughComponentProps as shown:

    <AppPlaceholder
      name="main"
      rendering={route}
      componentMap={components}
      page={page}
      passThroughComponentProps={{ extraDiv: true }}
    />
  5. All built-in Content SDK components that previously relied on React.forwardRef (such as RichText, Link, and others) now treat ref as a regular prop instead of using forwardRef. This change follows React 19 recommendations best practices.

Important

Please refer to the official React 19 upgrade guide to update other parts of your application that's not specific to the Content SDK.

Update Next.js template files

To update the Next.js template files:

  1. In sitecore.cli.config:

    • Previously, all functions in the build array of the defineCliConfig options object accepted a scConfig property in their options object. This property is no longer required and is provided internally during build time. For example, the previous implementation for writeImportMap contained the scConfig property as shown:

      import scConfig from './sitecore.config';
      import { defineCliConfig } from '@sitecore-content-sdk/nextjs/config-cli';
      import {
        ...
        writeImportMap,
      } from '@sitecore-content-sdk/nextjs/tools';
      
      
      export default defineCliConfig({
        config: scConfig,
        build: {
          commands: [
            ...
            writeImportMap({
              scCofig: scConfig,
              paths: ['src/components'],
            }),
          ],
        },
        ...
      });

      The rest of the configuration remains the same after removing the property:

      import scConfig from './sitecore.config';
      import { defineCliConfig } from '@sitecore-content-sdk/nextjs/config-cli';
      import {
        ...
        writeImportMap,
      } from '@sitecore-content-sdk/nextjs/tools';
      
      
      export default defineCliConfig({
        config: scConfig,
        build: {
          commands: [
            ...
            writeImportMap({
              paths: ['src/components'],
            }),
          ],
        },
        ...
      });
  2. The renderEmptyPlaceholder method from the Placeholder class has been removed.

    import React from 'react';
    import { Placeholder } from '@sitecore-content-sdk/react';
    
    class MyCustomPlaceholder extends Placeholder {
      render() {
        ...
        if (!placeholderRenderings.length) {
          ...
          return this.renderEmptyPlaceholder(emptyContent);
        }
        // Regular rendering logic
        return super.render();
      }
    }

    If your implementation relies on that method for custom placeholders, you can use the renderEmptyPlaceholder helper method exported from react and nextjs packages.

    import React from 'react';
    import { PlaceholderComponent, renderEmptyPlaceholder } from '@sitecore-content-sdk/react';
    
    class Placeholder extends Placeholder {
      render() {
        ...
        if (!placeholderRenderings.length) {
          ...
          return renderEmptyPlaceholder(emptyContent);
        }
    
        // Regular rendering logic
        return super.render();
      }
    }
  3. The DesignLibrary component no longer receives an import map as a prop. Instead, it is provided by the SitecoreProvider component. For Content SDK apps based on Pages Router, the DesignLibrary component is present in src/Layout.tsx and must be updated:

    ...
          <div className={mainClassPageEditing}>
            {mode.isDesignLibrary ? (
              <DesignLibrary />
            ) : (
              <>
                <header>
                  <div id="header">
    ...
  4. The SitecoreProvider component now requires the loadImportMap prop. Update the SitecoreProvider implementation as shown:

    import {
      ComponentPropsCollection,
      ComponentPropsContext,
      Page,
      SitecoreProvider,
    } from '@sitecore-content-sdk/nextjs';
    import components from '.sitecore/component-map';
    import scConfig from 'sitecore.config';
    
    const Providers = ({
      children,
      componentProps,
      page,
    }: {
      children: React.ReactNode;
      componentProps?: ComponentPropsCollection;
      page: Page;
    }) => {
      return (
        <ComponentPropsContext value={componentProps || {}}>
          <SitecoreProvider
            componentMap={components}
            api={scConfig.api}
            page={page}
            loadImportMap={() => import('.sitecore/import-map')}
          >
            {children}
          </SitecoreProvider>
        </ComponentPropsContext>
      );
    };
    
    export default Providers;
  5. If your application currently depends on SITECORE_EDGE_URL, remove all instances of SITECORE_EDGE_URL from env files and deployment configurations. Set the following new variable(s) to the same value and restart the app. Rebuild for Next.js so that NEXT_PUBLIC_* is in the client bundle.

    • NEXT_PUBLIC_SITECORE_EDGE_PLATFORM_HOSTNAME - for Next.js projects so both server and client use the custom host.

    • SITECORE_EDGE_PLATFORM_HOSTNAME - non Next.js or server only

    Note

    The default value of <https://edge-platform.sitecorecloud.io> is used unless you use the new variables.

    If you have explicitly configured edgeUrl in sitecore.config, set it to the following:

    edgeUrl: process.env.NEXT_PUBLIC_SITECORE_EDGE_PLATFORM_HOSTNAME || 'https://edge-platform.sitecorecloud.io',

    For non‑Next.js environments, mirror this pattern with the SITECORE_EDGE_PLATFORM_HOSTNAME variable instead.

    Note

    Newly scaffolded apps use defineConfig({}) and get edgeUrl from the new environment variable automatically, so you might not have the edgeUrl section under edge.

  6. A new configuration option, rewriteMediaUrls is available in sitecore.config. When set to true, the SDK inspects media URLs in the layout that are pointing to the production Experience Edge host (edge.sitecorecloud.io), and rewrites those URLs to use your custom host defined in SITECORE_EXPERIENCE_EDGE_HOSTNAME. The rewrite only targets the production Experience Edge host.

  7. 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.

  8. The @sitecore-content-sdk/content package has been decoupled from @sitecore-content-sdk/core to create a more focused "core" that can be referenced by other packages. The following changes are only required if you directly import from @sitecore-content-sdk/core.

    • The following submodules have been moved from @sitecore-content-sdk/core to @sitecore-content-sdk/content:

      Submodule

      Old path

      New path

      client

      @sitecore-content-sdk/core/client

      @sitecore-content-sdk/content/client

      codegen

      @sitecore-content-sdk/core/codegen

      @sitecore-content-sdk/content/codegen

      config-cli

      @sitecore-content-sdk/core/config-cli

      @sitecore-content-sdk/content/config-cli

      config

      @sitecore-content-sdk/core/config

      @sitecore-content-sdk/content/config

      editing

      @sitecore-content-sdk/core/editing

      @sitecore-content-sdk/content/editing

      i18n

      @sitecore-content-sdk/core/i18n

      @sitecore-content-sdk/content/i18n

      layout

      @sitecore-content-sdk/core/layout

      @sitecore-content-sdk/content/layout

      media

      @sitecore-content-sdk/core/media

      @sitecore-content-sdk/content/media

      personalize

      @sitecore-content-sdk/core/personalize

      @sitecore-content-sdk/content/personalize

      site

      @sitecore-content-sdk/core/site

      @sitecore-content-sdk/content/site

      Update all references to the old paths with the new ones.

    • All exports from the utils submodule are now available from the tools submodule. Update all references to the @sitecore-content-sdk/core/utils submodule and replace it with @sitecore-content-sdk/core/tools.

    • The following types and functions have been moved from @sitecore-content-sdk/core to @sitecore-content-sdk/content:

      Item

      Old path

      New path

      HTMLLink type

      @sitecore-content-sdk/core

      @sitecore-content-sdk/content

      StaticPath type

      @sitecore-content-sdk/core

      @sitecore-content-sdk/content

      defineConfig

      @sitecore-content-sdk/core

      @sitecore-content-sdk/content

      form

      @sitecore-content-sdk/core

      @sitecore-content-sdk/content

      Update all references to the old paths with the new ones.

    • The following tools have been moved from @sitecore-content-sdk/core/tools to @sitecore-content-sdk/content/tools:

      Item

      Old path

      New path

      generateSites

      @sitecore-content-sdk/core/tools

      @sitecore-content-sdk/content/tools

      GenerateSitesConfig

      @sitecore-content-sdk/core/tools

      @sitecore-content-sdk/content/tools

      GenerateMapFunction

      @sitecore-content-sdk/core/tools

      @sitecore-content-sdk/content/tools

      GenerateMapArgs

      @sitecore-content-sdk/core/tools

      @sitecore-content-sdk/content/tools

      extractFiles

      @sitecore-content-sdk/core/tools

      @sitecore-content-sdk/content/tools

      writeImportMap

      @sitecore-content-sdk/core/tools

      @sitecore-content-sdk/content/tools

      WriteImportMapArgs

      @sitecore-content-sdk/core/tools

      @sitecore-content-sdk/content/tools

      Update all references to the old paths with the new ones.

    • The following utilities and types have been removed from @sitecore-content-sdk/core/tools as they are no longer relevant or unused:

      Item

      Description

      generatePlugins

      No longer relevant

      PluginDefinition

      No longer relevant

      ModuleType

      No longer relevant

      tryParseEnvValue

      Unused utility

      isAbsoluteUrl

      Unused utility

Important

The default SXA rewrite logic has been changed to align with Next.js best practices for locale and to support the optional Shall language be preserved upon redirect? checkbox introduced in the Redirect Rules section in SitecoreAI. For example, earlier, when en is set as the default locale, URLs that do not specify a locale (like /my-page) resolves to that default locale. With this change, the same rule results in a redirect without the default locale if no locale is present. To continue using it as before, you must enable the Shall language be preserved upon redirect?.

Next steps

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

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