Pulled ALL skills from 15 source repositories: - anthropics/skills: 16 (docs, design, MCP, testing) - obra/superpowers: 14 (TDD, debugging, agents, planning) - coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth) - better-auth/skills: 5 (auth patterns) - vercel-labs/agent-skills: 5 (React, design, Vercel) - antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo) - Plus 13 individual skills from various repos Mosaic Stack is not limited to coding — the Orchestrator and subagents serve coding, business, design, marketing, writing, logistics, analysis, and more. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
220 lines
6.2 KiB
Markdown
220 lines
6.2 KiB
Markdown
---
|
|
category: State
|
|
related: useLocalStorage, useSessionStorage, useStorageAsync
|
|
---
|
|
|
|
# useStorage
|
|
|
|
Create a reactive ref that can be used to access & modify [LocalStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) or [SessionStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage).
|
|
|
|
Uses localStorage by default, other storage sources be specified via third argument.
|
|
|
|
## Usage
|
|
|
|
::: tip
|
|
When using with Nuxt 3, this function will **NOT** be auto imported in favor of Nitro's built-in [`useStorage()`](https://nitro.unjs.io/guide/storage). Use explicit import if you want to use the function from VueUse.
|
|
:::
|
|
|
|
```ts
|
|
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 is present and ignores the default value. Be aware that when you are adding more properties to the default value, the key might be `undefined` if client's storage does not have that key.
|
|
|
|
```ts
|
|
import { useStorage } from '@vueuse/core'
|
|
// ---cut---
|
|
localStorage.setItem('my-store', '{"hello": "hello"}')
|
|
|
|
const state = useStorage('my-store', { hello: 'hi', greeting: 'hello' }, localStorage)
|
|
|
|
console.log(state.value.greeting) // undefined, since the value is not presented in storage
|
|
```
|
|
|
|
To solve that, you can enable `mergeDefaults` option.
|
|
|
|
```ts
|
|
import { useStorage } from '@vueuse/core'
|
|
// ---cut---
|
|
localStorage.setItem('my-store', '{"hello": "nihao"}')
|
|
|
|
const state = useStorage(
|
|
'my-store',
|
|
{ hello: 'hi', greeting: 'hello' },
|
|
localStorage,
|
|
{ mergeDefaults: true } // <--
|
|
)
|
|
|
|
console.log(state.value.hello) // 'nihao', from storage
|
|
console.log(state.value.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
|
|
import { useStorage } from '@vueuse/core'
|
|
// ---cut---
|
|
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),
|
|
},
|
|
},
|
|
)
|
|
```
|
|
|
|
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' }
|
|
```
|
|
|
|
## Type Declarations
|
|
|
|
```ts
|
|
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
|
|
/**
|
|
* Wait for the component to be mounted before reading the storage.
|
|
*
|
|
* @default false
|
|
*/
|
|
initOnMounted?: boolean
|
|
}
|
|
export declare function useStorage(
|
|
key: MaybeRefOrGetter<string>,
|
|
defaults: MaybeRefOrGetter<string>,
|
|
storage?: StorageLike,
|
|
options?: UseStorageOptions<string>,
|
|
): RemovableRef<string>
|
|
export declare function useStorage(
|
|
key: MaybeRefOrGetter<string>,
|
|
defaults: MaybeRefOrGetter<boolean>,
|
|
storage?: StorageLike,
|
|
options?: UseStorageOptions<boolean>,
|
|
): RemovableRef<boolean>
|
|
export declare function useStorage(
|
|
key: MaybeRefOrGetter<string>,
|
|
defaults: MaybeRefOrGetter<number>,
|
|
storage?: StorageLike,
|
|
options?: UseStorageOptions<number>,
|
|
): RemovableRef<number>
|
|
export declare function useStorage<T>(
|
|
key: MaybeRefOrGetter<string>,
|
|
defaults: MaybeRefOrGetter<T>,
|
|
storage?: StorageLike,
|
|
options?: UseStorageOptions<T>,
|
|
): RemovableRef<T>
|
|
export declare function useStorage<T = unknown>(
|
|
key: MaybeRefOrGetter<string>,
|
|
defaults: MaybeRefOrGetter<null>,
|
|
storage?: StorageLike,
|
|
options?: UseStorageOptions<T>,
|
|
): RemovableRef<T>
|
|
```
|