TanStack Store is, first and foremost, a framework-agnostic signals implementation.
It can be used with any of our framework adapters, but can also be used in vanilla JavaScript or TypeScript. It's currently used to power many of our library's internals.
You'll start by creating a new store instance, which is a wrapper around your data:
import { Store } from '@tanstack/store';
const countStore = new Store(0);
console.log(countStore.state); // 0
countStore.setState(() => 1);
console.log(countStore.state); // 1
import { Store } from '@tanstack/store';
const countStore = new Store(0);
console.log(countStore.state); // 0
countStore.setState(() => 1);
console.log(countStore.state); // 1
This Store can then be used to track updates to your data:
const unsub = countStore.subscribe(() => {
console.log('The count is now:', countStore.state);
});
// Later, to cleanup
unsub();
const unsub = countStore.subscribe(() => {
console.log('The count is now:', countStore.state);
});
// Later, to cleanup
unsub();
You can even transform the data before it's updated:
const count = new Store(12, {
updateFn: (prevValue) => updateValue => {
return updateValue + prevValue;
}
});
count.setState(() => 12);
// count.state === 24
const count = new Store(12, {
updateFn: (prevValue) => updateValue => {
return updateValue + prevValue;
}
});
count.setState(() => 12);
// count.state === 24
And implement a primitive form of derived state:
let double = 0;
const count = new Store(0, {
onUpdate: () => {
double = count.state * 2;
}
})
let double = 0;
const count = new Store(0, {
onUpdate: () => {
double = count.state * 2;
}
})
You can batch updates to a store by using the batch function:
import { batch } from '@tanstack/store';
// countStore.subscribers will only trigger once at the end with the final state
batch(() => {
countStore.setState(() => 1);
countStore.setState(() => 2);
});
import { batch } from '@tanstack/store';
// countStore.subscribers will only trigger once at the end with the final state
batch(() => {
countStore.setState(() => 1);
countStore.setState(() => 2);
});
You can also use the Derived class to create derived values that lazily update when their dependencies change:
const count = new Store(0);
const double = new Derived({
fn: () => count.state * 2,
// Must explicitly list dependencies
deps: [count]
});
// Must mount the derived value to start listening for updates
const unmount = double.mount();
// Later, to cleanup
unmount();
const count = new Store(0);
const double = new Derived({
fn: () => count.state * 2,
// Must explicitly list dependencies
deps: [count]
});
// Must mount the derived value to start listening for updates
const unmount = double.mount();
// Later, to cleanup
unmount();
You can access the previous value of a derived computation by using the prevVal argument passed to the fn function:
const count = new Store(1);
const double = new Derived({
fn: ({ prevVal }) => {
return count.state + (prevVal ?? 0);
},
deps: [count]
});
double.mount();
double.state; // 1
count.setState(() => 2);
double.state; // 3
const count = new Store(1);
const double = new Derived({
fn: ({ prevVal }) => {
return count.state + (prevVal ?? 0);
},
deps: [count]
});
double.mount();
double.state; // 1
count.setState(() => 2);
double.state; // 3
You can access the values of the dependencies of a derived computation by using the prevDepVals and currDepVals arguments passed to the fn function:
const count = new Store(1);
const double = new Derived({
fn: ({ prevDepVals, currDepVals }) => {
return (prevDepVals[0] ?? 0) + currDepVals[0];
},
deps: [count]
});
double.mount();
double.state; // 1
count.setState(() => 2);
double.state; // 3
const count = new Store(1);
const double = new Derived({
fn: ({ prevDepVals, currDepVals }) => {
return (prevDepVals[0] ?? 0) + currDepVals[0];
},
deps: [count]
});
double.mount();
double.state; // 1
count.setState(() => 2);
double.state; // 3
You can also use the Effect class to manage side effects across multiple stores and derived values:
const effect = new Effect({
fn: () => {
console.log('The count is now:', count.state);
},
// Array of `Store`s or `Derived`s
deps: [count],
// Should effect run immediately, default is false
eager: true
})
// Must mount the effect to start listening for updates
const unmount = effect.mount();
// Later, to cleanup
unmount();
const effect = new Effect({
fn: () => {
console.log('The count is now:', count.state);
},
// Array of `Store`s or `Derived`s
deps: [count],
// Should effect run immediately, default is false
eager: true
})
// Must mount the effect to start listening for updates
const unmount = effect.mount();
// Later, to cleanup
unmount();
Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.