TanStack Store Skill / Design #314
G3NTs
started this conversation in
Show and tell
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I have been using tanstack store now for a few months and I absolutely love it.

I created an devtools tool for it that fits straight into the devtools widget, and have very strict store design for all my components.
THANK U TANSTACK DEVS for creating an amazing tool!
I have basically created an store base class, which my stores now inherits.
There are still some issues with hot reload, but so far this workflow feels so much better then zustand. I very much appreciate I can make classes rather then pure functional declarations.
I can then inherit it.
And then I have all of my businesslogic selectors, and mutations in separate files for them.
here is the skill
name: tanstack-store
description: >
Use this skill when working with any state store in this project. Triggers include:
creating a new store, adding or modifying store operations, writing selectors or mutations,
working with the appState aggregation system, or handling stores that wrap non-serializable
refs (e.g. Three.js scene objects). Covers the full file structure, store class pattern,
operation semantics, separation of concerns, and the cache-object pattern for ref-backed stores.
TanStack Store Skill
Stack
@tanstack/storeBaseStore<T>— not functionalappSettings.storeappState.store(single entry/exit point for save/load)File Structure
Every store lives under
src/stores/<storeName>/and exports throughindex.ts:Responsibilities
*.store.ts*.selectors.ts*.mutations.ts*.io.tsTypes Pattern
Rules:
idis alwaysstring, stable, and unique. Can be URIs or UUIDs, but must be consistent and never change for the same entity.Record<ID, Omit<T, 'id'>>*.types.tsStore Class Pattern
BaseStore<T>lives insrc/stores/base.store.tsand provides all CRUD ops. Every store extends it and scaffolds all methods withsupercalls — this makes extension points explicit and easy to find.Rules:
supercalls — never leave the class body emptyreadis not defined — use selectors insteadsetStatemust only be called insidebase.store.ts— never in subclassesOperation Semantics
insertidalready existsupdateidmissingupsertremoveid; no-op if missing*ManysetStatecallSelectors Pattern
When indexing
StoreState<T>(which isRecord<ID, ...>) with a plainstring, cast toID:Rules: Pure functions. No mutations. No side effects. May read from multiple stores.
Mutations Pattern
Rules: Calls store methods only. No direct state mutation. No async / IO.
IO Pattern
Rules: All side effects and async live here. Parse and validate external data (e.g. with Zod) before passing to the store. Never access
setStatedirectly.appState — Aggregation Store
appStateStoreis the single entry/exit point for persisting or hydrating all stores.Every serializable store must be registered here to participate in save/load.
Registering a new store
toStoreRefwraps any object that implementsget(): unknownandset(next: any): void— satisfied by allBaseStoreinstances.Save / Load (appState.io.ts)
IO iterates
appStateStore.state, callsstore.ref.get()for export andstore.ref.set(data)for import.Cache-Object Store (Ref-backed Objects)
Use this pattern when a store manages state for anything that lives inside the 3D scene (e.g. Three.js meshes, cameras, WebGL contexts) that should not trigger re-renders on internal property changes.
Cache stores extend
CacheStorerather thanBaseStore.How it works
The store holds a cache object — a plain object of values stored by reference. Mutations happen directly on the object in place, bypassing
setStateand store subscriptions. Only replacing the entire cache object triggers a store update. 3D scene objects access this data by reference, so they see mutations immediately without causing re-renders.Since the cache object contains only plain values, it is fully serializable and can be registered in
appStateStorelike any other store.Types
Per-store example
Same scaffolding convention as
BaseStore— all methods explicit withsupercalls:When to use each op
insertreplaceremoveupdateupsertgetCache*ManyChecklist: Adding a New Store
Standard store:
src/stores/<storeName>/with all 6 files*.types.ts— extendStoreBase, define state type viaStoreState<T>, add enums*.store.ts— extendBaseStore<T>; scaffold all methods withsupercalls*.selectors.ts— pure derived data; always includeselectById,selectAll,selectByLabel; caststringtoIDwhen indexing state*.mutations.ts— business logic; call store methods only, no async*.io.ts— async, persistence, external sync; validate external data before passing to store; cast string IDs toIDbefore calling store opsindex.ts— re-export store instance, selectors, mutations, IOappState.mutations.ts → storesCache store:
src/stores/<storeName>/with all 6 files*.types.ts— define cache shape, extendCacheBase<TCache>, define state type viaCacheState<T>*.store.ts— extendCacheStore<T>; scaffold all methods withsupercalls*.selectors.ts— pure derived data; always includeselectById,selectAll,selectByLabel; caststringtoIDwhen indexing state*.mutations.ts— usegetCache(id)for in-place mutations; use store ops for structural changes*.io.ts— async, persistence, external sync; validate external data before passing to storeindex.ts— re-export store instance, selectors, mutations, IOappState.mutations.ts → storesBeta Was this translation helpful? Give feedback.
All reactions