Exome
🔅 State manager for deeply nested states
Install / Use
/learn @Marcisbee/ExomeREADME
<a href="../../"><img src="logo/logo-title-dark.png" width="220" /></a>
<a href="https://github.com/Marcisbee/exome/actions"> <img alt="CI" src="https://img.shields.io/github/actions/workflow/status/Marcisbee/exome/main.yml?branch=main&style=flat-square" /> </a> <a href="https://www.npmjs.com/package/exome"> <img alt="npm" src="https://img.shields.io/npm/v/exome.svg?style=flat-square" /> </a> <a href="https://jsr.io/@exome/exome"> <img alt="jsr" src="https://jsr.io/badges/@exome/exome?style=flat-square" /> </a> <a href="https://bundlephobia.com/result?p=exome"> <img alt="package size" src="https://deno.bundlejs.com/?q=exome&badge=&badge-style=flat-square" /> </a>State manager for deeply nested states. Includes integration for React, Preact, Vue, Svelte, Solid, Lit, Rxjs, Angular & No framework. Can be easily used in microfrontends architecture.
Features
- 📦 Small: Just 1 KB minizipped
- 🚀 Fast: Uses no diffing of state changes see benchmarks
- 😍 Simple: Uses classes as state, methods as actions
- 🛡 Typed: Written in strict TypeScript
- 🔭 Devtools: Redux devtools integration
- 💨 Zero dependencies
// store/counter.ts
import { Exome } from "exome"
export class Counter extends Exome {
public count = 0
public increment() {
this.count += 1
}
}
export const counter = new Counter()
// components/counter.tsx
import { useStore } from "exome/react"
import { counter } from "../stores/counter.ts"
export default function App() {
const { count, increment } = useStore(counter)
return (
<h1 onClick={increment}>{count}</h1>
)
}
Table of contents
- Core concepts
- Usage
- Integration
- Redux devtools
- API
- FAQ
- Benchmarks
- Motivation
Installation
To install the stable version:
npm install --save exome
This assumes you are using npm as your package manager.
Core concepts
Any piece of state you have, must use a class that extends Exome.
Stores
Store can be a single class or multiple ones. I'd suggest keeping stores small, in terms of property sizes.
State values
Remember that this is quite a regular class (with some behind the scenes logic). So you can write you data inside properties however you'd like. Properties can be public, private, object, arrays, getters, setters, static etc.
Actions
Every method in class is considered as an action. They are only for changing state. Whenever any method is called in Exome it triggers update to middleware and updates view components. Actions can be regular methods or even async ones.
If you want to get something from state via method, use getters.
Usage
Library can be used without typescript, but I mostly recommend using it with typescript as it will guide you through what can and cannot be done as there are no checks without it and can lead to quite nasty bugs.
To create a typed store just create new class with a name of your choosing by extending Exome class exported from exome library.
import { Exome } from "exome"
// We'll have a store called "CounterStore"
class CounterStore extends Exome {
// Lets set up one property "count" with default value "0"
public count = 0
// Now lets create action that will update "count" value
public increment() {
this.count += 1
}
}
That is the basic structure of simple store. It can have as many properties as you'd like. There are no restrictions.
Now we should create an instance of CounterStore to use it.
const counter = new CounterStore()
Nice! Now we can start using counter state.
Integration
React
Use useStore() from exome/react to get store value and re-render component on store change.
import { useStore } from "exome/react"
import { counter } from "../stores/counter.ts"
export function Example() {
const { count, increment } = useStore(counter)
return <button onClick={increment}>{count}</button>
}
Preact
Use useStore() from exome/preact to get store value and re-render component on store change.
import { useStore } from "exome/preact"
import { counter } from "../stores/counter.ts"
export function Example() {
const { count, increment } = useStore(counter)
return <button onClick={increment}>{count}</button>
}
Vue
Use useStore() from exome/vue to get store value and re-render component on store change.
<script lang="ts" setup>
import { useStore } from "exome/vue";
import { counter } from "./store/counter.ts";
const { count, increment } = useStore(counter);
</script>
<template>
<button @click="increment()">{{ count }}</button>
</template>
Svelte
Use useStore() from exome/svelte to get store value and re-render component on store change.
<script>
import { useStore } from "exome/svelte"
import { counter } from "./store/counter.js"
const { increment } = counter
const count = useStore(counter, s => s.count)
</script>
<main>
<button on:click={increment}>{$count}</button>
</main>
Solid
Use useStore() from exome/solid to get store value and update signal selector on store change.
import { useStore } from "exome/solid"
import { counter } from "../stores/counter.ts"
export function Example() {
const count = useStore(counter, s => s.count)
return <button onClick={counter.increment}>{count}</button>
}
Lit
Use StoreController from exome/lit to get store value and re-render component on store change.
import { StoreController } from "exome/lit"
import { counter } from "./store/counter.js"
@customElement("counter")
class extends LitElement {
private counter = new StoreController(this, counter);
render() {
const { count, increment } = this.counter.store;
return html`
<h1 @click=${increment}>${count}</h1>
`;
}
}
Rxjs
Use observableFromExome from exome/rxjs to get store value as Observable and trigger it when it changes.
import { observableFromExome } from "exome/rxjs"
import { counter } from "./store/counter.js"
observableFromExome(countStore)
.pipe(
map(({ count }) => count),
distinctUntilChanged()
)
.subscribe((value) => {
console.log("Count changed to", value);
});
setInterval(counter.increment, 1000);
Angular
signals (>=16)
Use useStore from exome/angular to get store value and update signal selector on store change.
import { useStore } from "exome/angular"
import { counter } from "./store/counter.ts"
@Component({
selector: 'my-app',
template: `
<h1 (click)="increment()">
{{count}}
</h1>
`,
})
export class App {
public count = useStore(counter, (s) => s.count);
public increment() {
counter.increment();
}
}
observables (<=15)
Angular support is handled via rxjs async pipes!
Use observableFromExome from exome/rxjs to get store value as Observable and trigger it when it changes.
import { observableFromExome } from "exome/rxjs"
import { counter } from "./store/counter.ts"
@Component({
selector: 'my-app',
template: `
<h1 *ngIf="(counter$ | async) as counter" (click)="counter.increment()">
{{counter.count}}
</h1>
`,
})
export class App {
public counter$ = observableFromExome(counter)
}
No framework
Use subscribe from exome to get store value in subscription callback event when it changes.
import { subscribe } from "exome"
import { counter } from "./store/counter.js"
const unsubscribe = subscribe(counter, ({ count }) => {
console.log("Count changed to", count)
})
setInterval(counter.increment, 1000)
setTimeout(unsubscribe, 5000)
Redux devtools
You can use redux devtools extension to explore Exome store chunk by chunk.
Just add exomeReduxDevtools middleware via addMiddleware function exported by library before you start defining store.
import { addMiddleware } from 'exome'
import { exomeReduxDevtools } from 'exome/devtools'
addMiddleware(
exomeReduxDevtools({
name: 'Exome Playground'
})
)
It all will look something like this:

API
Exome
A class with underlying logic that handles state changes. Every store must be extended from this class.
abstract class Exome {}
useStore
Is function exported from "exome/react".
function useStore<T extends Exome>(store: T): Readonly<T>
Arguments
store(Exome): State to watch changes from. Without Exome being passed in this function, react component will not be updated when particular Exome updates.
Returns
- Exome: Same store is returned.
Example
import { useStore } from "exome/react"
const counter = new Counter()
function App() {
const { count, increment } = useStore(counter)
return <button onClick={increment}>{count}</button>
}
onAction
Function that calls callback whenever specific action on Exome is called.
function onAction(store: typeof Exome): Unsubscribe
Arguments
store(Exome constructor): Store that has desired action to listen to.- `actio
Related Skills
bluebubbles
342.0kUse when you need to send or manage iMessages via BlueBubbles (recommended iMessage integration). Calls go through the generic message tool with channel="bluebubbles".
node-connect
342.0kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
slack
342.0kUse when you need to control Slack from OpenClaw via the slack tool, including reacting to messages or pinning/unpinning items in Slack channels or DMs.
frontend-design
84.7kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
