The Pattern
Example: Preact 10.x
Preact exposesuseSyncExternalStore via preact/compat — the same API React uses. The adapter is nearly identical to the React one.
Example: Lit 3.x
Lit usesReactiveController to encapsulate reusable logic that hooks into a component’s update cycle. The controller subscribes to the engine and calls host.requestUpdate() when state changes.
Checklist
| Concern | What to do |
|---|---|
| Create | Call createLogoSoup() once per component instance |
| Subscribe | Push engine.getSnapshot() into your reactive state on each notification |
| Process | Call engine.process(options) when inputs change |
| Cleanup | Call both unsubscribe() and engine.destroy() on teardown |
| Stability | Store the engine in a ref/field — don’t recreate it on every render |
| SSR | The engine needs <canvas>, so guard behind a client-side check if your framework does SSR |
How Our First-Party Adapters Map
| Framework | Reactive primitive | Subscribe mechanism | Cleanup |
|---|---|---|---|
| React | useSyncExternalStore | Engine’s subscribe/getSnapshot directly | useEffect return |
| Vue | shallowRef | engine.subscribe() → ref.value = snapshot | onScopeDispose |
| Svelte | createSubscriber | Getter calls subscribe() before reading | $effect teardown |
| Solid | from() | Producer function (set) => engine.subscribe(...) | onCleanup |
| Angular | signal() | engine.subscribe() → _state.set(snapshot) | DestroyRef.onDestroy |
| jQuery | $.data() | engine.subscribe() → re-render DOM | $el.logoSoup('destroy') |
src/react, src/vue, src/svelte, src/solid, src/angular, and src/jquery.