npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

pinia-scope

v1.7.2

Published

Scoped Pinia Stores for Vue

Readme

Pinia Scope

Scoped Pinia Stores for Vue.js

Concept

Pinia Scope allows creating dynamically scoped versions of Pinia stores. A scope is a string that prefixes one or more Pinia Store ids separating them from their un-scoped version. After setting the scope of a component, its child components inherit that scope. This allows dynamic creation and disposal of the data layer and improved component re-use in a Vue app.

Installation

$ npm i pinia-scope

Attach pinia scope to the pinia instance in your main.js file.

// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

// add import here
import { attachPiniaScope } from 'pinia-scope'

const app = createApp(App)
const pinia = createPinia()

// attaching here
attachPiniaScope(pinia)

app.use(pinia)
app.mount('#app')

Usage

What is a Scope?

Pinia stores have a store.$id set when defining the store. A scoped store has a scope string prefixed onto its store.$id:

// normal pinia code
export const useMyStore = (scope: string = '') => {
  const myStore = defineStore(scope + 'my-store-id', () => { /* ... */
  })
  return myStore()
}

const myUnscopedStore = useMyStore()
myUnscopedStore.$id // 'my-store-id'
const myScopedStore = useMyStore('preview-scope-')
myScopedStore.$id // 'preview-scope-my-store-id'

The above lets you create separate instances of the same store, but does not have the Abilities of:

🔥Pinia Scope 🔥:

  • Apply scope to other stores it uses internally
  • Handle component scope inheritance
  • Handle cleaning up scoped stores after they are done being used

Defining Stores

Stores created with pinia's defineStore() are wrapped in a store creator function.

Scopeable Pinia Store

Definition

// vehicle-store.ts
import { defineScopeableStore, type StoreCreatorContext } from 'pinia-scope'

// has the same signature as the pinia defineStore() method, but context is passed to the setup function
export const useVehicleStore = defineScopeableStore('vehicles', ({ scope }: StoreCreatorContext) => {
  // use the same scope
  const engineStore = useEngineStore(scope)  
  
  //...
})

See: Pinia defineStore()

Usage in Component

<script setup lang="ts">
import { useVehicleStore } from 'vehicle-store.ts'

const vehicleStoreScoped = useVehicleStore('my-scope')
vehicleStoreUnScoped.$id // 'my-scope-vehicles'

const vehicleStoreUnScoped = useVehicleStore.unScoped()
vehicleStoreUnScoped.$id // 'vehicles'

// scoped by in this or parent component setComponentScope()
// or unScoped if never set
const vehicleStoreWithComponentScope = useVehicleStore()
</script>

Using Scope Within Stores

// vehicle-store.ts
import { type StoreCreatorContext, defineScopeableStore } from 'pinia-scope'
import { useTireStore } from 'tire-store.ts'

export const useVehicleStore = defineScopeableStore('vehicles', ({ scope }: StoreCreatorContext) => {

  // create a TireStore using the same scope as this VehicleStore
  // if this VehicleStore is unscoped, scope will be '' and the TireStore will also be unscoped
  const tireStore = useTireStore(scope)

  // creates a TireStore with no prefix (an un-scoped version of the TireStore)
  // this would be the same as useTireStore() in a normal Pinia store
  const tireStoreWithoutScope = useTireStore.unScoped()
  
  // ❌ you should never use component scope within a store
  const tireStoreComponentScoped = useTireStore()
  
  // ...
})

Setting Scope Within Components

<script setup lang="ts">
  import { setComponentScope } from 'pinia-scope'
  import { useVehicleStore } from 'vehicle-store.ts'

  // uses scope of a parent component if set
  // if not set the default scope is '' (empty string) aka un-scoped
  const parentScopedVehicleStore = useVehicleStore.injectedScope()

  // set scope for this component and its children
  setComponentScope('order-preview')

  // uses 'order-preview' scope from above
  const vehicleStore = useVehicleStore.injectedScope()
  vehicleStore.$id // 'order-preview-vehicles'
</script>

Best Practices

| Case | | In Component | In Store | |:------------------------------------------|:-----------------------------|:------------------|:-----------------| | Use component injected scope if available | useVehicleStore() | ✅ use by default | ❌ never use | | UnScoped | useVehicleStore.unScoped() | ⚠️ used rarely | ⚠️ used rarely | | Scoped | useVehicleStore(scope) | ⚠️ used rarely | ✅ use by default |

PiniaScopeProvider Component

Scope can also be set via the PiniaScopeProvider component.

Note: Stores are only instantiated once when a component is mounted. The value of a store variable (const vehicleStore = useStore(VehicleStore)) cannot be reactive and therefore cannot change after mounting the component.

To conditionally change the scope of a component, you can mount/unmount components with different scope using v-if="" and the PiniaScopeProvider component.

<script setup lang="ts">
  import { PiniaScopeProvider } from 'pinia-scope'
  import { defineProps } from 'vue'

  const { useScopeA } = defineProps({
    useScopeA: Boolean
  })
</script>
<template>
  <PiniaScopeProvider v-if="useScopeA" scope="scope-a">
    <div>Scope A</div>
  </PiniaScopeProvider>

  <PiniaScopeProvider v-else scope="scope-b">
    <div>Scope B</div>
  </PiniaScopeProvider>
</template>

Scope Options

After a scope is created, its options cannot be changed.

| option | default | description | |:-----------------|:--------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | autoDispose | true | If true, when there are no longer any mounted components using a scope, store.$dispose() will be called on all stores in the scope. Note: store.$dispose() does not delete the state data and the data will be re-used if the scope is created again later. Use autoClearState to do that. | | autoClearState | true | If true, when there are no longer any mounted components using a scope, delete pinia.state.value[store.$id] will be called on all stores in the scope. |

Setting Store Option Defaults

When attaching pinia scope, the default options for a specific scope(s) instead of repeating them.

// in main.js
import { attachPiniaScope } from 'pinia-scope'

// ...
attachPiniaScope(pinia, { 
  scopeOptions: {
    'my-scope': {
      autoDispose: false,
      autoClearState: false,
    }
  } 
})

Setting Scope Options

Scope Options can be se when calling setComponentScope(). If an options argument is provided, This will override any default scope options.

import { setComponentScope } from 'pinia-scope'

const storeOptions = {
  autoDispose: false,
  autoClearState: false,
}

setComponentScope('my-scope', storeOptions)

Scoped Store Id Generation

// by default a scope is prefixed onto a store id with the following function:
const generateScopedStoreId = (scope: string, id: string) => `${scope}-${id}`
// example:
const vehicleStore = useVehicleStore('my-scope')
vehicleStore.$id // 'my-scope-vehicles'

Custom Scoped Store Id Generation

// in main.js
import { attachPiniaScope } from 'pinia-scope'

// ...
attachPiniaScope(pinia, {
  // scoped store id generation can be customized
  scopeNameGenerator: (scope: string, id: string) => `[${scope}]~~[${id}]`
})

// example
const vehicleStore = useVehicleStore('my-scope')
vehicleStore.$id // '[my-scope]~~[vehicles]'

Disabling Component Scope Auto Injection

If you want to disable component scope auto injections and write code more manually you can.

// main.js
import { attachPiniaScope } from 'pinia-scope'

attachPiniaScope(pinia, {
  autoInjectScope: false,
})

// in a component/store
import { useVehicleStore } from 'vehicle-store.ts'

// always behaves the same as useVehicleStore.unScoped()
const vehicleStore = useVehicleStore()
// gets scope from component if set by setComponentScope()
// always behaves the same as useVehicleStore() when autoInjectScope = true
const vehicleStoreUnscoped = useVehicleStore.componentScoped()

// stats the same 
const vehicleStoreScoped = useVehicleStore('my-scope')

Pinia Plugins

The scope can be used to determine plugin options by passing a function as the store options argument.

// main.js
import { defineScopeableStore } from 'pinia-scope'

export const useVehicleStore = defineScopeableStore('vehicles', ({ scope }: StoreCreatorContext) => {
  
  // ...
}, (scope: string) => {
  return {
    somePluginOption: scope === 'foo'
  }
})

API

All API methods (excluding attachPiniaScope()) will work inside a vue component or a context where getActivePinia() will return a result.

attachPiniaScope()

Returns a scoped store.

import { attachPiniaScope } from 'pinia-scope'

attachPiniaScope(pinia, {
  // see above for options details
  autoInjectScope: false,
  scopeDefaults: { ... },
  scopeNameGenerator: () => '',
})

getComponentScope()

Returns the current scope or an empty string if no scope is set. Useful when debugging component scopes.

import { getComponentScope } from 'pinia-scope'

console.log(getComponentScope())

disposeOfPiniaScope()

Equivalent behavior to autoDispose = true and autoClearState = false scope options. If you want to store.$dispose() a scope's stores manually and never clear its state, set autoDispose = false and call disposeOfPiniaScope().

import { disposeOfPiniaScope } from 'pinia-scope'

disposeOfPiniaScope('my-scope')

disposeAndClearStateOfPiniaScope()

Equivalent to autoDispose = true and autoClearState = true scope options. If you want to store.$dispose() a scope's stores manually and clear its state, set autoDispose = false and autoClearState = false call disposeAndClearStateOfPiniaScope().

import { disposeAndClearStateOfPiniaScope } from 'pinia-scope'

disposeAndClearStateOfPiniaScope('my-scope')

getStoreInfo(), getStoreUnscopedId(), getStoreScope()

Inspects a store instance's Unscoped store.$id or current scope.

import { getStoreInfo, getStoreUnscopedId, getStoreScope } from 'pinia-scope'

const store = useMyStore()
const { unscopedId , scope } = getStoreInfo(store)

const unscopedId2 = getStoreUnscopedId(store)
const scope2 = getStoreScope(store)

eachStoreOfPiniaScope()

Loops over each store currently used by a given scope.

import { eachStoreOfPiniaScope } from 'pinia-scope'
import { type Store } from 'vue'

eachStoreOfPiniaScope('my-scope', (store: Store) => {
  console.log(store.$state)
})

Testing / Use Outside Components

To use pinia scope functions outside a component, you can manually set the active pinia instance. The same way you would for normal pinia stores.

import { useStore } from 'pinia-scope'
import { VehicleStore } from 'vehicle-store.ts'
import { createPinia, setActivePinia } from 'pinia'
import { attachPiniaScope } from './pinia-scope'

const pinia = createPinia()

setActivePinia(pinia)
attachPiniaScope(pinia)

const vehicleStore = useStore(VehicleStore)

Examples

Building

$ pnpm install $ pnpm run build

Testing

$ pnpm run test $ pnpm run test:mutation

References

Partly Inspired by: https://github.com/ccqgithub/pinia-di

Releases Automation

  • update package.json file version (example: 1.0.99)
  • manually create a github release with a tag matching the package.json version prefixed with v (example: v1.0.99)
  • npm should be updated automatically