.agents/skills/tanstack-store/SKILL.md
Framework-agnostic, immutable reactive data store with framework adapters for React, Vue, Solid, Angular, and Svelte.
npx skillsauth add em-jones/staccato-toolkit tanstack-storeInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
TanStack Store is a lightweight reactive store (signals-like) that powers the internals of TanStack libraries. Use createStore for state, derived values, and effects. Framework adapters provide reactive hooks for each framework.
Core: @tanstack/store
Solid: @tanstack/solid-store
React: @tanstack/react-store
# Core store
npm install @tanstack/store
# Framework adapters
npm install @tanstack/react-store # React
npm install @tanstack/solid-store # Solid
npm install @tanstack/vue-store # Vue
npm install @tanstack/angular-store # Angular
npm install @tanstack/svelte-store # Svelte
import { createStore } from "@tanstack/store";
const countStore = createStore(0);
const userStore = createStore<{ name: string; email: string }>({
name: "Alice",
email: "[email protected]",
});
// Access state
countStore.state; // 0
userStore.state.name; // 'Alice'
// Set state
countStore.setState((c) => c + 1);
userStore.setState((prev) => ({ ...prev, name: "Bob" }));
// Function updater (immutable update)
countStore.setState((prev) => prev + 1);
userStore.setState((prev) => ({ ...prev, name: "Bob" }));
const unsub = countStore.subscribe(() => {
console.log("Count:", countStore.state);
});
// Cleanup
unsub();
store.state; // Current state
store.prevState; // Previous state
store.listeners; // Set of listener callbacks
import { createStore } from "@tanstack/store";
const count = createStore(5);
const multiplier = createStore(2);
// Derived using createStore with a function
const doubled = createStore(() => count.state * multiplier.state);
console.log(doubled.state); // 10
count.setState(() => 10);
console.log(doubled.state); // 20
// Derived with previous value
const accumulated = createStore((prev) => {
return count.state + (prev?.currentVal ?? 0);
});
const filtered = createStore(() => dataStore.state.filter(matchesFilter(filterStore.state)));
const sorted = createStore(() => [...filtered.state].sort(comparator(sortStore.state)));
const paginated = createStore(() =>
sorted.state.slice(pageStore.state.offset, pageStore.state.offset + pageStore.state.limit),
);
import { createStore, effect } from "@tanstack/store";
const count = createStore(0);
// Create effect - automatically tracks dependencies
const logger = effect(() => {
console.log("Count changed:", count.state);
// Optionally return cleanup function
return () => console.log("Cleaning up");
});
count.setState(() => 1); // logs: "Count changed: 1"
// Cleanup
logger();
const timerEffect = effect(() => {
const id = setInterval(() => {
/* ... */
}, intervalStore.state);
return () => clearInterval(id); // cleanup on unmount
});
Group multiple updates into one notification:
import { batch } from "@tanstack/store";
// Subscribers fire only once with final state
batch(() => {
countStore.setState(() => 1);
nameStore.setState(() => "Alice");
settingsStore.setState((prev) => ({ ...prev, theme: "dark" }));
});
import { useStore } from "@tanstack/react-store";
// Subscribe to full state
function Counter() {
const count = useStore(countStore);
return <button onClick={() => countStore.setState((c) => c + 1)}>{count}</button>;
}
// Subscribe with selector (performance optimization)
function UserName() {
const name = useStore(userStore, (state) => state.name);
return <span>{name}</span>;
}
// Use shallow for object/array selectors
import { shallow } from "@tanstack/react-store";
const items = useStore(todosStore, (state) => state.items, shallow);
import { createStore, useStore } from "@tanstack/solid-store";
// Create store (not 'new Store')
export const store = createStore({ cats: 0, dogs: 0 });
// useStore returns an Accessor - MUST call with () to get value
function Display({ animals }: { animals: "dogs" | "cats" }) {
const count = useStore(store, (state) => state[animals]);
return (
<div>
{animals}: {count()}
</div>
); // Note: count() not count
}
function Button({ animals }: { animals: "dogs" | "cats" }) {
return (
<button
onClick={() => {
store.setState((state) => ({
...state,
[animals]: state[animals] + 1,
}));
}}
>
Increment
</button>
);
}
// Use shallow for object/array selectors
import { shallow } from "@tanstack/solid-store";
const items = useStore(todosStore, (state) => state.items, shallow);
Key Solid differences:
useStore returns an Accessor (signal), must call with () to unwrapcreateStore() not new Store()// stores/counter.ts
import { createStore } from "@tanstack/store";
export const counterStore = createStore(0);
export const doubledCount = createStore(() => counterStore.state * 2);
// Actions as plain functions
export function increment() {
counterStore.setState((c) => c + 1);
}
export function reset() {
counterStore.setState(() => 0);
}
| Framework | Package | Hook/Composable |
| --------- | ------------------------- | --------------------------------------------------- |
| React | @tanstack/react-store | useStore(store, selector?, equalityFn?) |
| Vue | @tanstack/vue-store | useStore(store, selector?) (returns computed ref) |
| Solid | @tanstack/solid-store | useStore(store, selector?) (returns Accessor) |
| Angular | @tanstack/angular-store | injectStore(store, selector?) (returns signal) |
| Svelte | @tanstack/svelte-store | useStore(store, selector?) (returns $state) |
useStore to prevent unnecessary re-rendersshallow when selectors return objects/arrayssetStatebatch for multiple related updatesfn for timers/listenersuseStore() result with () to unwrapstore.state directly instead of using setStateshallowuseStore() result with () (returns Accessor)For fine-grained reactivity without Store abstraction:
import { signal, computed, effect, batch } from "@tanstack/store";
// Create a signal
const count = signal(0);
count(); // get value: 0
count(5); // set value: 5
// Create computed (derived)
const doubled = computed(() => count() * 2);
doubled(); // 10
// Create effect (side effect)
const cleanup = effect(() => {
console.log("Count is:", count());
return () => console.log("Cleanup");
});
// Batch updates
batch(() => {
count(1);
count(2);
});
This is the same underlying implementation used by Store/Derived/Effect.
tools
<!--VITE PLUS START--> # Using Vite+, the Unified Toolchain for the Web This project is using Vite+, a unified toolchain built on top of Vite, Rolldown, Vitest, tsdown, Oxlint, Oxfmt, and Vite Task. Vite+ wraps runtime management, package management, and frontend tooling in a single global CLI called `vp`. Vite+ is distinct from Vite, but it invokes Vite through `vp dev` and `vp build`. ## Vite+ Workflow `vp` is a global binary that handles the full development lifecycle. Run `vp help` to pr
development
Guide for building performant data tables. Uses tanstack-table for table logic (sorting, filtering, pagination) and tanstack-virtual for rendering large datasets efficiently.
development
Expert guidance for building observable, expressive, and fault-tolerant TypeScript applications using the effect-ts/effect ecosystem. Covers Effect<A, E, R> type, error management, dependency injection via Layers, observability (logging, metrics, tracing), concurrency with Fibers, retry/scheduling, Schema validation, Streams, and Sinks.
tools
Complete E2E (end-to-end) and integration testing skill for TypeScript/NestJS projects using Jest, real infrastructure via Docker, and GWT pattern. ALWAYS use this skill when user needs to: **SETUP** - Initialize or configure E2E testing infrastructure: - Set up E2E testing for a new project - Configure docker-compose for testing (Kafka, PostgreSQL, MongoDB, Redis) - Create jest-e2e.config.ts or E2E Jest configuration - Set up test helpers for database, Kafka, or Redis - Configure .env.e2e environment variables - Create test/e2e directory structure **WRITE** - Create or add E2E/integration tests: - Write, create, add, or generate e2e tests or integration tests - Test API endpoints, workflows, or complete features end-to-end - Test with real databases, message brokers, or external services - Test Kafka consumers/producers, event-driven workflows - Working on any file ending in .e2e-spec.ts or in test/e2e/ directory - Use GWT (Given-When-Then) pattern for tests **REVIEW** - Audit or evaluate E2E tests: - Review existing E2E tests for quality - Check test isolation and cleanup patterns - Audit GWT pattern compliance - Evaluate assertion quality and specificity - Check for anti-patterns (multiple WHEN actions, conditional assertions) **RUN** - Execute or analyze E2E test results: - Run E2E tests - Start/stop Docker infrastructure for testing - Analyze E2E test results - Verify Docker services are healthy - Interpret test output and failures **DEBUG** - Fix failing or flaky E2E tests: - Fix failing E2E tests - Debug flaky tests or test isolation issues - Troubleshoot connection errors (database, Kafka, Redis) - Fix timeout issues or async operation failures - Diagnose race conditions or state leakage - Debug Kafka message consumption issues **OPTIMIZE** - Improve E2E test performance: - Speed up slow E2E tests - Optimize Docker infrastructure startup - Replace fixed waits with smart polling - Reduce beforeEach cleanup time - Improve test parallelization where safe Keywords: e2e, end-to-end, integration test, e2e-spec.ts, test/e2e, Jest, supertest, NestJS, Kafka, Redpanda, PostgreSQL, MongoDB, Redis, docker-compose, GWT pattern, Given-When-Then, real infrastructure, test isolation, flaky test, MSW, nock, waitForMessages, fix e2e, debug e2e, run e2e, review e2e, optimize e2e, setup e2e