Framework Adapters
Thin bridges connecting StateLoom's Subscribable<T> contract to framework-specific reactivity systems. Each adapter is 50-200 lines of pure bridging code with zero business logic.
Available Adapters
| Package | Framework | Bridging Strategy | Size |
|---|---|---|---|
@stateloom/react | React 18+ | useSyncExternalStore | ~1.1 KB |
@stateloom/vue | Vue 3.2+ | shallowRef + effect() | ~1.5 KB |
@stateloom/solid | Solid.js 1+ | createSignal + dual subscription | ~0.2 KB |
@stateloom/svelte | Svelte 4/5 | Native store contract (Readable/Writable) | ~0.1 KB |
@stateloom/angular | Angular 17+ | Angular Signals + RxJS Observable | ~0.3 KB |
The Universal Contract
Every signal, computed, store, and atom in StateLoom implements the Subscribable<T> interface:
interface Subscribable<T> {
get(): T;
subscribe(callback: (value: T) => void): () => void;
}Each framework adapter bridges this single interface to the framework's native reactivity primitives. Adapters contain no state management logic -- they are purely mechanical bridges.
Feature Comparison
| Feature | React | Vue | Solid | Svelte | Angular |
|---|---|---|---|---|---|
| Signal bridging | useSignal() | useSignal() | useSignal() | toReadable() | toAngularSignal() |
| Store with selector | useStore() | useStore() | useStore() | computed() + toReadable() | injectStore() |
| Atom hooks | useAtom() / useAtomValue() / useSetAtom() | via useSignal() | via useSignal() | via toReadable() | via toAngularSignal() |
| Proxy integration | useSnapshot() | -- | -- | -- | -- |
| Two-way binding | -- | -- | -- | toWritable() + bind:value | -- |
| RxJS bridge | -- | -- | -- | -- | toObservable() / fromObservable() |
| SSR scope | ScopeProvider | stateloomPlugin / ScopeProvider / provideScope() | ScopeProvider | setScope() / getScope() | provideStateloomScope() / injectScope() |
| Concurrent rendering | useSyncExternalStore | N/A | N/A | N/A | N/A |
| Auto-cleanup | React lifecycle | onScopeDispose | onCleanup | Svelte store contract | DestroyRef.onDestroy |
| Sub-path imports | /store, /atom, /proxy | -- | -- | -- | -- |
Paradigm Support by Adapter
| Paradigm | React | Vue | Solid | Svelte | Angular |
|---|---|---|---|---|---|
| Core signals | useSignal(signal) | useSignal(signal) | useSignal(signal) | toReadable(signal) / toWritable(signal) | toAngularSignal(signal) |
| Store | useStore(store, sel) | useStore(store, sel) | useStore(store, sel) | toReadable(computed(() => store.getState().slice)) | injectStore(store, sel) |
| Atom | useAtom(atom) | useSignal(atom) | useSignal(atom) | toReadable(atom) | toAngularSignal(atom) |
| Proxy | useSnapshot(proxy) | -- | -- | -- | -- |
TIP
Atoms implement Subscribable<T>, so they work with any adapter's signal bridge. Only React has dedicated atom hooks (useAtom, useAtomValue, useSetAtom) with the [value, setter] tuple pattern.
Adapter Bridging Strategies
React: useSyncExternalStore
React 18's concurrent rendering can "tear" -- reading different values during a single render pass. useSyncExternalStore is the only safe way to subscribe to external stores in concurrent mode. The React adapter also uses a StateLoom effect() inside subscribe to track computed signal dependencies through the reactive graph.
Vue: shallowRef + effect()
The Vue adapter creates a shallowRef and uses StateLoom's effect() to track dependencies. When the source changes, the effect updates the ref, and Vue's reactivity system picks up the change. shallowRef prevents Vue from deeply wrapping StateLoom-managed state with its own reactivity.
Solid: Dual Subscription
The Solid adapter uses both effect() (for graph-integrated sources like signals and computed) and subscribe() (for plain Subscribable objects). createSignal with equals: false ensures all updates propagate. The setValue(() => next) pattern safely handles function-typed values.
Svelte: Native Store Contract
Svelte's $store syntax works with any object implementing { subscribe }. The only gap: Svelte requires subscribe to call the callback immediately with the current value, while StateLoom's subscribe() only fires on changes. The toReadable/toWritable bridges close this gap.
Angular: Angular Signals + RxJS
Angular uses both RxJS Observables (for async pipes and operators) and Angular Signals (for template bindings). The adapter provides toObservable for the RxJS path, toAngularSignal for the Signals path, injectStore for store-to-template binding, and fromObservable for bridging RxJS back into StateLoom.
SSR Scope Integration
All adapters follow the same SSR pattern: provide a scope via the framework's context/injection system, read it in components that need scoped state.
| Framework | Scope Provider | Scope Consumer |
|---|---|---|
| React | <ScopeProvider scope={scope}> | useScopeContext() |
| Vue | app.use(stateloomPlugin, { scope }) / <ScopeProvider> / provideScope() | useScope() |
| Solid | <ScopeProvider scope={scope}> | useScope() |
| Svelte | setScope(scope) in parent component | getScope() |
| Angular | provideStateloomScope(scope) in bootstrapApplication | injectScope() |
Choosing an Adapter
Use the adapter that matches your framework. If you use multiple frameworks in a monorepo, each app uses its own adapter while sharing the same @stateloom/core signals and stores.
| If your framework is... | Install |
|---|---|
| React 18+ (Vite, Next.js, Remix) | @stateloom/react |
| Vue 3.2+ (Vite, Nuxt) | @stateloom/vue |
| Solid.js 1+ (Vite, SolidStart) | @stateloom/solid |
| Svelte 4/5 (Vite, SvelteKit) | @stateloom/svelte |
| Angular 17+ (Angular CLI, Analog) | @stateloom/angular |
| Vanilla JS/TS (no framework) | No adapter needed -- use subscribe() directly |
TIP
All adapters peer-depend on @stateloom/core and their respective framework. Paradigm packages (@stateloom/store, @stateloom/atom, @stateloom/proxy) are optional peer dependencies -- install only what you use.
Individual Adapter Documentation
@stateloom/react-- React hooks, sub-path imports for store/atom/proxy@stateloom/vue-- Vue composables, plugin, ScopeProvider component@stateloom/solid-- Solid accessors with dual-subscription strategy@stateloom/svelte-- Svelte store contract bridges (toReadable/toWritable)@stateloom/angular-- Angular Signals, RxJS Observable bridges, DI providers