Vize

use-storage

Typed built-in serializers, usable directly as the serializer option.

Package @vizejs/composable/use-storage
Own the source vize lib pull composable:use-storage
Runtime exports inferStorageSerializerKind, storageSerializers, useLocalStorage, useSessionStorage, useStorage
Gzip budget 4608 B

Minimal setup

Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).

vp install @vizejs/composable

Import the entry directly. Call lifecycle-bound utilities inside <script setup> so cleanup follows the component scope; consult the runtime contract for this entry's SSR and hydration behavior.

import { inferStorageSerializerKind, storageSerializers, useLocalStorage, useSessionStorage, useStorage } from "@vizejs/composable/use-storage";

Try it

Remember reading progress

Persist a selected guide section in the current browser and provide an explicit way to remove the saved choice.

Open the interactive example

What to observe: Choose Components, then reload this page to keep that choice. Reset reading progress removes the saved key and restores Getting started, including after another reload.

state is writable; supported and error describe the real localStorage capability. A validator accepts only the three guide sections. writeDefaults: false leaves an absent or removed key absent. SSR uses Getting started without accessing storage; the default post-flush initial read preserves hydration before restoring a saved choice. When storage is unavailable, choices still update in this tab but reload returns to the default. Storage can fail in restricted browsers. The key belongs to this example's origin; an embedded frame may have a different origin or storage policy from its parent. Watches and storage listeners follow the component scope.

Compare the initial and interacted states
Remember reading progress: initial state
Initial state
Remember reading progress: state after the verified interactions
After the verified interactions
When browser storage is unavailable

Choices still change in this tab. Reloading restores Getting started because the browser cannot save the choice. This capture uses actual Chromium with localStorage disabled.

Publishing selected in this tab while browser storage is unavailable

Copy the example

Save this complete SFC in your Vue project. Its public imports and source are the same ones executed in the preview.

<script setup lang="ts">
import { computed, useId } from "vue";
import { useStorage } from "@vizejs/composable/use-storage";

type GuideSection = "getting-started" | "components" | "publishing";

const sectionId = useId();
const {
  state: section,
  supported,
  error,
  remove,
} = useStorage<GuideSection>("vize:docs:reading-progress", "getting-started", {
  writeDefaults: false,
  validate: (candidate): candidate is GuideSection =>
    candidate === "getting-started" || candidate === "components" || candidate === "publishing",
});
const sectionTitle = computed(() => {
  if (section.value === "components") return "Components";
  if (section.value === "publishing") return "Publishing";
  return "Getting started";
});
</script>

<template>
  <div class="composable-example">
    <label :for="sectionId">Guide section</label>
    <select :id="sectionId" v-model="section" :aria-describedby="`${sectionId}-help`">
      <option value="getting-started">Getting started</option>
      <option value="components">Components</option>
      <option value="publishing">Publishing</option>
    </select>
    <p :id="`${sectionId}-help`">
      Choose where to resume reading, then reload this page. Available browser storage keeps your
      choice.
    </p>
    <output aria-live="polite">Resume at: {{ sectionTitle }}</output>
    <p role="status">
      {{
        error
          ? `Storage error: ${error.code}. The current section may not be saved.`
          : supported
            ? "Browser storage is available."
            : "Browser storage is unavailable; changes stay in this tab."
      }}
    </p>
    <button type="button" @click="() => remove()">Reset reading progress</button>
    <p>Reset removes the saved choice and restores Getting started.</p>
  </div>
</template>

Runtime contract

Utility Category Stability SSR Hydration Cleanup Targets Host globals Uses
inferStorageSerializerKind storage experimental safe not-applicable none web, server, worker, native, desktop, terminal — —
useStorage storage experimental deterministic-fallback caller-managed reactive-scope web, desktop CustomEvent, window inferStorageSerializerKind, tryOnScopeDispose
useLocalStorage storage experimental deterministic-fallback caller-managed reactive-scope web, desktop — useStorage
useSessionStorage storage experimental deterministic-fallback caller-managed reactive-scope web, desktop window useStorage

API

storageSerializers

Typed built-in serializers, usable directly as the serializer option. object round-trips JSON-compatible values; map and set store their entries as JSON arrays; date stores an ISO-8601 string.

const storageSerializers: { /** Decimal `bigint` text. */ readonly bigint: StorageSerializer<bigint>; /** `"true"` / `"false"`. */ readonly boolean: StorageSerializer<boolean>; /** ISO-8601 timestamp. */ readonly date: StorageSerializer<Date>; /** JSON array of `[key, value]` entries. */ readonly map: StorageSerializer<Map<unknown, unknown>>; /** Decimal number text. */ readonly number: StorageSerializer<number>; /** JSON text. */ readonly object: StorageSerializer<unknown>; /** JSON array of members. */ readonly set: StorageSerializer<Set<unknown>>; /** Raw string. */ readonly string: StorageSerializer<string>; }

inferStorageSerializerKind

Select the built-in serializer kind for a default value. null, arrays, and plain objects use JSON ("object"); Map, Set, and Date use their dedicated kinds; primitives use their typeof. Functions and symbols cannot be stored and fall back to "string".

function inferStorageSerializerKind(value: unknown): StorageSerializerKind

useStorage

Synchronize a typed reactive value with a Web Storage compatible backend. The serializer is inferred from defaultValue (numbers stay numbers, Map/Set/Date round-trip), and every decoded value passes the validate hook before it is accepted, so corrupted or foreign data falls back to the default instead of leaking the wrong type. Writes follow the reactive value; changes from other tabs (storage events) and from other instances in the same document using the same key are applied live. Server rendering: without a browser window no storage is touched and state holds the default. Node's global localStorage is intentionally ignored because it would be shared between requests. Inside a component initialRead defaults to "post-flush", so hydrating markup that rendered the default matches before switching to the stored value. Failures (quota, disabled storage, malformed data) never throw; they are exposed through error and onError.

function useStorage<Value>( key: MaybeRefOrGetter<string>, defaultValue: MaybeRefOrGetter<Value>, options: UseStorageOptions<Value> = {}, ): StorageControls<Value>
const { state: theme } = useStorage("theme", "light" as "light" | "dark");
theme.value = "dark"; // persisted

useLocalStorage

useStorage bound to window.localStorage by default.

function useLocalStorage<Value>( key: MaybeRefOrGetter<string>, defaultValue: MaybeRefOrGetter<Value>, options: UseStorageOptions<Value> = {}, ): StorageControls<Value>

useSessionStorage

useStorage bound to window.sessionStorage by default.

function useSessionStorage<Value>( key: MaybeRefOrGetter<string>, defaultValue: MaybeRefOrGetter<Value>, options: UseStorageOptions<Value> = {}, ): StorageControls<Value>

Types

StorageLike

Minimal synchronous key-value store compatible with the Web Storage API.

Member Type Description
getItem (key: string) => string | null Read the raw string stored for key, or null when absent.
setItem (key: string, value: string) => void Store value under key. May throw (for example on quota exhaustion).
removeItem (key: string) => void Remove key. Removing an absent key is a no-op.

StorageSerializer

Converts one value type to and from its stored string representation.

Member Type Description
read (raw: string) => Value Decode a stored string. Throwing marks the stored value as unreadable.
write (value: Value) => string Encode a value for storage.

StorageFailure

Failure observed while synchronizing a value with storage.

Member Type Description
code StorageErrorCode Which synchronization step failed.
key string Storage key involved in the failure.
cause unknown Exact thrown value, or the rejected candidate for "invalid-value".

UseStorageOptions

Options for useStorage.

Member Type Description
storage? MaybeRefOrGetter<StorageLike | null | undefined> Reactive storage backend. null/undefined keeps the composable on its default value, which is also what happens during server rendering.
eventTarget? MaybeRefOrGetter<EventTarget | null | undefined> Event target receiving cross-document storage events and same-document synchronization events between instances sharing a key.
serializer? StorageSerializer<Value> Explicit serializer. When omitted the serializer is inferred from the default value (see inferStorageSerializerKind).
validate? StorageValidator<Value> Schema validation hook applied to every decoded value. Rejected values fall back to the default and report an "invalid-value" failure.
mergeDefaults? boolean | ((stored: Value, defaults: Value) => Value) Merge a stored object with the default so newly added default keys are present. true performs a shallow merge; a function merges explicitly.
writeDefaults? boolean Persist the default value when the key is absent.
listenToStorageChanges? boolean Follow changes made by other documents and other instances.
deep? boolean Watch nested mutations of object values.
flush? "pre" | "post" | "sync" Write timing relative to component rendering.
initialRead? "sync" | "post-flush" When the stored value is first read. "post-flush" keeps the default through the first render so server-rendered markup hydrates without a mismatch, then reads storage after mounting.
onError? (failure: StorageFailure) => void Observe read, validation, write, and removal failures. Failures never throw out of the composable.

StorageControls

Reactive state and controls returned by useStorage.

Member Type Description
state Ref<Value> Writable value synchronized with storage.
supported Readonly<Ref<boolean>> Whether a storage backend is currently attached.
error Readonly<ShallowRef<StorageFailure | undefined>> Most recent synchronization failure, cleared by the next success.
refresh () => void Re-read the stored value, replacing the reactive value.
remove () => void Remove the key from storage and restore the default value.