Simplify Your React State Management with Nucleux
When an application's complexity grows beyond what local component state can handle, we need a state management solution that fits our architecture and allows our application to scale while preserving code readability. While Redux provides good scalability, it comes with too much boilerplate code. This created opportunities for simpler alternatives like Recoil, Zustand, and Jōtai to gain popularity. Today, I'm excited to introduce Nucleux - an evolution of App's Digest that provides a refreshingly simple approach to state management. What is Nucleux? Nucleux is a simple, atomic state management library based on the publisher-subscriber pattern and inversion-of-control (IoC) container design. Unlike Redux and Zustand where data is stored in global storage outside the component tree, Nucleux stores data in centralized locations called stores, with atomic units of state that your application can subscribe to. Nucleux only triggers strictly-needed, isolated updates for components subscribed to specific atoms - all without creating numerous providers or using derived state selectors. Instead, Nucleux gives you access to particular state anywhere in your application. Note: Nucleux is the evolution of App's Digest, refined based on developer feedback with an improved API and new features while maintaining the core principles of simplicity and atomic updates. Building a Todo App with Nucleux Let's build a simple but practical todo application to see Nucleux in action. Step 1: Installation First, install Nucleux: npm install nucleux # or yarn add nucleux Step 2: Create Your Todo Store Let's create a TodoStore that will manage our todo items: // TodoStore.js import { Store } from 'nucleux'; import { nanoid } from 'nanoid'; class TodoStore extends Store { // Define our atoms (state) todos = this.atom([], 'todos'); // The second parameter enables persistence // Add a todo addTodo(text) { const currentTodos = this.todos.value; this.todos.value = [ ...currentTodos, { id: nanoid(), text, completed: false, createdAt: Date.now() } ]; } // Toggle a todo's completion status toggleTodo(id) { const currentTodos = this.todos.value; this.todos.value = currentTodos.map(todo => todo.id === id ? { ...todo, completed: !todo.completed } : todo ); } // Update todo text updateTodo(id, text) { const currentTodos = this.todos.value; this.todos.value = currentTodos.map(todo => todo.id === id ? { ...todo, text } : todo ); } // Remove a todo removeTodo(id) { const currentTodos = this.todos.value; this.todos.value = currentTodos.filter(todo => todo.id !== id); } // Create a derived atom for ordered todos orderedTodos = this.deriveAtom( [this.todos], (todos) => { return [...todos].sort((a, b) => (a.createdAt > b.createdAt ? 1 : -1)); } ); } export default TodoStore; Step 3: Create React Components Now, let's create our React components: // TodoAdd.jsx import React, { useState } from 'react'; import { useStore } from 'nucleux'; import TodoStore from './TodoStore'; const TodoAdd = () => { const todoStore = useStore(TodoStore); const [value, setValue] = useState(""); const onInputChange = (e) => { setValue(e.target.value); }; const onAddClick = () => { if (value) { todoStore.addTodo(value); setValue(""); } }; return ( Add ); }; // TodoItem.jsx import React from 'react'; import { useStore } from 'nucleux'; import TodoStore from './TodoStore'; const TodoItem = ({ id, text, completed }) => { const todoStore = useStore(TodoStore); const onCheckboxChange = () => { todoStore.toggleTodo(id); }; const onInputChange = (e) => { todoStore.updateTodo(id, e.target.value); }; const onDeleteClick = () => { todoStore.removeTodo(id); }; return ( Delete ); }; // TodoList.jsx import React from 'react'; import { useStore, useValue } from 'nucleux'; import TodoStore from './TodoStore'; import TodoItem from './TodoItem'; const TodoList = () => { const todoStore = useStore(TodoStore); const todos = useValue(todoStore.orderedTodos); return ( {todos.map((todo) => ( ))} ); }; // App.jsx import React from 'react'; import TodoAdd from './TodoAdd'; import TodoList from './TodoList'; const App = () => { return ( State Management with Nucleux My TODO list ); }; export default App; Step 4: That's it! Yes, it's that simple! No additional setup, no wrapping your app in providers, no complex configuration. What Makes Nucleux Different? 1. Minimal Boilerplate Compare the code we wrote with Redux or even Context API with reducers: No action creators No reducers No dispatch functio

When an application's complexity grows beyond what local component state can handle, we need a state management solution that fits our architecture and allows our application to scale while preserving code readability.
While Redux provides good scalability, it comes with too much boilerplate code. This created opportunities for simpler alternatives like Recoil, Zustand, and Jōtai to gain popularity. Today, I'm excited to introduce Nucleux - an evolution of App's Digest that provides a refreshingly simple approach to state management.
What is Nucleux?
Nucleux is a simple, atomic state management library based on the publisher-subscriber pattern and inversion-of-control (IoC) container design. Unlike Redux and Zustand where data is stored in global storage outside the component tree, Nucleux stores data in centralized locations called stores, with atomic units of state that your application can subscribe to.
Nucleux only triggers strictly-needed, isolated updates for components subscribed to specific atoms - all without creating numerous providers or using derived state selectors. Instead, Nucleux gives you access to particular state anywhere in your application.
Note: Nucleux is the evolution of App's Digest, refined based on developer feedback with an improved API and new features while maintaining the core principles of simplicity and atomic updates.
Building a Todo App with Nucleux
Let's build a simple but practical todo application to see Nucleux in action.
Step 1: Installation
First, install Nucleux:
npm install nucleux
# or
yarn add nucleux
Step 2: Create Your Todo Store
Let's create a TodoStore that will manage our todo items:
// TodoStore.js
import { Store } from 'nucleux';
import { nanoid } from 'nanoid';
class TodoStore extends Store {
// Define our atoms (state)
todos = this.atom([], 'todos'); // The second parameter enables persistence
// Add a todo
addTodo(text) {
const currentTodos = this.todos.value;
this.todos.value = [
...currentTodos,
{
id: nanoid(),
text,
completed: false,
createdAt: Date.now()
}
];
}
// Toggle a todo's completion status
toggleTodo(id) {
const currentTodos = this.todos.value;
this.todos.value = currentTodos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
);
}
// Update todo text
updateTodo(id, text) {
const currentTodos = this.todos.value;
this.todos.value = currentTodos.map(todo =>
todo.id === id ? { ...todo, text } : todo
);
}
// Remove a todo
removeTodo(id) {
const currentTodos = this.todos.value;
this.todos.value = currentTodos.filter(todo => todo.id !== id);
}
// Create a derived atom for ordered todos
orderedTodos = this.deriveAtom(
[this.todos],
(todos) => {
return [...todos].sort((a, b) => (a.createdAt > b.createdAt ? 1 : -1));
}
);
}
export default TodoStore;
Step 3: Create React Components
Now, let's create our React components:
// TodoAdd.jsx
import React, { useState } from 'react';
import { useStore } from 'nucleux';
import TodoStore from './TodoStore';
const TodoAdd = () => {
const todoStore = useStore(TodoStore);
const [value, setValue] = useState("");
const onInputChange = (e) => {
setValue(e.target.value);
};
const onAddClick = () => {
if (value) {
todoStore.addTodo(value);
setValue("");
}
};
return (
<div>
<input
placeholder="New Todo"
value={value}
onChange={onInputChange}
/>
<button onClick={onAddClick}>Addbutton>
div>
);
};
// TodoItem.jsx
import React from 'react';
import { useStore } from 'nucleux';
import TodoStore from './TodoStore';
const TodoItem = ({ id, text, completed }) => {
const todoStore = useStore(TodoStore);
const onCheckboxChange = () => {
todoStore.toggleTodo(id);
};
const onInputChange = (e) => {
todoStore.updateTodo(id, e.target.value);
};
const onDeleteClick = () => {
todoStore.removeTodo(id);
};
return (
<div>
<input
type="checkbox"
checked={completed}
onChange={onCheckboxChange}
/>
<input value={text} onChange={onInputChange} />
<button onClick={onDeleteClick}>Deletebutton>
div>
);
};
// TodoList.jsx
import React from 'react';
import { useStore, useValue } from 'nucleux';
import TodoStore from './TodoStore';
import TodoItem from './TodoItem';
const TodoList = () => {
const todoStore = useStore(TodoStore);
const todos = useValue(todoStore.orderedTodos);
return (
<div>
{todos.map((todo) => (
<TodoItem key={todo.id} {...todo} />
))}
div>
);
};
// App.jsx
import React from 'react';
import TodoAdd from './TodoAdd';
import TodoList from './TodoList';
const App = () => {
return (
<div className="App">
<h1>State Management with Nucleuxh1>
<h2>My TODO listh2>
<TodoList />
<TodoAdd />
div>
);
};
export default App;
Step 4: That's it!
Yes, it's that simple! No additional setup, no wrapping your app in providers, no complex configuration.
What Makes Nucleux Different?
1. Minimal Boilerplate
Compare the code we wrote with Redux or even Context API with reducers:
- No action creators
- No reducers
- No dispatch functions
- No providers wrapping your app
2. Atomic Updates
Nucleux only updates components that are specifically subscribed to the atoms that change. If you have a complex UI with many components, only those that use the changed atoms would re-render - not the entire component tree.
3. Derived Atoms Made Simple
The orderedTodos
derived atom automatically updates when the todos
atom changes. This computation happens outside your components, keeping them clean and focused on presentation.
4. Built-in Persistence
By simply passing a string ID to the atom (this.atom([], 'todos')
), we get persistence for free.
5. Dependency Injection
While not shown in this example, Nucleux lets you inject dependencies between stores using the this.inject()
method - a powerful feature for larger applications.
From App's Digest to Nucleux: The Evolution
Nucleux builds on the foundation laid by App's Digest with improvements:
- More intuitive API (
todos.value
instead oftodos.publish()
) - Enhanced dependency injection through IoC container
- Expanded hook support with improved lifecycle management
- Better TypeScript support
- Smaller bundle size
- Enhanced performance optimizations
Conclusion
Nucleux provides a refreshingly simple approach to state management while offering the power needed for complex applications. The publisher-subscriber pattern and IoC container design principles enable clean architecture without the typical complexity.
Give Nucleux a try in your next project:
What state management challenges have you faced that Nucleux might solve? Let me know in the comments!