We’ve had reactivity on the browser since 2018, with async iterables
And it can be used to power client-side rendering
We’ve had reactivity on the browser since 2018, with async iterables

This week I had an epiphany, we already have a native reactivity primitive in JavaScript: Async Iterables.
The model is surprisingly simple:
- first
yield= initial state - subsequent
yields = updates for await (…)= subscription
That’s it — a read-only state is just an AsyncIterable.
The implications are of this approach are wide: anything supporting the Async Iterator protocol — even Web Standards like Server-Sent Events or Web Sockets — can directly be treated as a state.
This got me thinking about how every frontend framework reinvents reactivity:
- React → class
this.statethenuseStatehook - Vue → refs/reactivity
- Angular → RxJS → signals
- Svelte → stores → runes
- Solid → signals

To be fair, most of them were written before 2018. But what if we didn’t need any of that?
I built an experiment called **Yawn** to explore this idea:
- uses Async Iterables as the only reactivity primitive
- API mirrors Web standards (HTML attributes, DOM lifecycle, etc.)
- JSX returns real DOM nodes
- ~2.6kB rendered, ~1.5kB gzipped — without even trying to optimize it yet
Repo + demo: https://github.com/SacDeNoeuds/yawn
The goal is to ask: Could a frontend library be “forever v1” by aligning with the Web platform?

Me, after 10 years of JavaScript, when yet a new framework is created
Usage
Counter example — showcasing a writable state
Let’s start with a good ol’ counter example to explore writable states:
import { State } from '@sacdenoeuds/yawn'
export function Counter() {
const count = new State(0)
const decrement = () => count.update((count) => count - 1);
const increment = () => count.update((count) => count + 1);
const reset = () => count.set(0);
return (
<div class="counter" data-count={count}>
<button type="button" onclick={decrement}>
-
</button>
<span>{count}</span>
<button type="button" onclick={increment}>
+
</button>
</div>
);
}
Doubling the count — showcasing a derived state
The count state can be derived using an async iterable mapper:
type Props = { count: AsyncIterable<number> }
function Doubled({ count }: Props) {
// …
const doubled = count.map((count) => count * 2)
return <div>{doubled}</div>
}
Fetching a todo — showcasing a read-only custom state
We can also create read-only states from a simple async generator, let’s illustrate this with a todo fetcher:
type Todo { id: number, title: string, completed: boolean }
async function* makeTodoFetcher(todoId: number) {
yield { status: 'pending' } as const
// ^-> emit a state update
try {
const response = await fetch(`…/todo/${todoId}`)
if (!response.ok) throw response
const todo: Todo = await response.json()
yield { status: 'ok', todo } as const
// ^-> emit a state update
} catch (error) {
yield { status: 'failed', error } as const
// ^-> emit a state update
}
}
Now we can call the makeTodoFetcher to create a todo fetch state:
type Props = { todoId: number }
export function TodoFetcher({ todoId }: Props) {
const fetchState = makeTodoFetcher(todoId)
return fetchState.map((fetchState) => {
switch (fetchState.status) {
case 'pending':
return <div>Loading…</div>
case 'failed':
return <div>Error: {String(fetchState.error)}</div>
case 'ok':
return <Todo todo={fetchState.todo} />
}
})
}
Closing word
There are still open questions (memory, ergonomics), but the model feels surprisingly coherent once it clicks.
I am curious to hear thoughts — especially from people who’ve worked with signals, RxJS, or async iterators in production.
I use the GitHub stars as an interest-meter, so if you’d like to see this pushed further, please star the project.
Thanks for reading 💛
메타데이터
- post_id
- e4f0d6487a95
- slug
- weve-had-reactivity-on-the-browser-since-2018-with-async-iterables-e4f0d6487a95
- url
- https://levelup.gitconnected.com/weve-had-reactivity-on-the-browser-since-2018-with-async-iterables-e4f0d6487a95
- canonical_url
- https://levelup.gitconnected.com/weve-had-reactivity-on-the-browser-since-2018-with-async-iterables-e4f0d6487a95
- author_url
- https://medium.com/@thomas-juster
- status
- ok
- fetched_at
- 2026-06-11 17:15:47