cerebral
Declarative state and side effects management for popular JavaScript frameworks
Top Related Projects
A JS library for predictable global state management
Simple, scalable state management.
🗃️ Centralized State Management for Vue.js.
Reactive State for Angular
Application Architecture for Building User Interfaces
A reactive programming library for JavaScript
Quick Overview
Cerebral is a state management library for JavaScript applications. It provides a declarative and centralized approach to managing application state, side effects, and business logic. Cerebral aims to simplify complex state management scenarios and improve developer productivity.
Pros
- Centralized state management with a single state tree
- Powerful debugging tools with time-travel debugging
- Supports both React and Vue.js frameworks
- Modular architecture allowing for easy scaling of large applications
Cons
- Steeper learning curve compared to simpler state management solutions
- Requires additional setup and configuration
- May be overkill for small to medium-sized applications
- Less community support compared to more popular state management libraries
Code Examples
- Defining a state tree and actions:
import { Controller, Module } from 'cerebral'
const app = Module({
state: {
count: 0
},
signals: {
increment: ({ state }) => state.set('count', state.get('count') + 1),
decrement: ({ state }) => state.set('count', state.get('count') - 1)
}
})
const controller = Controller(app)
- Using Cerebral with React:
import React from 'react'
import { connect } from '@cerebral/react'
const Counter = connect(
{
count: state`count`,
increment: signal`increment`,
decrement: signal`decrement`
},
function Counter({ count, increment, decrement }) {
return (
<div>
<h1>{count}</h1>
<button onClick={() => increment()}>+</button>
<button onClick={() => decrement()}>-</button>
</div>
)
}
)
- Defining and using computed state:
import { Compute } from 'cerebral'
const doubleCount = Compute(
state`count`,
(count) => count * 2
)
const DoubleCounter = connect(
{
doubleCount
},
function DoubleCounter({ doubleCount }) {
return <h2>Double count: {doubleCount}</h2>
}
)
Getting Started
To get started with Cerebral, follow these steps:
-
Install Cerebral and its React bindings:
npm install cerebral @cerebral/react
-
Create a Cerebral controller:
import { Controller, Module } from 'cerebral' const app = Module({ state: { // Your initial state }, signals: { // Your signals (actions) } }) const controller = Controller(app)
-
Wrap your React app with the Cerebral provider:
import { Container } from '@cerebral/react' ReactDOM.render( <Container controller={controller}> <App /> </Container>, document.getElementById('root') )
-
Connect your components to the Cerebral state:
import { connect } from '@cerebral/react' export default connect( { // Map state and signals }, function YourComponent(props) { // Your component logic } )
Competitor Comparisons
A JS library for predictable global state management
Pros of Redux
- Larger ecosystem and community support
- More widespread adoption, leading to better documentation and resources
- Simpler learning curve for developers familiar with Flux architecture
Cons of Redux
- More boilerplate code required for setup and state management
- Can become complex in large applications with many actions and reducers
- Less opinionated, which may lead to inconsistent implementations across projects
Code Comparison
Redux:
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
default:
return state;
}
};
Cerebral:
export const increment = ({ state }) => {
state.set('count', state.get('count') + 1);
};
Redux typically uses switch statements in reducers to handle actions, while Cerebral uses a more declarative approach with state mutations. Cerebral's syntax is often more concise, but Redux's explicit nature can make it easier to understand the flow of data in larger applications.
Both libraries aim to simplify state management in complex applications, but they take different approaches. Redux focuses on predictability and a single source of truth, while Cerebral emphasizes declarative state changes and side-effect management. The choice between them often depends on project requirements and team preferences.
Simple, scalable state management.
Pros of MobX
- Simpler learning curve and more intuitive API
- Better performance for large-scale applications
- More flexible and less opinionated about application structure
Cons of MobX
- Less predictable state changes due to mutable state
- Potential for overuse of observables, leading to unnecessary re-renders
- Lack of built-in time-travel debugging
Code Comparison
MobX:
import { makeObservable, observable, action } from "mobx";
class TodoStore {
todos = [];
constructor() {
makeObservable(this, {
todos: observable,
addTodo: action
});
}
addTodo(text) {
this.todos.push({ text, completed: false });
}
}
Cerebral:
import { state, sequence } from 'cerebral'
export const state = {
todos: []
}
export const addTodo = sequence('Add todo', [
({ state, props }) => {
state.todos.push({ text: props.text, completed: false })
}
])
Both MobX and Cerebral are state management libraries for JavaScript applications, but they have different approaches. MobX focuses on reactive programming and uses observables, while Cerebral uses a more explicit signal-state-model architecture. MobX is generally easier to learn and integrate into existing projects, while Cerebral provides a more structured approach to state management.
🗃️ Centralized State Management for Vue.js.
Pros of Vuex
- Tightly integrated with Vue.js ecosystem, providing seamless state management for Vue applications
- Simple and intuitive API, making it easy to learn and use for developers familiar with Vue
- Extensive documentation and large community support
Cons of Vuex
- Limited to Vue.js applications, not suitable for other frameworks or vanilla JavaScript
- Can become complex in large applications with many modules and nested states
Code Comparison
Vuex:
const store = new Vuex.Store({
state: { count: 0 },
mutations: {
increment(state) { state.count++ }
}
})
Cerebral:
const controller = Controller({
state: { count: 0 },
signals: {
increment: [({ state }) => state.set('count', state.get('count') + 1)]
}
})
Both Vuex and Cerebral provide state management solutions, but Vuex is specifically designed for Vue.js applications, while Cerebral is framework-agnostic. Vuex offers a more straightforward API for simple use cases, whereas Cerebral provides a more flexible and powerful approach to complex state management scenarios.
Reactive State for Angular
Pros of NgRx
- Tightly integrated with Angular, providing a seamless experience for Angular developers
- Extensive ecosystem with additional libraries like @ngrx/effects and @ngrx/entity
- Strong community support and regular updates
Cons of NgRx
- Steeper learning curve, especially for developers new to Redux concepts
- More boilerplate code required compared to Cerebral
- Can be overkill for smaller applications
Code Comparison
NgRx:
@Component({...})
export class MyComponent {
data$ = this.store.select(selectData);
constructor(private store: Store) {}
loadData() {
this.store.dispatch(loadData());
}
}
Cerebral:
connect({
data: state`data`,
loadData: signal`loadData`
},
function MyComponent({ data, loadData }) {
return <button onClick={() => loadData()}>Load Data</button>
}
Both NgRx and Cerebral are state management solutions, but they differ in their approach and ecosystem. NgRx is specifically designed for Angular applications, while Cerebral is framework-agnostic. NgRx follows a more traditional Redux pattern, whereas Cerebral uses a unique signal-state model. The code comparison shows how actions/signals are dispatched in each library, with NgRx requiring more setup but offering tighter TypeScript integration.
Application Architecture for Building User Interfaces
Pros of Flux
- Simpler learning curve due to its straightforward unidirectional data flow concept
- Better integration with React ecosystem, being developed by Facebook
- More widespread adoption and larger community support
Cons of Flux
- Less flexibility in handling complex state management scenarios
- Lack of built-in tools for handling side effects and asynchronous operations
- Requires more boilerplate code for larger applications
Code Comparison
Flux:
var AppDispatcher = require('./AppDispatcher');
var TodoConstants = require('./TodoConstants');
var TodoActions = {
create: function(text) {
AppDispatcher.dispatch({
actionType: TodoConstants.TODO_CREATE,
text: text
});
}
};
Cerebral:
import {set} from 'cerebral/operators'
import {state} from 'cerebral/tags'
export const createTodo = ({props, store}) => {
store.set(state`todos.${props.id}`, props.todo)
}
Cerebral offers a more declarative approach to state management, with built-in operators and tags for easier state manipulation. Flux, on the other hand, relies on a more traditional dispatch-based system, which can be more verbose but is also more familiar to developers coming from other frameworks.
A reactive programming library for JavaScript
Pros of RxJS
- Widely adopted and mature ecosystem with extensive documentation
- Powerful operators for complex data transformations and event handling
- Cross-platform support (browser, Node.js, and mobile)
Cons of RxJS
- Steeper learning curve due to its functional reactive programming paradigm
- Can lead to increased complexity for simple use cases
- Potential performance overhead for large-scale applications
Code Comparison
RxJS:
import { fromEvent } from 'rxjs';
import { debounceTime, map } from 'rxjs/operators';
fromEvent(input, 'input')
.pipe(
debounceTime(300),
map(event => event.target.value)
)
.subscribe(value => console.log(value));
Cerebral:
import { state, sequence } from 'cerebral';
export const updateInput = sequence('updateInput', [
({ props, store }) => {
store.set(state.inputValue, props.value);
},
]);
RxJS excels in handling complex event streams and asynchronous operations, while Cerebral provides a more structured approach to state management. RxJS offers greater flexibility but may require more boilerplate, whereas Cerebral aims for simplicity in defining state changes. The choice between the two depends on the specific requirements of your project and your team's familiarity with reactive programming concepts.
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
Cerebral
A declarative state and side effects management solution for popular JavaScript frameworks
Project Status
Cerebral 5.3 is the latest release, bringing modern API patterns and significant improvements:
- Supports full type safety in your application
- Updated integrations for the latest view libraries (React, Vue, etc.)
- Compatibility with modern lint, build and publish tools
- Updated documentation
- Bug fixes and performance improvements
The project is currently in maintenance mode. We accept PRs and Issues for bug fixes. While we're not actively developing new features, if you have a reasonable feature request, please create an issue. If we agree that the request adds value and it receives community support (indicated by thumbs up), we may consider implementing it.
Framework Support
Cerebral works seamlessly with all major frontend frameworks:
- React: Compatible with React 16.3 through React 19
- Vue: Full support for Vue 3, with backward compatibility for Vue 2.6+
- Preact: Works with Preact X (v10+) and older v8
- Inferno: Supports v4 through v9
- Angular: Compatible with Angular 14 through 19
Getting Started
Installation
# Using npm
npm install cerebral
# Using yarn
yarn add cerebral
# Using pnpm
pnpm add cerebral
Basic Example (React)
import React from 'react'
import { createApp } from 'cerebral'
import { Container, connect } from '@cerebral/react'
import { state, sequences } from 'cerebral'
// Create an action
const increment = ({ store, get }) => {
// Use 'store' to update state
store.set(state`count`, get(state`count`) + 1)
}
// Create app with state and sequences
const app = createApp({
state: {
count: 0
},
sequences: {
increment: [increment]
}
})
// Connect component to Cerebral
const Counter = connect(
{
count: state`count`,
increment: sequences`increment`
},
function Counter({ count, increment }) {
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => increment()}>Increment</button>
</div>
)
}
)
// Provide the app to your component tree
const App = () => (
<Container app={app}>
<Counter />
</Container>
)
For more detailed examples, check the documentation. If you prefer the proxy syntax (state.count
instead of state`count`
), see our proxy documentation.
Documentation
You can find the Cerebral documentation at cerebraljs.com.
Contribute
Cerebral is organized as a monorepo to make contributions easier:
- Clone the repo:
git clone https://github.com/cerebral/cerebral.git
- Install dependencies:
npm install
(from the root folder)
You don't need to run npm install
in each package directory - the monorepo setup handles this for you.
Testing
Run all tests from the root directory:
npm test
Or run tests for a specific package:
# Navigate to the package
cd packages/cerebral
# Run tests for just this package
npm test
Making Changes
-
Create a branch for your changes
-
Make your code changes and add tests
-
Commit from the root using our guided format:
npm run commit
-
Push your branch and create a pull request on GitHub
Using the monorepo for development
If you want to use your local Cerebral code in your own project, you can create symlinks to the packages:
# From your project root
ln -s ../../cerebral/packages/node_modules/cerebral/ node_modules/
ln -s ../../cerebral/packages/node_modules/@cerebral/ node_modules/
Remember to remove these links before installing from npm:
unlink node_modules/cerebral
unlink node_modules/@cerebral
Release process
- Review and merge PRs into
next
branch. It is safe to use "Update branch", the commit created by Github will not be part ofnext
history - From command line:
git switch next
git pull
npm install # make sure any new dependencies are installed
npm run release -- --dry-run --print-release # and check release notes
git switch master
git pull
git merge --ff-only next
git push
Top Related Projects
A JS library for predictable global state management
Simple, scalable state management.
🗃️ Centralized State Management for Vue.js.
Reactive State for Angular
Application Architecture for Building User Interfaces
A reactive programming library for JavaScript
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot