Simple, atomic hub for all your React application's state management needs.
- Zero boilerplate - Write less, do more
- No providers - Use state anywhere without wrapping components
- Atomic updates - Only subscribed components re-render
- Framework agnostic - Works with or without React
npm install nucleux
Create a store with atomic state:
import { Store } from 'nucleux';
class CounterStore extends Store {
count = this.atom(0);
increment() {
this.count.value += 1;
}
}
Use it in React components:
import { useStore, useValue } from 'nucleux';
function Counter() {
const store = useStore(CounterStore);
const count = useValue(store.count);
return <button onClick={store.increment}>Count: {count}</button>;
}
That's it! No providers, no reducers, no dispatch.
Atoms are reactive pieces of state. When you change an atom's value, only components subscribed to that specific atom will re-render.
class TodoStore extends Store {
todos = this.atom([]);
filter = this.atom('all');
addTodo(text) {
this.todos.value = [
...this.todos.value,
{ id: Date.now(), text, done: false },
];
}
}
const todoStore = useStore(TodoStore);
// Access: todoStore.addTodo(), todoStore.toggleTodo(), etc.
const todos = useValue(todoStore.todos);
// Or directly: const todos = useValue(TodoStore, 'todos');
const todo = useNucleux(TodoStore);
// Access: todo.todos, todo.filter, todo.addTodo(), etc.
Save state automatically:
class UserStore extends Store {
// Persists to localStorage with key 'user-preferences'
preferences = this.atom({ theme: 'dark' }, 'user-preferences');
}
Compute values from multiple atoms:
class TodoStore extends Store {
todos = this.atom([]);
filter = this.atom('all');
// Automatically updates when todos or filter changes
filteredTodos = this.deriveAtom(
[this.todos, this.filter],
(todos, filter) => {
if (filter === 'done') return todos.filter((t) => t.done);
if (filter === 'pending') return todos.filter((t) => !t.done);
return todos;
},
);
}
Inject other stores:
class NotificationStore extends Store {
userStore = this.inject(UserStore);
notifications = this.atom([]);
constructor() {
super();
// Watch for user changes
this.watchAtom(this.userStore.currentUser, (user) => {
if (user) this.loadNotifications(user.id);
});
}
}
Option 1: Set storage for the entire store:
import AsyncStorage from '@react-native-async-storage/async-storage';
class AppStore extends Store {
storage = AsyncStorage; // All persistent atoms will use AsyncStorage
settings = this.atom({ notifications: true }, 'app-settings');
preferences = this.atom({ theme: 'dark' }, 'user-preferences');
}
Option 2: Set storage per atom:
class AppStore extends Store {
settings = this.atom({ notifications: true }, 'app-settings', {
storage: AsyncStorage,
});
}
Install the polyfill and import it before Nucleux:
npm install react-native-get-random-values
// App.js - Import this first!
import 'react-native-get-random-values';
import { useStore, useValue } from 'nucleux';
import React from 'react';
import { Store, useNucleux } from 'nucleux';
class TodoStore extends Store {
todos = this.atom([]);
addTodo(text) {
const newTodo = { id: Date.now(), text, done: false };
this.todos.value = [...this.todos.value, newTodo];
}
toggleTodo(id) {
this.todos.value = this.todos.value.map((todo) =>
todo.id === id ? { ...todo, done: !todo.done } : todo,
);
}
}
function TodoApp() {
const { todos, addTodo, toggleTodo } = useNucleux(TodoStore);
const [input, setInput] = React.useState('');
const handleAdd = () => {
if (input.trim()) {
addTodo(input.trim());
setInput('');
}
};
return (
<div>
<div>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Add todo..."
/>
<button onClick={handleAdd}>Add</button>
</div>
<ul>
{todos.map((todo) => (
<li key={todo.id}>
<label>
<input
type="checkbox"
checked={todo.done}
onChange={() => toggleTodo(todo.id)}
/>
{todo.text}
</label>
</li>
))}
</ul>
</div>
);
}
this.atom(initialValue, persistKey?, storage?)
- Create reactive statethis.deriveAtom(atoms[], computeFn)
- Create computed statethis.inject(StoreClass)
- Inject another storethis.watchAtom(atom, callback)
- Watch atom changes
useStore(StoreClass)
- Get store instance with methodsuseValue(atom)
oruseValue(StoreClass, 'atomKey')
- Subscribe to atom valueuseNucleux(StoreClass)
- Get all methods and atom values
Requirements: Node ≥14, React ≥16.9.0 (optional)
Marty Roque
- GitHub: @martyroque
- X: @lmproque
- LinkedIn: @lmproque
Copyright © 2025 Marty Roque.