CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-pinia-plugin-persistedstate

Configurable persistence and rehydration of Pinia stores with SSR-friendly Nuxt integration.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

store-persistence.mddocs/

Store Persistence

Configuration options for individual store persistence behavior, including storage selection, state filtering, and lifecycle hooks. Each store can configure its own persistence settings independently of global plugin options.

Capabilities

Persistence Configuration

Configure how individual stores persist their state with fine-grained control over storage, serialization, and state selection.

/**
 * Persistence options for individual stores
 */
interface PersistenceOptions<State = any> {
  /** Storage key to use (defaults to store.$id) */
  key?: string;
  /** Enable debug logging for this store */
  debug?: boolean;
  /** Storage backend for this store */
  storage?: StorageLike;
  /** Serializer for state conversion */
  serializer?: Serializer;
  /** Hook called before hydrating store */
  beforeHydrate?: (context: PiniaPluginContext) => void;
  /** Hook called after hydrating store */
  afterHydrate?: (context: PiniaPluginContext) => void;
  /** Dot-notation paths to include in persistence */
  pick?: Path<State>[] | string[];
  /** Dot-notation paths to exclude from persistence */
  omit?: Path<State>[] | string[];
}

/**
 * Dot-notation path type for deep object property access
 */
type Path<T> = string;

/**
 * Persist option types for store configuration
 */
type Persist<State = any> = boolean | PersistenceOptions<State> | PersistenceOptions<State>[];

Usage Examples:

import { defineStore } from 'pinia';

// Simple boolean persistence
export const useSimpleStore = defineStore('simple', {
  state: () => ({ count: 0 }),
  persist: true,
});

// Configured persistence
export const useConfiguredStore = defineStore('configured', {
  state: () => ({
    user: { name: 'John', email: 'john@example.com' },
    settings: { theme: 'dark', notifications: true },
    tempData: 'not persisted',
  }),
  persist: {
    key: 'user-store',
    storage: sessionStorage,
    pick: ['user', 'settings'], // Only persist these fields
  },
});

// Multiple persistence configurations
export const useMultiStore = defineStore('multi', {
  state: () => ({
    localData: 'saved locally',
    sessionData: 'saved in session',
    tempData: 'not saved',
  }),
  persist: [
    {
      key: 'local-data',
      storage: localStorage,
      pick: ['localData'],
    },
    {
      key: 'session-data', 
      storage: sessionStorage,
      pick: ['sessionData'],
    },
  ],
});

Store Hydration and Persistence Methods

Stores with persistence enabled automatically receive additional methods for manual hydration and persistence control.

/**
 * Additional methods added to persisted stores
 */
interface PiniaCustomProperties {
  /** Manually hydrate store from configured storage */
  $hydrate: (opts?: { runHooks?: boolean }) => void;
  /** Manually persist store to configured storage */
  $persist: () => void;
}

Usage Examples:

const store = useMyStore();

// Manual hydration (useful for conditional loading)
store.$hydrate();

// Manual hydration without hooks
store.$hydrate({ runHooks: false });

// Manual persistence (useful for explicit save points)
store.$persist();

Configuration Options

Key Option

Custom storage key for the store, overriding the default store ID.

interface PersistenceOptions {
  key?: string;
}

Usage Example:

export const useStore = defineStore('internal-store-id', {
  state: () => ({ data: [] }),
  persist: {
    key: 'user-friendly-key', // Stored as 'user-friendly-key' instead of 'internal-store-id'
  },
});

Storage Selection

Choose storage backend for this specific store.

interface PersistenceOptions {
  storage?: StorageLike;
}

Usage Examples:

// Use sessionStorage
persist: {
  storage: sessionStorage,
}

// Custom storage
persist: {
  storage: {
    getItem: (key) => indexedDB.getItem(key),
    setItem: (key, value) => indexedDB.setItem(key, value),
  },
}

State Filtering

Control which parts of the state are persisted using pick/omit patterns.

interface PersistenceOptions<State> {
  /** Include only these paths */
  pick?: Path<State>[] | string[];
  /** Exclude these paths */
  omit?: Path<State>[] | string[];
}

Usage Examples:

export const useUserStore = defineStore('user', {
  state: () => ({
    profile: {
      name: 'John',
      email: 'john@example.com',
      preferences: {
        theme: 'dark',
        notifications: true,
      },
    },
    session: {
      token: 'secret',
      expires: Date.now(),
    },
    tempData: {},
  }),
  persist: {
    // Only persist user profile and preferences
    pick: ['profile.name', 'profile.email', 'profile.preferences'],
    
    // Alternative: exclude sensitive data
    // omit: ['session.token', 'tempData'],
  },
});

Lifecycle Hooks

Custom hooks that run before and after state hydration from storage.

interface PersistenceOptions {
  beforeHydrate?: (context: PiniaPluginContext) => void;
  afterHydrate?: (context: PiniaPluginContext) => void;
}

Usage Examples:

export const useStore = defineStore('store', {
  state: () => ({ version: '1.0', data: [] }),
  persist: {
    beforeHydrate: (context) => {
      console.log('About to hydrate store:', context.store.$id);
    },
    afterHydrate: (context) => {
      // Migrate data if needed
      const store = context.store;
      if (store.version !== '1.0') {
        // Perform migration logic
        store.version = '1.0';
      }
    },
  },
});

Serialization

Custom serialization for this store, overriding global serializer.

interface PersistenceOptions {
  serializer?: Serializer;
}

interface Serializer {
  serialize: (data: any) => string;
  deserialize: (data: string) => any;
}

Usage Example:

export const useStore = defineStore('store', {
  state: () => ({ sensitiveData: 'secret' }),
  persist: {
    serializer: {
      serialize: (data) => btoa(JSON.stringify(data)), // Base64 encode
      deserialize: (data) => JSON.parse(atob(data)),   // Base64 decode
    },
  },
});

Install with Tessl CLI

npx tessl i tessl/npm-pinia-plugin-persistedstate

docs

index.md

nuxt-integration.md

plugin-configuration.md

store-persistence.md

tile.json