Server-Side Rendering
Note: Spartacus 4.x is no longer maintained. Please upgrade to the latest version.
Note: Spartacus 4.x was tested with SAP Commerce Cloud versions 1905 to 2205. Spartacus 4.x has not been verified to work with (and is not guaranteed to work with) SAP Commerce Cloud 2211 or later releases.
In Spartacus, server-side rendering allows you to render static versions of pages on the server side. This speeds up response times, assists with SEO, and allows the application to render more quickly. After Angular has bootstrapped, users of your site will have the full experience.
Table of Contents
- Adding SSR Support Using Schematics (Recommended)
- Adding SSR Support Manually
- Installation Steps for Internal Spartacus Development
Adding SSR Support Using Schematics (Recommended)
The recommended way to add SSR support to your Spartacus application is to use schematics. With a single command, all required files are added automatically, and all modifications for SSR support are done automatically as well. To add SSR support to your Spartacus application, run the following command:
ng add @spartacus/schematics --ssr
You have now added SSR support to your Spartacus application. No further steps are required.
If you experience any issues, see Troubleshooting for more information.
Adding SSR Support Manually
For most situations and setups, is is best to add SSR support to your Spartacus application using schematics, as described in the previous section. However, if you are unable to add SSR support using schematics, the following steps describe how to manually add SSR support so that your Spartacus shell app includes the Spartacus libraries running in SSR mode.
-
Add the following dependencies to
package.json
:"@angular/platform-server": "~10.1.0", "@nguniversal/express-engine": "^10.1.0", "@spartacus/setup": "^3.0.0-rc.2", "express": "^4.15.2"
-
Add the following developer dependencies to
package.json
:"ts-loader": "^6.0.4", "@nguniversal/builders": "^10.1.0", "@types/express": "^4.17.0",
-
For convenience, add the following scripts to
package.json
:"e2e": "ng e2e", "dev:ssr": "ng run <your-project-name>:serve-ssr", "serve:ssr": "node dist/<your-project-name>/server/main.js", "build:ssr": "ng build --prod && ng run <your-project-name>:server:production", "prerender": "ng run <your-project-name>:prerender"
-
Update the
src/main.ts
file, as follows://from platformBrowserDynamic().bootstrapModule(AppModule); //to document.addEventListener("DOMContentLoaded", () => { platformBrowserDynamic().bootstrapModule(AppModule); });
-
Update the
app.module.ts
file, as follows://from: BrowserModule, //to BrowserModule.withServerTransition({ appId: 'spartacus-app' }),
-
Additionally, update the
app.module.ts
file, as follows://from import { BrowserModule } from '@angular/platform-browser'; //to import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser';
-
Also in the
app.module.ts
file, addBrowserTransferStateModule
to theimports
array of the@NgModule
decorator. -
In the
src/index.html
file, add the following meta attribute, and replaceOCC_BACKEND_BASE_URL_VALUE
with the URL of your back end instance, as follows:<meta name="occ-backend-base-url" content="OCC_BACKEND_BASE_URL_VALUE" />
-
Update
projects.<your-project-name>.architect.build.options
, as follows://from "outputPath": "dist/<your-project-name>", //to "outputPath": "dist/<your-project-name>/browser",
-
In
projects.<your-project-name>.architect.lint.options.tsConfig
, add the following line:"tsconfig.server.json"
-
In
projects.<your-project-name>.architect
, add the following configuration to your existingangular.json
file:"server": { "builder": "@angular-devkit/build-angular:server", "options": { "outputPath": "dist/<your-project-name>/server", "main": "server.ts", "tsConfig": "tsconfig.server.json" }, "configurations": { "production": { "outputHashing": "media", "fileReplacements": [ { "replace": "src/environments/environment.ts", "with": "src/environments/environment.prod.ts" } ], "sourceMap": false, "optimization": true } } }, "serve-ssr": { "builder": "@nguniversal/builders:ssr-dev-server", "options": { "browserTarget": "<your-project-name>:build", "serverTarget": "<your-project-name>:server" }, "configurations": { "production": { "browserTarget": "<your-project-name>:build:production", "serverTarget": "<your-project-name>:server:production" } } }, "prerender": { "builder": "@nguniversal/builders:prerender", "options": { "browserTarget": "<your-project-name>:build:production", "serverTarget": "<your-project-name>:server:production", "routes": [ "/" ] }, "configurations": { "production": {} } }
Note: In the above example, remember to replace the string
"<your-project-name>"
with your project name (such asmystore
, for example). -
Add the
tsconfig.server.json
file to your existing shell app. The following is an example:{ "extends": "./tsconfig.app.json", "compilerOptions": { "outDir": "./out-tsc/server", "target": "es2016", "types": [ "node" ] }, "files": [ "src/main.server.ts", "server.ts" ], "angularCompilerOptions": { "entryModule": "./src/app/app.server.module#AppServerModule" } }
-
Add the
src/main.server.ts
file to your existing shell app. The following is an example:/** * Load `$localize` onto the global scope - used if i18n tags appear in Angular templates. */ import '@angular/localize/init'; import { enableProdMode } from "@angular/core"; import { environment } from "./environments/environment"; if (environment.production) { enableProdMode(); } export { AppServerModule } from './app/app.server.module'; export { renderModule, renderModuleFactory } from '@angular/platform-server';
-
Add the
src/app/app.server.module
file to your existing shell app. The following is an example:import { NgModule } from "@angular/core"; import { ServerModule, ServerTransferStateModule, } from "@angular/platform-server"; import { AppModule } from "./app.module"; import { AppComponent } from "./app.component"; @NgModule({ imports: [ // The AppServerModule should import your AppModule followed // by the ServerModule from @angular/platform-server. AppModule, ServerModule, ServerTransferStateModule, ], bootstrap: [AppComponent], }) export class AppServerModule {}
For more information about caching and transfer state, see Caching the Site Context with Server-Side Rendering.
-
Add the
server.ts
file to your existing shell app. The following is an example:/** * Load `$localize` onto the global scope - used if i18n tags appear in Angular templates. */ import '@angular/localize/init'; import 'zone.js/dist/zone-node'; import { ngExpressEngine as engine } from '@nguniversal/express-engine'; import { NgExpressEngineDecorator } from '@spartacus/setup/ssr'; import * as express from 'express'; import { join } from 'path'; import { AppServerModule } from './src/main.server'; import { APP_BASE_HREF } from '@angular/common'; import { existsSync } from 'fs'; const ngExpressEngine = NgExpressEngineDecorator.get(engine); // The Express app is exported so that it can be used by serverless Functions. export function app() { const server = express(); const distFolder = join(process.cwd(), 'dist/<your-project-name>/browser'); const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index'; server.engine( 'html', ngExpressEngine({ bootstrap: AppServerModule, }) ); server.set('view engine', 'html'); server.set('views', distFolder); // Serve static files from /browser server.get( '*.*', express.static(distFolder, { maxAge: '1y', }) ); // All regular routes use the Universal engine server.get('*', (req, res) => { res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }], }); }); return server; } function run() { const port = process.env.PORT || 4000; // Start up the Node server const server = app(); server.listen(port, () => { console.log(`Node Express server listening on http://localhost:${port}`); }); } // Webpack will replace 'require' with '__webpack_require__' // '__non_webpack_require__' is a proxy to Node 'require' // The below code is to ensure that the server is run only when not requiring the bundle. declare const __non_webpack_require__: NodeRequire; const mainModule = __non_webpack_require__.main; const moduleFilename = (mainModule && mainModule.filename) || ''; if (moduleFilename === __filename || moduleFilename.includes('iisnode')) { run(); } export * from './src/main.server';
Note: In the above example, remember to replace the string
"<your-project-name>"
with your project name (such asmystore
, for example). -
Build the SSR version of your Spartacus shell app by running the following command:
yarn run build:ssr && yarn run serve:ssr
Installation Steps for Internal Spartacus Development
If you are involved in Spartacus internal development (for example, if you are contributing to the Spartacus core libraries), or if you wish to submit a pull request, you can perform the following steps, which describe how to run Spartacus in SSR mode using the Spartacus storefront app.
Note: You do not need to follow the steps in this section if your intention is to add SSR support to your Spartacus application. You can do that simply by running the schematics command, as described in Adding SSR Support Using Schematics (Recommended).
-
Set the production server endpoint in your
environment.prod.ts
(dev mode) orapp.module.ts
(shell app mode), as follows:environment = { occBaseUrl: 'https://[your_occ_endpoint]', };
-
Turn PWA off.
As soon as Spartacus is installed in PWA mode, a service worker is installed, and it serves a cached version of
index.html
, along with thejs
files. This results in SSR being completely skipped. The following steps describe how to turn off PWA:-
Check that there are no service workers registered in your app. If you do find any service workers, remove them.
-
Turn PWA off in your app module configuration, as follows:
StorefrontModule.withConfig({ backend: { occ: { baseUrl: 'https://[your_enpdoint], }, }, pwa: { enabled: false, }, };
-
-
Rebuild your local Spartacus libraries by running the following command:
yarn build:core:lib
-
Build your local Spartacus shell app by running the following command:
yarn build --prod
-
Build the SSR version of your shell app by running the following command:
yarn build:ssr
-
Start Spartacus with the SSR server by running the following command:
yarn serve:ssr