DEV Community

Cover image for Why I Built ngx-local-vault: Signals, Encryption, and TTL for Angular Storage
Yasin Demir
Yasin Demir

Posted on • Originally published at ysndmr.com

Why I Built ngx-local-vault: Signals, Encryption, and TTL for Angular Storage

Every app needs to persist something in the browser: user preferences, a sidebar toggle, a short-lived session token. localStorage and sessionStorage are the default tools for that.

But using the raw Web Storage API in Angular still feels like 2015. You end up writing the same boilerplate every time.

Most storage wrappers give you a getter and a setter and call it done. Reactivity, encryption, expiration — you're on your own for all three.

So I built ngx-local-vault: persistence, encryption, and TTL, wired into a native Angular WritableSignal.


What's Actually Wrong with Browser Storage

If you've built anything non-trivial in Angular, at least one of these has bitten you:

  • No reactivity. localStorage.setItem() doesn't trigger change detection. You end up writing your own events, or an RxJS Subject, just so a component notices the value changed.
  • Plain text. JWTs and profile data sitting in localStorage in plain text are one DevTools tab (or one XSS script) away from being read.
  • No expiry. localStorage just sits there forever. Want a token to die after 15 minutes? Store a timestamp yourself, check it on load, delete it yourself.
  • SSR headaches. Touch window.localStorage during server-side rendering and you get ReferenceError: window is not defined. Wrapping every access in isPlatformBrowser gets old fast.

ngx-local-vault

It's a small library — under 2KB gzipped, no dependencies beyond Angular itself — that treats browser storage as something a component can just read reactively.

Instead of separate read and write calls, there's a watchSignal() method that hands you back a real Angular Signal. Read it, .set() it, .update() it — encryption and TTL happen underneath without you thinking about them.

1. Setup

Add it to app.config.ts. Works with Angular 17 through 20.

import { ApplicationConfig } from '@angular/core';
import { provideVault } from 'ngx-local-vault';

export const appConfig: ApplicationConfig = {
  providers: [
    provideVault({
      prefix: 'app_',
      encryptionKey: 'your-secure-key',
      driver: 'local'
    })
  ]
};
Enter fullscreen mode Exit fullscreen mode

2. Using it

Inject VaultService and watch a key:

import { inject, Component } from '@angular/core';
import { VaultService } from 'ngx-local-vault';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  standalone: true
})
export class AppComponent {
  private vault = inject(VaultService);

  theme = this.vault.watchSignal<'light' | 'dark'>('theme', 'light');

  session = this.vault.watchSignal<string | null>('session-token', null, {
    expiresIn: '15m'
  });

  toggleTheme() {
    this.theme.update(current => current === 'light' ? 'dark' : 'light');
  }

  login(token: string) {
    this.session.set(token);
  }
}
Enter fullscreen mode Exit fullscreen mode

What it does differently

  • Real signals. watchSignal() returns an actual WritableSignal<T>, not some wrapper you have to learn.
  • Encrypted at rest. Open the Application tab in DevTools and you won't find raw JSON or a bare token sitting there.
  • TTL that actually deletes itself. Pass expiresIn: '15m' and the entry disappears in-tab when the clock runs out — no reload needed. It takes plain strings: 500ms, 30s, 15m, 2h, 1d.
  • SSR-safe. Guarded by PLATFORM_ID, so on the server it's just a no-op. No hydration mismatches.
  • Actually zero dependencies. Only @angular/core and @angular/common as peers. Even tslib gets stripped at build time.

Try It

If you're not on Angular, there are React and Vue versions too — links are in the GitHub README.

Wrap Up

Signals changed how Angular handles state. I think storage should have followed the same shift a while ago.

How are you handling browser storage in your Angular apps right now? Open an issue or drop a star on GitHub if this is useful to you.

Top comments (0)