Observable Store is a front-end state management library that provides a simple yet powerful way to manage state in front-end applications. Front-end state management has become so complex that many of us spend more hours working on the state management code than on the rest of the application. Observable Store has one overall goal - "keep it simple".
The goal of observable store is to provide a small, simple, and consistent way to manage state in any front-end application (Angular, React, Vue.js or any other) while achieving many of the key goals offered by more complex state management solutions. While many front-end frameworks/libraries provide state management functionality, many can be overly complex and are only useable with the target framework/library. Observable Store is simple and can be used with any front-end JavaScript codebase.

npm install @codewithdan/observable-store)npm install @codewithdan/observable-store@2.2.15)To run samples locally from this repo:
Build the core modules first:
bash
npm run build
Then go into any sample and install + start:
bash
cd samples/angular-store
npm install
npm start
Here's a simple example of getting started using Observable Store. Note that if you're using TypeScript you can provide additional details about the store state by using an interface or class (additional examples of that can be found below).
Install the Observable Store package:
npm install @codewithdan/observable-store
Install RxJS - a required peer dependency if your project doesn't already reference it:
npm install rxjs
Create a class that extends ObservableStore. Optionally pass settings into super() in your class's constructor (view Observable Store settings). While this shows a pure JavaScript approach, ObservableStore also accepts a generic that represents the store type. See the Angular example below for more details.
``` javascript export class CustomersStore extends ObservableStore {
constructor() {
super({ /* add settings here */ });
}
} ```
Update the store state using setState(state, action).
javascript
addCustomerToStore(newCustomer) {
this.setState({ customer: newCustomer }, 'add_customer');
}
Retrieve store state using getState().
javascript
getCustomerFromStore() {
this.getState().customer;
}
Subscribe to store changes in other areas of the application by using the store's stateChanged observable.
``` javascript // Create CustomersStore object or have it injected if platform supports that
init() { this.storeSub = this.customersStore.stateChanged.subscribe(state => { if (state) { this.customer = state.customer; } }); }
// Note: Would need to unsubscribe by calling this.storeSub.unsubscribe() // as the target object is destroyed ```
Access store state history in CustomersStore by calling the stateHistory property (this assumes that the trackStateHistory setting is set to true)
javascript
console.log(this.stateHistory);
Observable Store Global Settings
Open the samples folder available at the Github repo and follow the instructions provided in the readme file for any of the provided sample projects.
See the samples folder in the Github repo for examples of using Observable Store with Angular.
Create an Angular application using the Angular CLI or another option.
Install @codewithdan/observable-store:
npm install @codewithdan/observable-store
Add an interface or model object that represents the shape of the data you'd like to add to your store. Here's an example of an interface to store customer state:
typescript
export interface StoreState {
customers: Customer[];
customer: Customer;
}
Add a service (you can optionally call it a store if you'd like) that extends ObservableStore. Pass the interface or model class that represents the shape of your store data in for T as shown next:
``` typescript @Injectable({ providedIn: 'root' }) export class CustomersService extends ObservableStore {
} ```
In the constructor add a call to super(). The store allows you to turn tracking of store state changes on and off using the trackStateHistory property. See a list of Observable Store Settings.
typescript
constructor() {
super({ trackStateHistory: true });
}
Add functions into your service/store to retrieve, store, sort, filter, or perform any actions you'd like. To update the store call setState() and pass the action that is occurring as well as the store state. To get the state out of the store call getState(). Note that store data is immutable and getState() always returns a clone of the store data. Here's a simple example:
``` typescript @Injectable({ providedIn: 'root' }) export class CustomersService extends ObservableStore { sorterService: SorterService;
constructor(sorterService: SorterService) {
const initialState = {
customers: [],
customer: null
}
super({ trackStateHistory: true });
this.setState(initialState, 'INIT_STATE');
this.sorterService = sorterService;
}
get() {
const { customers } = this.getState();
if (customers) {
return of(customers);
}
// call server and get data
// assume async call here that returns observable
return asyncData;
}
add(customer: Customer) {
let state = this.getState();
state.customers.push(customer);
this.setState({ customers: state.customers }, 'ADD_CUSTOMER');
}
remove() {
let state = this.getState();
state.customers.splice(state.customers.length - 1, 1);
this.setState({ customers: state.customers }, 'REMOVE_CUSTOMER');
}
sort(property: string = 'id') {
let state = this.getState();
const sortedState = this.sorterService.sort(state.customers, property);
this.setState({ customers: sortedState }, 'SORT_CUSTOMERS');
}
} ```
While strings are used for actions in the prior examples, you can use string enums (a TypeScript feature) as well if you want to have a set list of actions to choose from:
``` typescript export enum CustomersStoreActions { AddCustomer = 'ADD_CUSTOMER', RemoveCustomer = 'REMOVE_CUSTOMER', GetCustomers = 'GET_CUSTOMERS', SortCustomers = 'SORT_CUSTOMERS' }
// Example of using the enum in a store
add(customer: Customer) {
let state = this.getState();
state.customers.push(customer);
this.setState({ customers: state.customers }, CustomersStoreActions.AddCustomer);
}
```
If you want to view all of the changes to the store you can access the stateHistory property:
typescript
console.log(this.stateHistory);
An example of the state history output is shown next:
typescript
// example stateHistory output
[
{
"action": "INIT_STATE",
"beginState": null,
"endState": {
"customers": [
{
"id": 1545847909628,
"name": "Jane Doe",
"address": {
"street": "1234 Main St.",
"city": "Phoenix",
"state": "AZ",
"zip": "85258"
}
}
],
"customer": null
}
},
{
"action": "ADD_CUSTOMER",
"beginState": {
"customers": [
{
"id": 1545847909628,
"name": "Jane Doe",
"address": {
"street": "1234 Main St.",
"city": "Phoenix",
"state": "AZ",
"zip": "85258"
}
}
],
"customer": null
},
"endState": {
"customers": [
{
"id": 1545847909628,
"name": "Jane Doe",
"address": {
"street": "1234 Main St.",
"city": "Phoenix",
"state": "AZ",
"zip": "85258"
}
},
{
"id": 1545847921260,
"name": "Fred",
"address": {
"street": "1545847921260 Main St.",
"city": "Phoenix",
"state": "AZ",
"zip": "85258"
}
}
],
"customer": null
}
}
]
Any component can be notified of changes to the store state by injecting the store and then subscribing to the stateChanged observable:
``` typescript customers: Customer[]; storeSub: Subscription;
constructor(private customersService: CustomersService) { }
ngOnInit() { // If using async pipe (recommend renaming customers to customers$) // this.customers$ = this.customersService.stateChanged;
// Can subscribe to stateChanged observable of the store
this.storeSub = this.customersService.stateChanged.subscribe(state => {
if (state) {
this.customers = state.customers;
}
});
// Can call service/store to get data directly
// It won't fire when the store state changes though in this case
//this.storeSub = this.customersService.get().subscribe(custs => this.customers = custs);
} ```
Unsubscribe when the component is destroyed to avoid memory leaks:
typescript
ngOnDestroy() {
if (this.storeSub) {
this.storeSub.unsubscribe();
}
}
See the samples/react-store folder in the Github repo for a complete example.
Create a React application using Vite or another tool:
bash
npm create vite@latest my-app -- --template react
Install @codewithdan/observable-store and RxJS:
bash
npm install @codewithdan/observable-store rxjs
Create a store class that extends ObservableStore:
``` javascript import { ObservableStore } from '@codewithdan/observable-store';
class CustomersStore extends ObservableStore {
constructor() {
super({ trackStateHistory: true });
}
fetchCustomers() {
return fetch('/customers.json')
.then(response => response.json())
.then(customers => {
this.setState({ customers }, 'GET_CUSTOMERS');
return customers;
});
}
getCustomers() {
const state = this.getState();
if (state && state.customers) {
return Promise.resolve(state.customers);
}
return this.fetchCustomers();
}
}
export default new CustomersStore();
$ claude mcp add Observable-Store \
-- python -m otcore.mcp_server <graph>