Skip to main content

Quick Start

This page builds a counter store and reads it from a functional component. For class components, see Class Components.

1. Wrap your app in <NekutaStore>

Every store lives on a Nekuta instance — the equivalent of a Pinia instance. <NekutaStore> creates one and makes it available to everything below it, in one step:

App.tsx
import { NekutaStore } from '@devtools/core';

export function App() {
return (
<NekutaStore>
<Counter />
</NekutaStore>
);
}

2. Define a store

stores/counterStore.ts
import { defineStore } from '@devtools/core';

export const useCounterStore = defineStore({
id: 'counter',
state: () => ({ count: 0 }),
getters: {
doubleCount: (state) => state.count * 2
},
actions: {
increment(this: { count: number }, by = 1) {
this.count += by;
}
}
});

id must be unique across your app — it's the key Nekuta uses to store this store's state on the Nekuta instance. state is a factory function (called once per Nekuta instance) so each instance gets its own fresh copy, never a shared reference.

3. Read and write it from a component

Counter.tsx
import { useStore } from '@devtools/core';
import { useCounterStore } from './stores/counterStore';

export function Counter() {
const counter = useStore(useCounterStore);

return (
<div>
<p>
{counter.count} (double: {counter.doubleCount})
</p>
<button onClick={() => counter.increment()}>+1</button>
</div>
);
}

useStore() is the hook — it takes the store definition (useCounterStore, what defineStore() returned) and gives you back the live store, subscribed for re-renders. State, getters, and actions are all accessed directly off the object it returns: counter.count, counter.doubleCount, counter.increment().

Mutating state directly also works, the same as Pinia:

<button onClick={() => counter.count++}>+1 (direct mutation)</button>

Next steps