ArkhamJS makes state management predictable by using a unidirectional data flow and immutable state updates. It combines the best of Flux architecture with modern event-driven patterns to help you write applications that behave consistently across environments.
ArkhamJS stands out as a truly universal data state management library, designed to work seamlessly across all JavaScript environments. Unlike most state management solutions that focus primarily on web applications, ArkhamJS provides consistent APIs and behavior whether you're building React web apps, React Native mobile applications, or Node.js server environments. This cross-platform capability means you can use the same state management patterns and code across your entire tech stack, significantly reducing context-switching and enabling code sharing between frontend and backend systems.
- π Why ArkhamJS?
- π Multi-Environment Support
- π¦ Packages
- π― Key Features
- β‘ Performance & Bundle Size
- ποΈ Architecture
- π Migration Guide
- π Quick Start
- π§ Advanced Usage
- π€ Community & Support
ArkhamJS helps you write applications that behave consistently across different environments. By enforcing a unidirectional data flow and immutable state updates, it makes state changes predictable and traceable.
- β Predictable state updates with unidirectional data flow
- β Immutable state for consistent behavior
- β Type-safe with first-class TypeScript support
- β Minimal boilerplate compared to Redux
- β Powerful middleware system for extensibility
- β Cross-platform storage integrations
- β Efficient debugging with DevTools and logging
- β Universal compatibility across all JavaScript environments
| Challenge | Traditional Solutions | ArkhamJS Solution |
|---|---|---|
| State Predictability | Complex reducers and actions | Simple store patterns |
| Data Consistency | Manual immutability | Built-in immutable updates |
| State Debugging | External tools, complex setup | Integrated DevTools |
| State Persistence | External libraries | Built-in storage adapters |
| Type Safety | Complex type definitions | First-class TypeScript |
| Cross-Environment | Different solutions per platform | One consistent API |
ArkhamJS works seamlessly across all JavaScript environments with platform-specific optimizations:
import { Flux } from '@nlabs/arkhamjs';
import { useFluxState, useFluxDispatch } from '@nlabs/arkhamjs-utils-react';
// Type-safe store definition
interface UserState {
users: User[];
currentUser: User | null;
}
const UserStore = {
name: 'user',
action: (type: string, data: any, state: UserState): UserState => {
switch (type) {
case 'ADD_USER':
return { ...state, users: [...state.users, data] };
case 'SET_CURRENT_USER':
return { ...state, currentUser: data };
default:
return state;
}
}
};
// React component with hooks
const UserList = (): JSX.Element => {
const users = useFluxState<User[]>('user.users', []);
const dispatch = useFluxDispatch();
const addUser = (user: User): void => {
dispatch({ type: 'ADD_USER', user });
};
return (
<div>
{users.map(user => (
<div key={user.id}>{user.name}</div>
))}
<button onClick={() => addUser({ id: 1, name: 'John' })}>
Add User
</button>
</div>
);
};import { Flux } from '@nlabs/arkhamjs';
import { NativeStorage } from '@nlabs/arkhamjs-storage-native';
// Same store logic, different storage
Flux.init({
name: 'my-app',
stores: [UserStore],
storage: NativeStorage, // AsyncStorage for React Native
storageWait: 300
});
// React Native component
const UserProfile = (): JSX.Element => {
const currentUser = useFluxState<User | null>('user.currentUser', null);
return (
<View>
<Text>Welcome, {currentUser?.name}!</Text>
</View>
);
};import { Flux } from '@nlabs/arkhamjs';
import { NodeStorage } from '@nlabs/arkhamjs-storage-node';
// Server-side state management
Flux.init({
name: 'my-server',
stores: [UserStore],
storage: NodeStorage, // File system or database storage
storageWait: 500
});
// API endpoint using ArkhamJS
const userController = {
createUser: async (req: Request, res: Response): Promise<void> => {
const user = req.body;
// Dispatch action to update state
Flux.dispatch({ type: 'ADD_USER', user });
// State is automatically persisted
res.json({ success: true, user });
},
getUsers: async (req: Request, res: Response): Promise<void> => {
// Get state directly
const users = Flux.getState<User[]>('user.users');
res.json(users);
}
};import { Flux } from '@nlabs/arkhamjs';
// Works in any JavaScript environment
Flux.init({
name: 'my-app',
stores: [UserStore]
});
// Event-driven updates
Flux.on('ADD_USER', (action: { type: string; user: User }) => {
console.log('User added:', action.user);
updateUI();
});
// Direct state access
const updateUI = (): void => {
const users = Flux.getState<User[]>('user.users');
renderUserList(users);
};ArkhamJS is built as a modular ecosystem, allowing you to use only what you need:
- @nlabs/arkhamjs - The main Flux framework with event-driven architecture
- @nlabs/arkhamjs-middleware-logger - Powerful logging and debugging middleware
- @nlabs/arkhamjs-middleware-devtools - Chrome DevTools integration for state inspection
- @nlabs/arkhamjs-middleware-redux - Redux compatibility layer for easy migration
- @nlabs/arkhamjs-storage-browser - Browser storage (localStorage, sessionStorage)
- @nlabs/arkhamjs-storage-native - React Native storage (AsyncStorage)
- @nlabs/arkhamjs-storage-node - Node.js storage (file system, databases)
- @nlabs/arkhamjs-utils-react - React hooks and utilities for seamless integration
- @nlabs/arkhamjs-devtools-extension - Browser extension for enhanced debugging
- @nlabs/arkhamjs-example-ts-react - Complete TypeScript React example
// Single source of truth
const currentState = Flux.getState();
// Predictable state updates
Flux.dispatch({ type: 'ADD_USER', user });Why it matters: A single source of truth makes your application state predictable and easier to debug, test, and reason about.
// Data flows in one direction
Flux.dispatch({ type: 'ADD_USER', user }); // 1. Action dispatched
// 2. Store processes action
// 3. State is updated immutably
// 4. Subscribers are notifiedWhy it matters: Unidirectional data flow ensures that state changes are predictable and traceable, making debugging easier.
// Immutable state updates
const UserStore = {
name: 'user',
action: (type, data, state) => {
switch (type) {
case 'ADD_USER':
return { ...state, users: [...state.users, data] }; // New object returned
default:
return state;
}
}
};Why it matters: Immutability guarantees that state is never modified directly, preventing hard-to-track bugs and enabling time-travel debugging.
// Extensible middleware system
Flux.init({
middleware: [
loggerMiddleware, // Logging
devToolsMiddleware, // DevTools integration
persistMiddleware // State persistence
]
});Why it matters: Middleware provides a clean way to extend functionality for logging, debugging, API calls, and more without cluttering your application logic.
// Consistent storage API across platforms
Flux.init({
storage: BrowserStorage, // Browser
// or: NativeStorage, // React Native
// or: NodeStorage // Node.js
});Why it matters: Built-in storage adapters provide a consistent API for persisting state across different environments.
| Library | Gzipped Size | Minified Size | Features |
|---|---|---|---|
| ArkhamJS | 13.4 KB | 40 KB | Full-featured Flux + middleware + storage |
| Zustand | ~3.2 KB | ~8.5 KB | Minimal, no middleware |
| Jotai | ~4.1 KB | ~11 KB | Atomic model, no middleware |
| Redux Toolkit | ~14 KB | ~41 KB | Full Redux ecosystem |
| MobX | ~7.5 KB | ~23 KB | Core only |
| Recoil | ~8.5 KB | ~25 KB | Facebook, atomic |
ArkhamJS provides more features for similar size - you get a complete state management solution with middleware, storage, and devtools support.
- Tree-shaking enabled for minimal bundle size
- Selective re-renders with state path subscriptions
- Immutable updates prevent unnecessary re-renders
- Event-driven updates only trigger relevant components
- Lazy loading of middleware and storage adapters
ArkhamJS combines the best of Flux architecture with modern event-driven patterns:
class FluxFramework extends EventEmitter {
// Centralized state store (Flux)
private state: Record<string, any> = {};
// Event-driven updates (Pub/Sub)
async dispatch(action: FluxAction): Promise<void> {
// 1. Update stores (Flux pattern)
this.updateStoresState(action.type, action);
// 2. Emit events (Pub/Sub pattern)
this.emit(action.type, action);
this.emit('arkhamjs', this.state);
}
}| Pattern | ArkhamJS | Redux Toolkit | Zustand | Jotai | Valtio |
|---|---|---|---|---|---|
| Immutable Updates | β Full | β Full | β Partial | β Full | β Mutable |
| Event-Driven | β Native | β Actions | β Direct | β Atoms | β Proxy |
| Middleware Support | β Built-in | β Extensive | β No | β No | |
| DevTools | β Plugin | β Built-in | β Basic | β No | β No |
| Storage Integration | β Built-in | β External | β Plugin | β No | β No |
| TypeScript | β First-class | β Excellent | β Good | β Built-in | β Good |
| Multi-Platform | β Universal | β Web-only | β Web-only | β Web-only | β Web-only |
Benefits:
- 70% less boilerplate code
- Same familiar patterns (actions, stores, middleware)
- Better performance with event-driven updates
- Smaller bundle size (13.4KB vs 14KB for Redux Toolkit)
// Before: Redux
const userSlice = createSlice({
name: 'user',
initialState: { users: [] },
reducers: { addUser: (state, action) => { state.users.push(action.payload); } }
});
dispatch(addUser(user));
// After: ArkhamJS
Flux.dispatch({ type: 'ADD_USER', user });Benefits:
- Built-in middleware support (logging, devtools, persistence)
- Event-driven architecture for better reactivity
- Familiar Flux patterns for team consistency
- Storage integration out of the box
// Before: Zustand
const useStore = create((set) => ({
users: [],
addUser: (user: User) => set((state) => ({ users: [...state.users, user] }))
}));
// After: ArkhamJS
Flux.dispatch({ type: 'ADD_USER', user });Benefits:
- Better performance with selective updates
- Middleware support for debugging and persistence
- Predictable state management with immutable updates
- Event-driven architecture for complex interactions
npm install @nlabs/arkhamjsimport { Flux } from '@nlabs/arkhamjs';
// Define your stores with TypeScript
interface UserState {
users: User[];
}
const UserStore = {
name: 'user',
action: (type: string, data: any, state: UserState): UserState => {
switch (type) {
case 'ADD_USER':
return { ...state, users: [...(state.users || []), data] };
default:
return state;
}
}
};
// Initialize Flux
Flux.init({
name: 'my-app',
stores: [UserStore],
debug: true // Enable debugging in development
});
// Dispatch actions
Flux.dispatch({ type: 'ADD_USER', user: { name: 'John', email: 'john@example.com' } });
// Listen to events
Flux.on('ADD_USER', (action: { type: string; user: User }) => {
console.log('User added:', action.user);
});
// Get state
const users: User[] = Flux.getState('user.users');import React, { useEffect, useState } from 'react';
import { Flux } from '@nlabs/arkhamjs';
const UserList = (): JSX.Element => {
const [users, setUsers] = useState<User[]>([]);
useEffect(() => {
// Get initial state
setUsers(Flux.getState<User[]>('user.users') || []);
// Listen for updates
const handleUserAdded = (action: { type: string; user: User }): void => {
setUsers(Flux.getState<User[]>('user.users'));
};
Flux.on('ADD_USER', handleUserAdded);
return () => {
Flux.off('ADD_USER', handleUserAdded);
};
}, []);
return (
<div>
{users.map(user => (
<div key={user.email}>{user.name}</div>
))}
</div>
);
};import { Flux } from '@nlabs/arkhamjs';
import { Logger } from '@nlabs/arkhamjs-middleware-logger';
import { DevTools } from '@nlabs/arkhamjs-middleware-devtools';
Flux.init({
name: 'my-app',
stores: [UserStore],
middleware: [
Logger(), // Automatic action logging
DevTools() // Chrome DevTools integration
]
});import { Flux } from '@nlabs/arkhamjs';
import { BrowserStorage } from '@nlabs/arkhamjs-storage-browser';
Flux.init({
name: 'my-app',
stores: [UserStore],
storage: BrowserStorage, // Automatic localStorage persistence
storageWait: 300 // Debounce storage updates
});import { Flux, FluxAction, FluxStore } from '@nlabs/arkhamjs';
interface User {
name: string;
email: string;
}
interface UserState {
users: User[];
}
const UserStore: FluxStore<UserState> = {
name: 'user',
action: (type: string, data: any, state: UserState): UserState => {
switch (type) {
case 'ADD_USER':
return { ...state, users: [...state.users, data] };
default:
return state;
}
}
};
// Type-safe action dispatching
Flux.dispatch<{ type: 'ADD_USER'; user: User }>({
type: 'ADD_USER',
user: { name: 'John', email: 'john@example.com' }
});- π Documentation - Complete API reference and guides
- π¬ Discord Community - Chat with other developers
- π GitHub Issues - Report bugs and request features
- π Examples - Complete working examples
We welcome contributions! Please see our Contributing Guide for details.
MIT License - see LICENSE file for details.
ArkhamJS is the perfect balance of simplicity and power. Whether you're building a small startup app or a large enterprise system, ArkhamJS scales with your needs while keeping your code clean and maintainable.
Start building with ArkhamJS today and experience the future of state management! π
npm install @nlabs/arkhamjs