---
title: "Upgrade JSS apps to JSS 22.10"
slug: "upgrade-jss-apps-to-jss-22-10"
abstract: "How to upgrade your JavaScript Rendering SDKs to version 22.10 to benefit from the latest improvements, including the update to Angular 20."
---
# Upgrade JSS apps to JSS 22.10

Sitecore JSS version 22.10 primarily focuses on support for Angular 20. For Angular apps, you will need to update multiple dependencies. In general, the only other requirement is to update and install core JSS dependencies before re-testing your apps.

> [!note]
> **Before you begin**
> -   If you haven't already done so, [upgrade your app to JSS 22.9](https://doc.sitecore.com/xp/en/developers/hd/22/sitecore-headless-development/upgrade-jss-apps-to-jss-22-9.html).
>
> -   Since this process also involves updating Angular to version 20, you'll need to refer to the [official guide](https://angular.dev/update-guide?v=18.0-20.0&l=3) to complete the upgrade process for your apps.
>
> -   Familiarize yourself with the [JSS 22.10](https://github.com/Sitecore/jss/blob/v22.10.0/CHANGELOG.md#22100) changelog to ensure you understand the implications of upgrading.

This topic describes how to:

1. [Update the Angular template files in your existing app](https://doc.sitecore.com/xp/en/developers/hd/22/sitecore-headless-development/upgrade-jss-apps-to-jss-22-10.html#UUID-31b752e0-398f-289d-be4d-ce2723210cae_section-id235281237141058 "Update the Angular template files in your existing app")
2. [Update application dependencies in your existing app](https://doc.sitecore.com/xp/en/developers/hd/22/sitecore-headless-development/upgrade-jss-apps-to-jss-22-10.html#UUID-31b752e0-398f-289d-be4d-ce2723210cae_section-id235281233363217 "Update application dependencies in your existing app")

## Update the Angular template files in your existing app

This version of JSS upgrades Angular to version 20 that switches your components to be standalone by default. You should refer to the [official Angular guide](https://angular.dev/update-guide?v=18.0-20.0&l=3) to complete the upgrade process for your apps and familiarize with standalone components and relevant [migrations](https://v20.angular.dev/reference/migrations).

To upgrade your JSS app to Angular 20:

1. In your existing application's `package.json` file, update the following dependencies to the specified versions:

    ```

    "@angular/animations": "~20.3.10",
    "@angular/common": "~20.3.10",
    "@angular/compiler": "~20.3.10",
    "@angular/core": "~20.3.10",
    "@angular/forms": "~20.3.10",
    "@angular/platform-browser": "~20.3.10",
    "@angular/platform-browser-dynamic": "~20.3.10",
    "@angular/platform-server": "~20.3.10",
    "@angular/router": "~20.3.10",
    ....
    "apollo-angular": "~11.0.0",
    ...
    "graphql": "~16.11.0",
    ...
    "zone.js": "~0.15.0",
    ...
    "@angular-builders/custom-webpack": "^20.0.0",
    "@angular-devkit/build-angular": "^20.3.9",
    "@angular-eslint/builder": "^20.5.1",
    "@angular-eslint/eslint-plugin": "^20.5.1",
    "@angular-eslint/eslint-plugin-template": "^20.5.1",
    "@angular-eslint/schematics": "^20.5.1",
    "@angular-eslint/template-parser": "^20.5.1",
    "@angular/cli": "~20.3.9",
    "@angular/compiler-cli": "~20.3.10",
    "@angular/language-service": "~20.3.10",
    ...
    "typescript": "~5.8.0"
    ```

2. In the `src/app` folder:
    - Add a new file called `ViewBag.ts` with the following content:

        ```ts
        export interface ViewBag {
            [key: string]: unknown;
            dictionary: { [key: string]: string };
        }
        ```

    - Add a new file called `injection-tokens.ts` with the following content:

        ```ts
        import { InjectionToken } from '@angular/core';
        import { InMemoryCache } from '@apollo/client/core';
        import { JssState } from './JssState';
        import { ViewBag } from './ViewBag';

        /**
         * Injection token for server-side layout data provided by server.bundle
         */
        export const JSS_SERVER_LAYOUT_DATA = new InjectionToken<JssState>('JSS_SERVER_LAYOUT_DATA');

        export const JSS_SERVER_VIEWBAG = new InjectionToken<ViewBag>('JSS_SERVER_VIEWBAG');

        export const APOLLO_CACHE = new InjectionToken<InMemoryCache>('APOLLO_CACHE');
        ```

3. Use the `inject()` method to to replace field initializations in the constructor for all instances of class fields.

    For example, in `src/app/jss-context.service.ts`, the original constructor is as shown:

    ```ts
    constructor(protected transferState: TransferState, protected layoutService: JssLayoutService, protected stateService: JssStateService<JssState>) {}
    ```

    To use the `inject()` method here:

    - Import the method from the `angular` package:

        ```ts
        import { Injectable, TransferState, makeStateKey, inject } from '@angular/core';
        ```

    - Use the `inject()` function to remove the field initializations from the constructor as shown:

        ```ts
        protected transferState = inject(TransferState);
        protected layoutService = inject(JssLayoutService);
        protected stateService = inject(JssStateService<JssState>);
        ```

    - Repeat the above steps for the rest of the app logic like components, services (for example, `src/app/jss-context.server.ts`, `jss-graphql.service.ts` files), etc.
4. For all the modules using `declarations`, move the entries into `imports` instead.
    - For example, in `src/app/routing/routing.module.ts`, the existing declarations are as shown:

        ```ts
        @NgModule({
          imports: [
            ....
          ],
          exports: [...],
          declarations: [NotFoundComponent, ServerErrorComponent, LayoutComponent, NavigationComponent],
        ```

        Move the entries to the imports as shown:

        ```ts
        @NgModule({
          imports: [
            ...
            LayoutComponent,
            NotFoundComponent,
            ServerErrorComponent,
          ],
          exports: [RouterModule, TranslateModule],
          ...
        })
        ```

    - Similarly, in `src/app/routing/scripts/scripts.module.ts`, move the `ScriptsComponent` and `VisitorIdentificationComponent` (if present) from `declarations` to `imports` as shown:

        ```csharp
        @NgModule({
          exports: [ScriptsComponent],
          imports: [ScriptsComponent, VisitorIdentificationComponent],
        })
        ```

    - Perform the same step for `StyleguideSpecimenComponent` in `src/app/components/app-components.shared.module.ts` and any other component modules you wish to have as standalone.
5. The `app.component.ts` file will remain non-standalone for simplicity and continued support of SSR proxy. Make the following changes to the `app.component.ts ` file:
    - Mark the component with `standalone: false` as shown

        ```ts
        @Component({
          selector: 'app-root',
          templateUrl: './app.component.html'
          templateUrl: './app.component.html',
          /* eslint-disable-next-line @angular-eslint/prefer-standalone */
          standalone: false,
        })
        ```

    - Implement the `OnInit` interface

        ```ts
        export class AppComponent implements OnInit, OnDestroy {
        ```

    - Add the `ngInit()` method:

        ```ts
        ngOnInit() {
        this.contextSubscription = this.jssContextService.state.subscribe((jssState: { language: string }) => {
            // listen for language changes
            if (jssState.language) {
            this.translate.use(jssState.language);
            }
        });
        }
        ```

    - Remove the original constructor and initialize fields with the `inject()` method:

        ```ts
        ...
        private contextSubscription: Subscription;
        private translate = inject(TranslateService);
        private jssContextService = inject(JssContextService);
        ...
        ```

    - Making the app-component non-standalone allows you to keep the `declarations` section in `app.module.ts` as is, but `app.server.module.ts` should be modified to use an injection token.

        Find the existing string token:

        ```csharp
        TranslateModule.forRoot({
              ....
                deps: ['JSS_SERVER_VIEWBAG', TransferState],
              ....
            }),
        ```

        Replace it with an imported token object:

        ```ts
        import { JSS_SERVER_VIEWBAG } from './src/app/injection-tokens';
        ....
        ....
        TranslateModule.forRoot({
              ...
                deps: [JSS_SERVER_VIEWBAG, TransferState],
              ...
            }),
        ```

6. Use injection tokens in the `server.bundle.ts` file:
    - Import the injection tokens:

        ```ts
        import { JSS_SERVER_LAYOUT_DATA, JSS_SERVER_VIEWBAG } from './src/app/injection-tokens';
        ```

    - Add the injection tokens to the `extraProviders` section of the `renderModule` function as shown:

        ```ts
        renderModule(AppServerModule, {
              document: template,
              url: path,
              extraProviders: [
                // custom injection with the initial state that SSR should utilize
                { provide: JSS_SERVER_LAYOUT_DATA, useValue: transferState },
                { provide: JSS_SERVER_VIEWBAG, useValue: state.viewBag },
              ],
            })
        ```

7. In the app's `src/app/routing/layout/layout.component.ts` file:
    - Ensure the following imports are present:

        ```ts
        import { Component, OnInit, OnDestroy, inject } from '@angular/core';
        import {
          RouteData,
          Field,
          LayoutServiceContextData,
          getContentStylesheetLink,
          JssModule,
        } from '@sitecore-jss/sitecore-jss-angular';
        ...
        import { NotFoundComponent } from '../not-found/not-found.component';
        import { ServerErrorComponent } from '../server-error/server-error.component';
        import { ScriptsComponent } from '../scripts/scripts.component';
        ...
        import { CommonModule } from '@angular/common';
        ```

    - Add an `imports` section for the `Component` directive:

        ```ts
        @Component({
          selector: 'app-layout',
          templateUrl: './layout.component.html',
          imports: [CommonModule, JssModule, NotFoundComponent, ServerErrorComponent, ScriptsComponent],
        })
        ```

    - Use the `inject()` function to remove the field initializations from the constructor as shown:

        ```ts
        private activatedRoute = inject(ActivatedRoute);
        private readonly meta = inject(JssMetaService);
        private linkService = inject(JssLinkService);
        ```

8. In the app's `jss-graphql.module.ts` file:
    - Import `inject` from the `angular` package, replace `ApolloModule` with `provideApollo` from the `apollo-angular` package, and import the `APOLLO_CACHE` injection token as shown:

        ```ts
        import { makeStateKey, TransferState, NgModule, PLATFORM_ID, inject } from '@angular/core';
        ....
        import { Apollo, provideApollo } from 'apollo-angular';
        ...
        import { APOLLO_CACHE } from './injection-tokens';
        ```

    - Add the following two new function outside of `GraphQLModule`:

        ```ts
        const getGraphQLCache = () => {
          const possibleTypes = {} as PossibleTypesMap;
          introspectionQueryResultData.__schema.types.forEach((supertype) => {
            possibleTypes[supertype.name] = supertype.possibleTypes.map((subtype) => subtype.name);
          });
          return new InMemoryCache({
            possibleTypes,
          });
        };

        const getApolloOptions = () => {
          const httpLink = inject(HttpBatchLink);
          const platformId = inject(PLATFORM_ID);
          /*
          QUERY LINK SELECTION
          A link is transport which GraphQL queries are pushed across.
          You have many choices.
          See the apollo-link documentation for more details.
        */

          // set sc_apikey header which is required for any GraphQL calls
          const sc_apikey = new HttpHeaders().set('sc_apikey', environment.sitecoreApiKey);

          // choose between a basic HTTP link to run queries...
          // import { createHttpLink } from 'apollo-angular-link-http';
          // const link = createHttpLink({ uri: endpoint });

          // ...or a batched link (multiple queries within 10ms all go in one HTTP request)
          const batchHttp = httpLink.create({
            uri: environment.graphQLEndpoint,
            headers: sc_apikey,
          });

          const cache = inject(APOLLO_CACHE);

          return {
            link: batchHttp,
            cache,
            ssrMode: isPlatformServer(platformId),
            ssrForceFetchDelay: 100,
          };
        }
        ```

    - Remove `ApolloModule` from `imports` and expand the `providers` section as shown:

        ```ts
        @NgModule({
          imports: [
            HttpClientModule, // provides HttpClient for HttpLink
          ],
          providers: [
            JssGraphQLService,
            { provide: APOLLO_CACHE, useValue: getGraphQLCache() },
            provideApollo(getApolloOptions),
          ],
        })
        ```

    - Modify the module constructor as shown:

        ```ts
        constructor() {
          this.addCacheEvents();
        }
        ```

    - Initialize two module fields with the `inject()` function:

        ```ts
        private readonly transferState = inject(TransferState);
        apolloClient = inject(Apollo);
        ```

    - Replace the `createApolloClient` method with the following:

        ```ts
        private addCacheEvents(): void {
            const cache = inject(APOLLO_CACHE);

            const isBrowser = this.transferState.hasKey(STATE_KEY);

            if (isBrowser) {
                this.onBrowser(cache);
            } else {
                this.onServer(cache);
            }
        }
        ```

9. In the app's `src/app/jss-graphql.service.ts` file, use the `inject()` function to remove the field initializations from the constructor as shown:

    ```ts
    protected apollo = inject(Apollo);
    protected sitecoreContext = inject(JssContextService);
    protected platformId = inject(PLATFORM_ID);
    ```

10. You can optionally replace all usages of `ngIf` and `ngFor` in your components with the new `@if` and `@for` directives. See the updated [Angular sample](https://github.com/Sitecore/jss/tree/v22.10.0/packages/sitecore-jss-angular) for examples on how to do it. To continue using `NgIf` and `NgFor` with your standalone components, import `ngIf` and `ngFor` from the `@angular/core package` and add them to your component’s `imports` section:

    ```csharp
    @Component({
      ...
      imports: [NgIf, NgFor, ....],
    })
    export class MyComponent implements .... {...
    ```

## 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` file, update every `@sitecore-jss` package to version `~22.10.0`.
2. Install the dependencies with the following command:

    ```text
    npm install
    ```