Overview
This guide helps you migrate between major versions of TanStack Store and understand breaking changes. TanStack Store follows semantic versioning, so major version bumps indicate breaking changes.
Migrating to v0.9.x
Breaking Changes in v0.9.0
Version 0.9.0 introduced significant API changes to improve ergonomics and leverage a new reactive core based on alien-signals .
Major API Changes Version 0.9.0 is a major rewrite with breaking changes. Review this section carefully before upgrading.
Class Constructors → Factory Functions
The biggest change is moving from class constructors to factory functions:
// ❌ Old API (v0.8.x)
import { Store , Derived , Effect } from '@tanstack/store'
const store = new Store ( initialValue )
const derived = new Derived (() => store . state * 2 )
const effect = new Effect (() => {
console . log ( store . state )
})
// ✅ New API (v0.9.x)
import { createStore } from '@tanstack/store'
const store = createStore ( initialValue )
const derived = createStore (() => store . state * 2 )
const subscription = store . subscribe (() => {
console . log ( store . state )
})
Derived Stores
The Derived class has been replaced with computed createStore() calls:
// ❌ Old API
import { Store , Derived } from '@tanstack/store'
const count = new Store ( 10 )
const doubled = new Derived (() => count . state * 2 )
// ✅ New API
import { createStore } from '@tanstack/store'
const count = createStore ( 10 )
const doubled = createStore (() => count . state * 2 )
The new API is simpler: if you pass a function to createStore, it creates a derived (readonly) store. If you pass a value, it creates a mutable store.
Effects → Subscriptions
The Effect class has been removed in favor of the subscribe() method:
// ❌ Old API
import { Store , Effect } from '@tanstack/store'
const store = new Store ( 0 )
const effect = new Effect (() => {
console . log ( 'Count:' , store . state )
})
// Cleanup
effect . destroy ()
// ✅ New API
import { createStore } from '@tanstack/store'
const store = createStore ( 0 )
const { unsubscribe } = store . subscribe (( value ) => {
console . log ( 'Count:' , value )
})
// Cleanup
unsubscribe ()
Type Changes
Import types from the main package:
// ❌ Old API
import { Store , StoreInstance } from '@tanstack/store'
const store : StoreInstance < number > = new Store ( 0 )
// ✅ New API
import { createStore , Store , ReadonlyStore } from '@tanstack/store'
// Mutable store
const store : Store < number > = createStore ( 0 )
// Readonly store
const derived : ReadonlyStore < number > = createStore (() => store . state * 2 )
Observable Interop
The subscription API now follows the Observable spec more closely:
// ❌ Old API
effect . subscribe ({
onNext : ( value ) => console . log ( value ),
onError : ( error ) => console . error ( error ),
})
// ✅ New API
store . subscribe ({
next : ( value ) => console . log ( value ),
error : ( error ) => console . error ( error ),
complete : () => console . log ( 'complete' ),
})
// Or use the simpler callback form
store . subscribe (( value ) => console . log ( value ))
Step-by-Step Migration
Update Dependencies
Update your package.json to the latest version: npm install @tanstack/store@latest
# or
yarn add @tanstack/store@latest
# or
pnpm add @tanstack/store@latest
Replace Store Constructor Calls
Find and replace all new Store() with createStore(): // Before
const store = new Store ( initialValue )
// After
const store = createStore ( initialValue )
Replace Derived Stores
Replace new Derived() with derived createStore(): // Before
const derived = new Derived (() => store . state * 2 )
// After
const derived = createStore (() => store . state * 2 )
Replace Effects with Subscriptions
Replace new Effect() with store.subscribe(): // Before
const effect = new Effect (() => {
console . log ( store . state )
})
effect . destroy () // cleanup
// After
const { unsubscribe } = store . subscribe (( value ) => {
console . log ( value )
})
unsubscribe () // cleanup
Update Type Imports
Update your type imports: // Before
import { Store , StoreInstance } from '@tanstack/store'
// After
import { createStore , Store , ReadonlyStore } from '@tanstack/store'
Test Your Application
Run your test suite to ensure everything works:
Migrating from Other State Libraries
From Redux
TanStack Store can replace Redux with less boilerplate:
// Redux
import { createStore } from 'redux'
const reducer = ( state = { count: 0 }, action ) => {
switch ( action . type ) {
case 'INCREMENT' :
return { count: state . count + 1 }
case 'DECREMENT' :
return { count: state . count - 1 }
default :
return state
}
}
const store = createStore ( reducer )
store . dispatch ({ type: 'INCREMENT' })
// TanStack Store
import { createStore } from '@tanstack/store'
const store = createStore ({ count: 0 })
const increment = () => {
store . setState (( s ) => ({ ... s , count: s . count + 1 }))
}
const decrement = () => {
store . setState (( s ) => ({ ... s , count: s . count - 1 }))
}
increment ()
From Zustand
TanStack Store has a similar API to Zustand:
// Zustand
import { create } from 'zustand'
const useStore = create (( set ) => ({
count: 0 ,
increment : () => set (( state ) => ({ count: state . count + 1 })),
}))
// TanStack Store
import { createStore } from '@tanstack/store'
import { useStore } from '@tanstack/react-store'
const store = createStore ({ count: 0 })
const increment = () => {
store . setState (( s ) => ({ ... s , count: s . count + 1 }))
}
function Component () {
const state = useStore ( store )
return < div onClick ={ increment }>{state. count } </ div >
}
From Jotai
TanStack Store’s atoms work similarly to Jotai:
// Jotai
import { atom , useAtom } from 'jotai'
const countAtom = atom ( 0 )
const doubledAtom = atom (( get ) => get ( countAtom ) * 2 )
// TanStack Store
import { createAtom } from '@tanstack/store'
import { useStore } from '@tanstack/react-store'
const countAtom = createAtom ( 0 )
const doubledAtom = createAtom (() => countAtom . get () * 2 )
function Component () {
const count = useStore ( countAtom )
const doubled = useStore ( doubledAtom )
return < div >{ count } × 2 = { doubled } </ div >
}
From MobX
TanStack Store provides similar reactivity with less boilerplate:
// MobX
import { makeObservable , observable , computed , action } from 'mobx'
import { observer } from 'mobx-react-lite'
class Store {
count = 0
constructor () {
makeObservable ( this , {
count: observable ,
doubled: computed ,
increment: action ,
})
}
get doubled () {
return this . count * 2
}
increment () {
this . count ++
}
}
// TanStack Store
import { createStore } from '@tanstack/store'
import { useStore } from '@tanstack/react-store'
const countStore = createStore ( 0 )
const doubledStore = createStore (() => countStore . state * 2 )
const increment = () => {
countStore . setState (( c ) => c + 1 )
}
function Component () {
const doubled = useStore ( doubledStore )
return < button onClick ={ increment }>{ doubled } </ button >
}
Common Migration Issues
Issue: Cannot call setState on readonly store
// ❌ Error: Property 'setState' does not exist
const derived = createStore (() => source . state * 2 )
derived . setState ( 10 )
Solution : Derived stores (created with a function) are readonly. Update the source store instead:
// ✅ Correct: Update the source
const source = createStore ( 5 )
const derived = createStore (() => source . state * 2 )
source . setState ( 10 ) // derived automatically updates to 20
Subscriptions fire once immediately on creation (this is intentional):
let count = 0
store . subscribe (() => {
count ++ // Fires immediately, then on each update
})
console . log ( count ) // 1 (not 0!)
Solution : Handle the initial call if needed:
let isFirst = true
store . subscribe (() => {
if ( isFirst ) {
isFirst = false
return
}
// Handle subsequent updates
})
Issue: Type errors with generic stores
// ❌ Type error
const store = createStore < User >( null )
Solution : Use union types for nullable values:
// ✅ Correct
const store = createStore < User | null >( null )
Framework-Specific Migration
React
npm install @tanstack/react-store
import { createStore } from '@tanstack/store'
import { useStore } from '@tanstack/react-store'
const store = createStore ( 0 )
function Component () {
const count = useStore ( store )
return < div >{ count } </ div >
}
Vue
npm install @tanstack/vue-store
import { createStore } from '@tanstack/store'
import { useStore } from '@tanstack/vue-store'
const store = createStore ( 0 )
export default {
setup () {
const count = useStore ( store )
return { count }
} ,
}
Svelte
npm install @tanstack/svelte-store
import { createStore } from '@tanstack/store'
import { useStore } from '@tanstack/svelte-store'
const store = createStore ( 0 )
const count = useStore ( store )
< script >
import { count } from './store'
</ script >
< div > { $ count } </ div >
Version History
v0.9.1 (Latest)
Fix : Derived createStore now returns readonly store (#278 )
v0.9.0
Breaking : new Store() → createStore() (#265 )
Breaking : new Derived() → derived createStore()
Breaking : new Effect() → store.subscribe()
New : Uses alien-signals for efficient reactivity
v0.8.1
Fix : Issues with Derived Fields not Retriggering (#274 )
Getting Help
If you encounter migration issues:
GitHub Discussions Ask questions and get help from the community
GitHub Issues Report bugs or migration problems
Discord Join the TanStack Discord for real-time help
Twitter Follow @tanstack for updates
Next Steps