How to handle browser storage in Angular SSR?

Sumit kumar Singh
Web Development with sumit
4 min readJan 12, 2024

--

Introduction

Angular Server-Side Rendering (SSR) brings additional challenges compared to traditional client-side rendering, especially when dealing with browser storage. In this comprehensive guide, we’ll explore the intricacies of handling browser storage in an Angular SSR application. We’ll cover the basics of SSR, the challenges posed by browser storage, and various strategies to overcome them. Throughout this journey, we’ll delve into code examples and best practices to ensure a seamless and performant user experience.

Understanding Angular SSR

Angular SSR is a technique that allows rendering Angular applications on the server side, providing benefits such as improved performance, search engine optimization (SEO), and better initial load times. During SSR, the application is rendered on the server and the resulting HTML is sent to the client, where the Angular application is bootstrapped.

Key Concepts of Angular SSR:

  1. Server-Side Rendering (SSR): The process of rendering Angular components on the server rather than in the browser.
  2. Universal Rendering: The term often used synonymously with SSR in the Angular context.
  3. PlatformServer: The Angular module that facilitates server-side rendering.
  4. TransferState: A service provided by Angular to transfer state from the server to the client during SSR.

Challenges with Browser Storage in SSR

Browser storage, encompassing mechanisms like localStorage and sessionStorage, poses unique challenges in the context of SSR. Traditionally, these storage mechanisms are client-side, but with SSR, there’s a disconnect between the server rendering the initial page and the client taking over.

Challenges:

  1. No Access to Window Object: The absence of a window object during SSR restricts direct access to browser storage.
  2. Data Synchronization: Ensuring synchronization between server-rendered content and client-side storage is crucial.
  3. Security Concerns: Handling sensitive data in storage introduces security concerns, especially during SSR.

Strategies for Handling Browser Storage in SSR

To address the challenges, developers can adopt various strategies, combining server-side and client-side logic. Let’s explore these strategies in-depth, along with code examples.

1. Use TransferState for Initial Data Transfer:

Angular’s TransferState provides a mechanism to transfer state from the server to the client. Leveraging TransferState, developers can transfer data needed for initializing client-side storage.

// Server-Side Rendering (Angular Universal)
import { TransferState } from '@angular/platform-browser';

app.get('*', (req, res) => {
const state = { /* Initial state data */ };
const transferState = new TransferState();
transferState.setState(state);

// Render Angular application with SSR
renderAngularApp(req, res, transferState);
});
// Client-Side (Angular)
import { TransferState } from '@angular/platform-browser';

export class AppComponent implements OnInit {
constructor(private transferState: TransferState) {}

ngOnInit() {
// Retrieve initial state transferred from the server
const initialState = this.transferState.get<any>(STATE_KEY, null);

// Use initialState to initialize client-side storage
// ...
}
}

2. Conditional Client-Side Execution:

Use Angular’s isPlatformBrowser and isPlatformServer to conditionally execute code based on the platform. This ensures that certain logic is only executed on the client side.

import { PLATFORM_ID, Inject } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';

// ...

constructor(@Inject(PLATFORM_ID) private platformId: Object) {}

ngOnInit() {
if (isPlatformBrowser(this.platformId)) {
// Code to execute only on the client side
// ...
}
}

3. Custom Storage Service:

Implement a custom service that abstracts browser storage access. This service can handle platform-specific logic and ensure a consistent interface for storage operations.

import { Injectable, PLATFORM_ID, Inject } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';

@Injectable({
providedIn: 'root',
})
export class BrowserStorageService {
private storage: Storage;

constructor(@Inject(PLATFORM_ID) private platformId: Object) {
this.storage = isPlatformBrowser(this.platformId) ? localStorage : null;
}

getItem(key: string): string | null {
return this.storage ? this.storage.getItem(key) : null;
}

setItem(key: string, value: string): void {
if (this.storage) {
this.storage.setItem(key, value);
}
}

// Additional storage methods...
}

4. Lazy Loading and Asynchronous Initialization:

Delay accessing browser storage until after the Angular application has fully loaded on the client side. Use Angular’s APP_INITIALIZER to asynchronously initialize storage.

import { InjectionToken } from '@angular/core';
import { APP_INITIALIZER } from '@angular/core';

export const STORAGE_INITIALIZATION = new InjectionToken<(() => void)[]>('STORAGE_INITIALIZATION');

export function initializeStorage(storageService: BrowserStorageService): () => void {
return () => {
// Initialize storage asynchronously
return storageService.initialize();
};
}

@NgModule({
providers: [
{
provide: APP_INITIALIZER,
useFactory: initializeStorage,
deps: [BrowserStorageService],
multi: true,
},
],
})
export class AppInitializerModule {}
// BrowserStorageService
initialize(): Promise<void> {
return new Promise<void>((resolve) => {
// Asynchronously initialize storage
// ...

resolve();
});
}

5. Secure Data Handling:

Implement encryption or tokenization for sensitive data stored on the client side. Ensure that sensitive information is appropriately protected, even when stored in client-side storage.

// Encryption example using CryptoJS
import * as CryptoJS from 'crypto-js';

const secretKey = 'your-secret-key';

const encryptedData = CryptoJS.AES.encrypt('sensitive-data', secretKey).toString();

const decryptedData = CryptoJS.AES.decrypt(encryptedData, secretKey).toString(CryptoJS.enc.Utf8);

Best Practices

To ensure robust handling of browser storage in Angular SSR, adhere to the following best practices:

1. Encapsulate Storage Logic: Encapsulate browser storage logic within a dedicated service to ensure a modular and maintainable codebase.

2. Use Platform Checks: Leverage isPlatformBrowser and isPlatformServer for platform-specific checks to prevent the execution of inappropriate code.

3. Secure Sensitive Data: Implement encryption or tokenization for sensitive data stored on the client side to enhance security.

4. Lazy Loading for Performance: Implement lazy loading and asynchronous initialization to avoid blocking the application’s main thread during startup.

5. Consider Storage Limitations: Be mindful of storage limitations, especially when dealing with large datasets. Consider compression or pagination for efficient storage.

Conclusion

Handling browser storage in Angular SSR requires a thoughtful approach that combines server-side and client-side strategies. By understanding the challenges posed by SSR, adopting appropriate strategies, and following best practices, developers can ensure a seamless and performant user experience. The provided code examples illustrate how to implement these strategies effectively. In conclusion, the careful handling of browser storage in Angular SSR is crucial for building robust, secure, and high-performance applications.

--

--

Sumit kumar Singh
Web Development with sumit

YouTube: https://www.youtube.com/@tech..Design/ 📚 HTML,Angular, React,and JavaScript 🧑‍💻 Tips & tricks on Web Developing 👉 FULL STACK DEVELOPER