Appearance
Implementing the createPinia Function ​
In this section, we will focus on implementing the createPinia function. The primary role of createPinia is to create the central Pinia instance that will manage all stores. This instance is then made available throughout your Vue application's component tree via Vue's Provide/Inject mechanism. We will proceed by implementing a simplified version, referencing the principles found in the official Pinia source code.
The Pinia Instance ​
The object returned by createPinia is the central Pinia instance. This instance serves as the hub that connects individual stores to your Vue application. It's the object that is 'provided' at the application root and can then be 'injected' into any descendant component.
What information does this central Pinia instance need to hold? While it doesn't directly hold the state values of all stores (that state is managed within each individual store instance created by defineStore), it needs a way to keep track of all the active store instances within the application.
A fundamental requirement for the Pinia instance is a mechanism to register and retrieve store instances dynamically. The simplest way to achieve this is by using a map (like JavaScript's Map or a plain object). This internal map will store the actual, active store instances, typically keyed by the store's unique ID (which is provided in defineStore).
When a component calls a store function (e.g., useCounterStore()), the underlying useStore logic accesses this internal map (_s in the Pinia source) on the injected Pinia instance. If the requested store instance already exists in the map, it's returned. If not, a new instance is created based on the defineStore blueprint, registered in the map using its ID, and then returned. This mechanism ensures that each store ID corresponds to only one active instance per Pinia instance, enabling state sharing across components while importantly respecting SSR isolation (as each SSR request gets its own unique Pinia instance and thus its own set of store instances).
This diagram illustrates how the createPinia function fits into the overall Pinia architecture, particularly its role in providing the central instance:
Let's begin implementing our simplified createPinia!
Project Files for createPinia ​
Navigate to your small-pinia package directory that we created earlier and create the necessary files:
bash
cd ./packages/small-pinia/
touch createPinia.ts types.ts rootStore.tsWe create createPinia.ts for the main function, rootStore.ts to hold shared definitions like the Pinia interface and the symbol used for Provide/Inject, and types.ts for other type definitions we'll add later. For now, we'll define a basic Pinia interface in rootStore.ts sufficient for the createPinia function. The full Store type is complex and will be addressed in a later section.
Implementing createPinia.ts ​
Here is the basic implementation of the createPinia function:
typescript
// createPinia.ts
import { type App } from "vue";
import { type Pinia, piniaSymbol } from "./rootStore";
export function createPinia(): Pinia {
  const pinia: Pinia = {
    install(app: App) {
      // Provide the unique Pinia instance to the entire application tree
      app.provide(piniaSymbol, pinia);
    },
    _s: new Map<string, any>(),
  };
  return pinia;
}typescript
// rootStore.ts
import type { InjectionKey } from "vue";
import type { App } from "vue";
export interface Pinia {
  install: (app: App) => void;
  _s: Map<string, any>;
}
export const piniaSymbol = Symbol("smallPinia") as InjectionKey<Pinia>;Integrating with the Vue Application via a Plugin ​
To make the pinia instance created by createPinia available throughout your Vue application component tree, it needs to be installed as a Vue plugin using app.use(). In a Nuxt 3 application, the standard and recommended place to do this is within a Nuxt plugin file located in the plugins directory. Nuxt automatically registers files in this directory as Vue plugins.
Navigate to your Nuxt application's root (playground/nuxt/) and create the plugins directory and two plugin files:
bash
cd ./nuxt
mkdir plugins
touch plugins/small-pinia.ts plugins/pinia.tsWe create two files – one for the official Pinia instance and one for our small-pinia imitation. This will allow us to easily compare their behavior.
typescript
// plugins/pinia.ts
import { createPinia } from "@pinia/src";
export default defineNuxtPlugin((nuxtApp) => {
  const pinia = createPinia();
  nuxtApp.vueApp.use(pinia);
});
// plugins/small-pinia.ts
import { createPinia } from "@small-pinia/createPinia";
export default defineNuxtPlugin((nuxtApp) => {
  const pinia = createPinia();
  nuxtApp.vueApp.use(pinia);
});Reference: You can check the code I write in the pr link https://github.com/KOBATATU/small-pinia/pull/1
