If you’ve ever closed a tab and lost a half filled form, you’ve met the problem that session storage can solve. In Angular apps, saving small bits of state per browser tab can keep users happy without reaching for a database or setting cookies. I’ll show you exactly how to set JSON data to session storage in Angular for later retrieval, explain what to watch out for, and share production ready patterns you can drop into your code today. We’ll also compare what you’re reading to three popular articles and go further than they do, so you walk away with something better than the basics.
What “Session Storage” Actually In The Browser
Session storage is a browser feature that stores key value pairs for the lifetime of a single tab. Close the tab and the data’s gone open a new tab and it gets a fresh, empty store. This is different from localStorage, which is shared across tabs. In other words, session storage is scoped to one tab and one origin, and it persists across page navigations within that tab. That simple mental model “per-tab, temporary, string-only storage” explains a lot of its behavior for Angular apps.
The JSON Objects Must Be Strings First
Session storage only stores strings, not objects. That means your Angular code should stringify on the way in and parse on the way out. This is true whether you write plain TypeScript, use a service, or wrap the API with generics. The core dance is JSON.stringify()
when setting and JSON.parse()
when retrieving. If you forget, you’ll read back text that looks like JSON but won’t behave like an object. This isn’t an Angular quirk it’s how the Web Storage API works.
The Minimal Set And Get JSON In A Component
Let’s start tiny and obvious so nothing is mysterious. In any Angular component, you can write:
// Store
const profile = { id: 42, name: 'Ada', roles: ['admin', 'editor'] };
sessionStorage.setItem('profile', JSON.stringify(profile));
// Retrieve
const stored = sessionStorage.getItem('profile');
const parsed = stored ? JSON.parse(stored) as { id: number; name: string; roles: string[] } : null;
This snippet shows the core pattern behind how to set JSON data to session storage in Angular for later retrieval. If stored
is null
, the key wasn’t saved yet, which is normal on a first visit. If parsing fails, your data wasn’t valid JSON catch that in production with try/catch. The stringify-then-parse pattern is exactly what the standard teaches and what Q&A threads reiterate again and again.
Production Pattern A Typed Session Storage Service
Hard-coding sessionStorage
calls in components gets messy. Wrap them in a service so your UI stays clean. Here’s a robust, typed approach with safety and helpful errors:
import { Injectable, Inject, PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
@Injectable({ providedIn: 'root' })
export class SessionStore {
constructor(@Inject(PLATFORM_ID) private platformId: Object) {}
private get canUse(): boolean {
return isPlatformBrowser(this.platformId) && typeof sessionStorage !== 'undefined';
}
set<T>(key: string, value: T): void {
if (!this.canUse) return;
try {
const payload = JSON.stringify(value);
sessionStorage.setItem(key, payload);
} catch (err) {
// Quota errors or circular structures end up here
console.error(`SessionStore.set failed for key "${key}"`, err);
}
}
get<T>(key: string): T | null {
if (!this.canUse) return null;
const raw = sessionStorage.getItem(key);
if (raw == null) return null;
try {
return JSON.parse(raw) as T;
} catch (err) {
console.warn(`SessionStore.get JSON parse failed for key "${key}"`, err);
return null;
}
}
remove(key: string): void {
if (!this.canUse) return;
sessionStorage.removeItem(key);
}
clear(): void {
if (!this.canUse) return;
sessionStorage.clear();
}
}
This design gives you typed reads, defensive checks for server-side rendering, and graceful handling of quota exceptions and malformed JSON problems you’ll eventually hit in real apps. The check for isPlatformBrowser
is crucial if you use Angular Universal, since window
and sessionStorage
don’t exist on the server.
Angular ish Way To Consume The Service In Components
Now your component becomes clean and intention-revealing:
export class ProfileComponent {
constructor(private store: SessionStore) {}
saveDraft(profile: ProfileDraft) {
this.store.set<ProfileDraft>('profile:draft', profile);
}
loadDraft(): ProfileDraft | null {
return this.store.get<ProfileDraft>('profile:draft');
}
}
This is how you keep UI code readable while still doing the exact thing you set out to do: set JSON data to session storage in Angular for later retrieval without sprinkling stringifying logic everywhere.
The Route Resolver Trick Preload Session Data Before A Page Shows
Sometimes you want the data ready before the component initializes. A route resolver can do that:
@Injectable({ providedIn: 'root' })
export class DraftResolver implements Resolve<ProfileDraft | null> {
constructor(private store: SessionStore) {}
resolve(): ProfileDraft | null {
return this.store.get<ProfileDraft>('profile:draft');
}
}
With a resolver, your component receives the draft via route data, and you avoid “flash of empty UI” while the draft loads. This pattern is underused but feels great in complex flows.
Autosave To Session Storage While The User Types
For forms, use valueChanges
to autosave on each change and restore on load:
ngOnInit() {
const draft = this.store.get<ProfileDraft>('profile:draft');
if (draft) this.form.patchValue(draft, { emitEvent: false });
this.form.valueChanges.subscribe(v => {
this.store.set<ProfileDraft>('profile:draft', v);
});
}
This gives users a “safety net” per tab. If they navigate away and come back in the same tab, their work is still there. Ben Nadel explored this exact idea years ago; we’re extending it with typing, SSR safety, and better error handling.
The Size Limits And Errors:
Browsers enforce per-origin quotas for Web Storage. If you try to store too much, setItem
throws a QuotaExceededError
. Real numbers vary by implementation, but common guidance is that storage is limited to a handful of megabytes, and you should handle exceptions. Wrap writes in try/catch (as shown earlier), and consider pruning large or old keys. MDN explains quotas and eviction behavior so you’re not surprised in production.
Use A Replacer And Reviver If You Must
When you need a tiny bit of control say to handle Date
objects you can pass a replacer to JSON.stringify
and a reviver to JSON.parse
:
function replacer(_key: string, value: unknown) {
return value instanceof Date ? { _type: 'Date', value: value.toISOString() } : value;
}
function reviver(_key: string, value: any) {
return value && value._type === 'Date' ? new Date(value.value) : value;
}
this.store.set('report', report,); // modify service to accept a replacer if needed
const raw = sessionStorage.getItem('report');
const parsed = raw ? JSON.parse(raw, reviver) : null;
Conclusion
Session storage is deceptively simple, but doing it well in Angular means adding a few guardrails. You now have them: a typed service, safe serialization, SSR awareness, UX-friendly autosave, route time hydration, and a realistic view of quotas and security. If your goal was to learn how to set JSON data to session storage in Angular for later retrieval, you’ve got the how and the why. Keep it small, keep it per-tab, and keep secrets out of it and your users will feel like the app is reading their mind, not their storage.