Next-generation state management for reactive applications.
Built on streamix for ultimate performance and simplicity.
If actionstack helps you, please give it a star: https://github.com/epikodelabs/actionstack
- Modular Architecture - Feature-based modules with co-located state and logic
- Reactive Streams - Built on Streamix for high-performance reactive updates
- Action Handlers - No reducers needed - sync actions with state logic
- Thunk Support - Built-in async operations via thunks
- Safe Concurrency - Built-in locking and execution control
- Dynamic Loading - Load/unload modules at runtime
- Type Safety - Full TypeScript support with intelligent inference
npm install @epikodelabs/actionstackimport{createStore,createModule,action,thunk,selector}from'@epikodelabs/actionstack';// Actions with built-in state handlersconstincrement=action('increment',(state: number,payload: number=1)=>state+payload);constreset=action('reset',()=>0);// Create moduleconstcounterModule=createModule({slice: 'counter',initialState: 0,actions: { increment, reset },selectors: {count: selector((state: number)=>state),}});// Initializeconststore=createStore();counterModule.init(store);// Use actions directlycounterModule.actions.increment(5);// Counter: 5counterModule.actions.reset();// Counter: 0// Subscribe to changescounterModule.data$.count().subscribe(count=>{console.log('Counter:',count);});interfaceTodoState{todos: Todo[];loading: boolean;}constaddTodo=action('add',(state: TodoState,text: string)=>({
...state,todos: [...state.todos,{id: Date.now(), text,completed: false}]}));constsetTodos=action('setTodos',(state: TodoState,todos: Todo[])=>({ ...state, todos,loading: false}));constsetLoading=action('setLoading',(state: TodoState,loading: boolean)=>({ ...state, loading }));// Thunk using createThunkconstfetchTodos=thunk('fetchTodos',()=>(dispatch,getState,dependencies)=>{todoModule.actions.setLoading(true);dependencies.todoService.fetchTodos().then(todos=>todoModule.actions.setTodos(todos)).catch(error=>{todoModule.actions.setLoading(false);console.error('Failed to fetch todos:',error);});});// SelectorsconstselectActiveTodos=selector((state: TodoState)=>state.todos.filter(t=>!t.completed));// Module with dependenciesconsttodoModule=createModule({slice: 'todos',initialState: {todos: [],loading: false},actions: { addTodo, setTodos, setLoading, fetchTodos },selectors: { selectActiveTodos },dependencies: {todoService: newTodoService()}});// UsageregisterModule(store,todoModule);todoModule.actions.fetchTodos();// Reactive UI updatestodoModule.data$.selectActiveTodos().subscribe(activeTodos=>{renderTodos(activeTodos);});letstore=createStore();populateStore(store,authModule,uiModule,settingsModule);// Load modules at runtimeconstfeatureModule=createDashboardModule();registerModule(store,featureModule);// Unload when no longer needed and clear stateunregisterModule(store,featureModule,true);import{combineLatest,map,filter,eachValueFrom}from'@epikodelabs/streamix';// Combine data from multiple modulesconstdashboardData$=combineLatest(userModule.data$.selectCurrentUser(),todoModule.data$.selectActiveTodos(),notificationModule.data$.selectUnread()).pipe(map(([user,todos,notifications])=>({
user,todoCount: todos.length,hasNotifications: notifications.length>0})));// React to combined state changesforawait(constdataofeachValueFrom(dashboardData$)){updateDashboard(data);}conststore=createStore({dispatchSystemActions: true,enableGlobalReducers: false,exclusiveActionProcessing: false},applyMiddleware(logger,devtools));The combination of Streamix's query() method and actionstack's thunks creates a uniquely powerful and streamlined approach:
- Reactive by default - Subscribe to streams for UI updates
- Imperative when needed - Use query() for instant access in business logic
- Consistent API - Same selectors work for both reactive and imperative use
- Type-safe - Full TypeScript inference across reactive and sync access patterns
- Performance optimized - Query avoids subscription overhead for one-time reads
| Feature | actionstack | Redux + RTK | Zustand |
|---|---|---|---|
| Bundle Size | Minimal | Large | Small |
| Reactivity | Built-in | Manual | Manual |
| Modules | Native | Manual | Manual |
| Type Safety | Excellent | Good | Good |
| Async Actions | Native | Thunks | Manual |
actionstack is available at no charge under the GNU Affero General Public License v3. Optional paid support, consulting, and custom delivery are available by separate written agreement.
Ready for next-gen state management?
Install from NPMView on GitHub
