Skip to content
On this page

useStorage

Category
Export Size
2.16 kB
Last Changed
4 days ago
Related

Reactive LocalStorage/SessionStorage

Demo

name: 'Banana'
color: 'Yellow'
size: 'Medium'
count: 0

Usage

TIP

When using with Nuxt 3, this functions will NOT be auto imported in favor of Nitro's built-in useStorage(). Use explicit import if you want to use the function from VueUse.

js
import { useStorage } from '@vueuse/core'

// bind object
const state = useStorage('my-store', { hello: 'hi', greeting: 'Hello' })

// bind boolean
const flag = useStorage('my-flag', true) // returns Ref<boolean>

// bind number
const count = useStorage('my-count', 0) // returns Ref<number>

// bind string with SessionStorage
const id = useStorage('my-id', 'some-string-id', sessionStorage) // returns Ref<string>

// delete data from storage
state.value = null
import { useStorage } from '@vueuse/core'

// bind object
const state = useStorage('my-store', { hello: 'hi', greeting: 'Hello' })

// bind boolean
const flag = useStorage('my-flag', true) // returns Ref<boolean>

// bind number
const count = useStorage('my-count', 0) // returns Ref<number>

// bind string with SessionStorage
const id = useStorage('my-id', 'some-string-id', sessionStorage) // returns Ref<string>

// delete data from storage
state.value = null

Merge Defaults

By default, useStorage will use the value from storage if it presents and ignores the default value. Be aware that when you adding more properties to the default value, the key might be undefined if client's storage does not have that key.

ts
localStorage.setItem('my-store', '{"hello": "hello"}')

const state = useStorage('my-store', { hello: 'hi', greeting: 'hello' }, localStorage)

console.log(state.greeting) // undefined, since the value is not presented in storage
localStorage.setItem('my-store', '{"hello": "hello"}')

const state = useStorage('my-store', { hello: 'hi', greeting: 'hello' }, localStorage)

console.log(state.greeting) // undefined, since the value is not presented in storage

To solve that, you can enable mergeDefaults option.

ts
localStorage.setItem('my-store', '{"hello": "nihao"}')

const state = useStorage(
  'my-store',
  { hello: 'hi', greeting: 'hello' },
  localStorage,
  { mergeDefaults: true } // <--
)

console.log(state.hello) // 'nihao', from storage
console.log(state.greeting) // 'hello', from merged default value
localStorage.setItem('my-store', '{"hello": "nihao"}')

const state = useStorage(
  'my-store',
  { hello: 'hi', greeting: 'hello' },
  localStorage,
  { mergeDefaults: true } // <--
)

console.log(state.hello) // 'nihao', from storage
console.log(state.greeting) // 'hello', from merged default value

When setting it to true, it will perform a shallow merge for objects. You can pass a function to perform custom merge (e.g. deep merge), for example:

ts
const state = useStorage(
  'my-store',
  { hello: 'hi', greeting: 'hello' },
  localStorage,
  { mergeDefaults: (storageValue, defaults) => deepMerge(defaults, storageValue) } // <--
)
const state = useStorage(
  'my-store',
  { hello: 'hi', greeting: 'hello' },
  localStorage,
  { mergeDefaults: (storageValue, defaults) => deepMerge(defaults, storageValue) } // <--
)

Custom Serialization

By default, useStorage will smartly use the corresponding serializer based on the data type of provided default value. For example, JSON.stringify / JSON.parse will be used for objects, Number.toString / parseFloat for numbers, etc.

You can also provide your own serialization function to useStorage

ts
import { useStorage } from '@vueuse/core'

useStorage(
  'key',
  {},
  undefined,
  {
    serializer: {
      read: (v: any) => v ? JSON.parse(v) : null,
      write: (v: any) => JSON.stringify(v),
    },
  },
)
import { useStorage } from '@vueuse/core'

useStorage(
  'key',
  {},
  undefined,
  {
    serializer: {
      read: (v: any) => v ? JSON.parse(v) : null,
      write: (v: any) => JSON.stringify(v),
    },
  },
)

Please note when you provide null as the default value, useStorage can't assume the data type from it. In this case, you can provide a custom serializer or reuse the built-in ones explicitly.

ts
import { StorageSerializers, useStorage } from '@vueuse/core'

const objectLike = useStorage('key', null, undefined, { serializer: StorageSerializers.object })
objectLike.value = { foo: 'bar' }
import { StorageSerializers, useStorage } from '@vueuse/core'

const objectLike = useStorage('key', null, undefined, { serializer: StorageSerializers.object })
objectLike.value = { foo: 'bar' }

Type Declarations

Show Type Declarations
typescript
export interface Serializer<T> {
  read(raw: string): T
  write(value: T): string
}
export interface SerializerAsync<T> {
  read(raw: string): Awaitable<T>
  write(value: T): Awaitable<string>
}
export declare const StorageSerializers: Record<
  "boolean" | "object" | "number" | "any" | "string" | "map" | "set" | "date",
  Serializer<any>
>
export declare const customStorageEventName = "vueuse-storage"
export interface StorageEventLike {
  storageArea: StorageLike | null
  key: StorageEvent["key"]
  oldValue: StorageEvent["oldValue"]
  newValue: StorageEvent["newValue"]
}
export interface UseStorageOptions<T>
  extends ConfigurableEventFilter,
    ConfigurableWindow,
    ConfigurableFlush {
  /**
   * Watch for deep changes
   *
   * @default true
   */
  deep?: boolean
  /**
   * Listen to storage changes, useful for multiple tabs application
   *
   * @default true
   */
  listenToStorageChanges?: boolean
  /**
   * Write the default value to the storage when it does not exist
   *
   * @default true
   */
  writeDefaults?: boolean
  /**
   * Merge the default value with the value read from the storage.
   *
   * When setting it to true, it will perform a **shallow merge** for objects.
   * You can pass a function to perform custom merge (e.g. deep merge), for example:
   *
   * @default false
   */
  mergeDefaults?: boolean | ((storageValue: T, defaults: T) => T)
  /**
   * Custom data serialization
   */
  serializer?: Serializer<T>
  /**
   * On error callback
   *
   * Default log error to `console.error`
   */
  onError?: (error: unknown) => void
  /**
   * Use shallow ref as reference
   *
   * @default false
   */
  shallow?: boolean
}
export declare function useStorage(
  key: string,
  defaults: MaybeComputedRef<string>,
  storage?: StorageLike,
  options?: UseStorageOptions<string>
): RemovableRef<string>
export declare function useStorage(
  key: string,
  defaults: MaybeComputedRef<boolean>,
  storage?: StorageLike,
  options?: UseStorageOptions<boolean>
): RemovableRef<boolean>
export declare function useStorage(
  key: string,
  defaults: MaybeComputedRef<number>,
  storage?: StorageLike,
  options?: UseStorageOptions<number>
): RemovableRef<number>
export declare function useStorage<T>(
  key: string,
  defaults: MaybeComputedRef<T>,
  storage?: StorageLike,
  options?: UseStorageOptions<T>
): RemovableRef<T>
export declare function useStorage<T = unknown>(
  key: string,
  defaults: MaybeComputedRef<null>,
  storage?: StorageLike,
  options?: UseStorageOptions<T>
): RemovableRef<T>
export interface Serializer<T> {
  read(raw: string): T
  write(value: T): string
}
export interface SerializerAsync<T> {
  read(raw: string): Awaitable<T>
  write(value: T): Awaitable<string>
}
export declare const StorageSerializers: Record<
  "boolean" | "object" | "number" | "any" | "string" | "map" | "set" | "date",
  Serializer<any>
>
export declare const customStorageEventName = "vueuse-storage"
export interface StorageEventLike {
  storageArea: StorageLike | null
  key: StorageEvent["key"]
  oldValue: StorageEvent["oldValue"]
  newValue: StorageEvent["newValue"]
}
export interface UseStorageOptions<T>
  extends ConfigurableEventFilter,
    ConfigurableWindow,
    ConfigurableFlush {
  /**
   * Watch for deep changes
   *
   * @default true
   */
  deep?: boolean
  /**
   * Listen to storage changes, useful for multiple tabs application
   *
   * @default true
   */
  listenToStorageChanges?: boolean
  /**
   * Write the default value to the storage when it does not exist
   *
   * @default true
   */
  writeDefaults?: boolean
  /**
   * Merge the default value with the value read from the storage.
   *
   * When setting it to true, it will perform a **shallow merge** for objects.
   * You can pass a function to perform custom merge (e.g. deep merge), for example:
   *
   * @default false
   */
  mergeDefaults?: boolean | ((storageValue: T, defaults: T) => T)
  /**
   * Custom data serialization
   */
  serializer?: Serializer<T>
  /**
   * On error callback
   *
   * Default log error to `console.error`
   */
  onError?: (error: unknown) => void
  /**
   * Use shallow ref as reference
   *
   * @default false
   */
  shallow?: boolean
}
export declare function useStorage(
  key: string,
  defaults: MaybeComputedRef<string>,
  storage?: StorageLike,
  options?: UseStorageOptions<string>
): RemovableRef<string>
export declare function useStorage(
  key: string,
  defaults: MaybeComputedRef<boolean>,
  storage?: StorageLike,
  options?: UseStorageOptions<boolean>
): RemovableRef<boolean>
export declare function useStorage(
  key: string,
  defaults: MaybeComputedRef<number>,
  storage?: StorageLike,
  options?: UseStorageOptions<number>
): RemovableRef<number>
export declare function useStorage<T>(
  key: string,
  defaults: MaybeComputedRef<T>,
  storage?: StorageLike,
  options?: UseStorageOptions<T>
): RemovableRef<T>
export declare function useStorage<T = unknown>(
  key: string,
  defaults: MaybeComputedRef<null>,
  storage?: StorageLike,
  options?: UseStorageOptions<T>
): RemovableRef<T>

Source

SourceDemoDocs

Contributors

Anthony Fu
sun0day
Jelf
Jeffrey Li
James Garbutt
Yury Rudey
Puru Vijay
webfansplz
spking11
Maurici Abad Gutierrez
Guillaume Chau
Denis Blazhkun
Andreas Weber
Le Minh Tri
Jamie Warburton
Shinigami
Sasan Farrokh
Pig Fang
wheat
Alex Kozack
Nurettin Kaya
Pine
Antério Vieira
Ivan Demchuk

Changelog

v9.13.0 on 2/18/2023
c6185 - fix: emit a custom event to support custom storage areas (#2630)
v9.8.2 on 12/20/2022
800f7 - fix: sync within page, close #1595
v9.8.1 on 12/20/2022
c3851 - fix: resume the watch after setting the value, fix loop updates
v9.3.1 on 10/17/2022
a06b7 - fix: fixed data desynchronization in case of clearing storage (#2221)
v9.0.0-beta.2 on 7/24/2022
26aae - feat: mergeDefaults option (#1957)
v8.9.3 on 7/14/2022
499f6 - fix!: rename type StorageOptions to UseStorageOptions (#1867)
v8.9.1 on 7/8/2022
a9ccc - feat(all): use MaybeComputedRef (#1768)
v8.2.4 on 4/3/2022
9b238 - fix: fix Set serialize error (#1475)
v8.2.2 on 3/31/2022
f5f6a - feat: support serializing Date (#1471)
71105 - fix: update on same tick (#1472)
v8.2.0 on 3/25/2022
1ae32 - fix: fix sync racing and shreding (#1459)
v8.1.1 on 3/16/2022
27d1b - fix(useStroage): synced timing
v8.1.0 on 3/16/2022
1ee0a - fix: useStorage sync race condition, closes #808 (#1430)
v7.5.3 on 1/5/2022
77fe0 - fix(storage): caught DOMException accessing storage (#1124)
v7.4.1 on 12/23/2021
3aa49 - feat: ssr handlers (#1060)

Released under the MIT License.