svelte-persistent-runes Svelte Themes

Svelte Persistent Runes

A Svelte reactive rune that keep its value through pages and reloads

Svelte persistent runes

A Svelte reactive rune that keep its value through pages and reloads

[ D E M O ]

Installation

With NPM

npm install --save-dev @macfja/svelte-persistent-runes
# or
yarn add --save-dev @macfja/svelte-persistent-runes
# or
pnpm add --save-dev @macfja/svelte-persistent-runes
# or
deno install --dev npm:@macfja/svelte-persistent-runes

Quickstart

Update your ./svelte.config.js to add a new preprocessor:

 import adapter from '@sveltejs/adapter-auto';
+import persist from "@macfja/svelte-persistent-runes/preprocessor"
 const config = {
+     preprocess: [persist()],
    kit: {
        adapter: adapter()
    }
 };
 export default config;

Replace your $state with $persist:

 <script>
+import "@macfja/svelte-persistent-runes"
-let count = $state(0);
+let count = $persist(0, 'counter');
 </script>

Usage

This library have 2 parts:

  • A preprocessor to add the $persist rune.
  • A set of configuration to persist your data.

You MUST add the preprocessor to use $persist. It's as simple as to add it in your Svelte configuration (svelte.config.js) with the import of @macfja/svelte-persistent-runes/preprocessor

./svelte.config.js
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
import persist from "@macfja/svelte-persistent-runes/preprocessor"

/** @type {import('@sveltejs/kit').Config} */
const config = {
    preprocess: [vitePreprocess(), persist()],
    kit: {
        adapter: adapter()
    }
};

export default config;

Now that the preprocessor is added, you can use the $persist rune instead of the $state rune.

./src/anywhere/component.svelte
<script>
import "@macfja/svelte-persistent-runes"
let count = $persist(0, 'counter');
</script>
<div class="counter">
    <button onclick={() => (count -= 1)} aria-label="Decrease the counter by one">-</button>
    <div><strong>{count}</strong></div>
    <button onclick={() => (count += 1)} aria-label="Increase the counter by one">+</button>
</div>
./src/anywhere/data.svelte.ts
import "@macfja/svelte-persistent-runes"
export class Person {
    name = $persist('John', 'user-name')
    age = $persist(33, 'user-age')
    
    greet(): string {
        return `Hello ${this.name}`;
    }
    birthday(): string {
        this.age += 1;
        return `Happy birthday ${this.name}!`
    }
}
export const currentUser = new Person()

[!IMPORTANT] You need to import import "@macfja/svelte-persistent-runes" to prevent Typescript to complain about the unknown function $persist

Definition

type PersistentRunesOptions = {
    /**
     * Convert the source data into its string representation
     * @param input The source data
     * @return The string representation of data
     */
    serialize<T>(input: T): string;
    /**
     * Convert back the string representation into the source data
     * @param input The string representation of the date
     * @return The new data based on its string representation
     */
    deserialize<T>(input: string): T;
    /**
     * Write data into the store
     * @param key The storage key to write
     * @param value The data to write
     */
    storageWrite(key: string, value: string): void;
    /**
     * Read data from the storage
     * @param key The storage key to read
     * @returns The data or `undefined` if the data don't exist in the storage
     */
    storageRead(key: string): string | undefined;
};

/**
 * A reactive state, that can restore its state upon page reload
 * @param initial The initial value of the state
 * @param key The storage key of the state. Must be unique in your application
 * @param options The persistence options (how and where)
 */
declare function $persist<T>(initial: T, key: string, options?: Partial<PersistentRunesOption>)

Options

You can customize how and where the state value is persisted.

The $persist runes take a third (and optional) parameter of type PersistentRunesOption.

The options consist of 2 main part: the serializer and the storage. It can be defined as a plain object or as the result of the buildOptions (impoerted from @macfja/svelte-persistent-runes/options)

/**
 * Create a `PersistentRunesOptions` from a serializer and a storage
 * @param serializer The serializer to use (if `undefined` then `JsonSerializer` will be used)
 * @param storage The storage to use (if `undefined` then `BrowserLocalStorage` will be used)
 */
declare function buildOptions(
    serializer: PersistentRunesSerializer | undefined, 
    storage: PersistentRunesStorage | undefined
): PersistentRunesOptions;

The serializer

The serializer part of the option are:

  • serialize: This function is responsible for converting the original type into a string
  • deserialize: This function is responsible to convert back a string to the original type

The library have several built-in serializer:

  • JsonSerializerFactory: factory to create a JSON based serializer
    • JsonSerializer: A basic JSON serializer (no replacer, nor reviver)
  • DevalueSerializerFactory: factory to create a Devalue based serializer
    • DevalueSerializer: A basic Devalue serializer (no reducers, nor revivers)
  • ESSerializerSerializerFactory: factory to create a ESSerializer based serializer
    • ESSerializerSerializer: A basic ESSerializer serializer (no SerializeOptions, nor classes)
  • MacfjaSerializerFactory: factory to create a @macfja/serializer based serializer
  • SuperJsonSerializer: A superjson serializer
  • NextJsonSerializerFactory: factory to create a next-json based serializer
    • NextJsonSerializerFactory: A basic next-json serializer (no options, nor replacers, nor revivers)

The storage

The storage part of the option are:

  • storageWrite: This function is responsible to write data into the storage
  • storageRead: This function is responsible to read data from the storage

The library have several built-in storage:

  • BrowserCookieStorageFactory: factory to create a Cookie based storage (DOM API, browser only)
    • BrowserCookieStorage: A basic Cookie storage (no particular options, except for samesite: Strict)
  • BrowserLocalStorage: a browser localStorage storage (DOM API, browser only)
  • BrowserSessionStorage: a browser sessionStorage storage (DOM API, browser only)
  • addEncryptionStorage: a wrapper function to add AES GCM encryption on stored data

Example

Browser session storage and @macfja/serializer
<script>
import "@macfja/svelte-persistent-runes"
import { buildOptions, MacfjaSerializer, BrowserSessionStorage } "@macfja/svelte-persistent-runes/options"
let count = $persist(0, 'counter', buildOptions(MacfjaSerializer, BrowserSessionStorage));
</script>
<div class="counter">
    <button onclick={() => (count -= 1)} aria-label="Decrease the counter by one">-</button>
    <div><strong>{count}</strong></div>
    <button onclick={() => (count += 1)} aria-label="Increase the counter by one">+</button>
</div>
Browser local storage, encrypted and SuperJson
<script>
import "@macfja/svelte-persistent-runes"
import { buildOptions, SuperJsnoSerializer, BrowserLocalStorage, addEncryptionStorage } "@macfja/svelte-persistent-runes/options"
let count = $persist(0, 'counter', buildOptions(SuperJsnoSerializer, addEncryptionStorage(BrowserLocalStorage, '12345678901234567890123456879012'));
</script>
<div class="counter">
    <button onclick={() => (count -= 1)} aria-label="Decrease the counter by one">-</button>
    <div><strong>{count}</strong></div>
    <button onclick={() => (count += 1)} aria-label="Increase the counter by one">+</button>
</div>

Top categories

svelte logo

Need a Svelte website built?

Hire a professional Svelte developer today.
Loading Svelte Themes