Using State Transfer in Angular 17 SSR

Saikiranmaddukuri
3 min readNov 22, 2023

Please refer to my old article on SSR before reading this for better understanding.

State Transfer

A key value store that is transferred from the application on the server side to the application on the client side.

In SSR when you make API calls on the server the data will be rendered and binded to the HTML, that HTML template will be transfered to the client and rendering happens again.

We can use isPlatformServer to restrict that to run on server only. but when it rerender on the client the states get reinitated and the data will stays default to get the rendered data on the server we have to set the data to State Transfer on the server part and get the data on the client part from State Transfer.

I am having an API hosting on localhost:8080 which return static json data({data:’hello’}) for the below code.

import { Component, Inject, OnInit, PLATFORM_ID } from '@angular/core';
import { CommonModule, isPlatformServer } from '@angular/common';
import { RouterOutlet } from '@angular/router';
import { HttpClient, HttpClientModule } from '@angular/common/http';

@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, RouterOutlet, HttpClientModule],
template: `{{bindingData?.data}}`,
styles: []
})
export class AppComponent implements OnInit{
bindingData?:{data:string};
constructor(@Inject(PLATFORM_ID)private platformID: Object, private httpClient: HttpClient){

}
ngOnInit(): void {
if(isPlatformServer(this.platformID)){
this.httpClient.get("http://localhost:8080/data").subscribe((r:any)=>{
this.bindingData=r;
console.log("data is rendered",r);
})
}
}
}

and in the browser it first loads.

and renders blank page while loading the page and data will be reinitiated after loading so bindingData wil be reinitated to undefined and we see a blank page.

in order to solve this we use state transfer for the below fallowing code.

import { Component, Inject, OnInit, PLATFORM_ID, TransferState, makeStateKey } from '@angular/core';
import { CommonModule, isPlatformBrowser, isPlatformServer } from '@angular/common';
import { RouterOutlet } from '@angular/router';
import { HttpClient, HttpClientModule } from '@angular/common/http';

const dataKey = makeStateKey<{data:string}>("data")

@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, RouterOutlet, HttpClientModule],
template: `{{bindingData?.data}}`,
styles: []
})
export class AppComponent implements OnInit{
bindingData?:{data:string};
constructor(@Inject(PLATFORM_ID) private platformID: Object, private httpClient: HttpClient,private transferState:TransferState){

}
ngOnInit(): void {
if(isPlatformServer(this.platformID)){ //<--- this block run on server
console.log("this block runs only on server");
this.httpClient.get("http://localhost:8080/data").subscribe((r:any)=>{
this.bindingData=r;
this.transferState.set(dataKey,r); //<--- add this line to save the state
console.log("data is rendered",r);
})
}
else if(isPlatformBrowser(this.platformID)){
console.log("this block runs only on client");
console.log("client site retrived data before ",this.bindingData)
this.bindingData=this.transferState.get<{data:string}>(dataKey,{data:""});
console.log("client site retrived data after ",this.bindingData)
}
}
}

the output on the server side will be.

and on the browser

Reference:

https://angular.io/api/core/TransferState

--

--