🍡 mochi
← All demos

Runed Utilities

Runed is a Svelte 5 collection of reactive utilities built on runes. Because most of them read the DOM, timers, or device sensors, each card is its own mochi:hydrate island — the page renders on the server with default values, then Runed brings it to life on the client.

Runed needs no special setup — it's a plain Svelte 5 runes library, so you install it and import from runed. In Mochi it bundles straight into whichever island imports it.

bun add runed

Then import a utility and use it like any rune — e.g. the Debounced value driving the first card:

import { Debounced } from 'runed';

let query = $state('');
const search = new Debounced(() => query, 400);
// search.current — query, 400ms after it stops changing

Explore the full toolkit at runed.dev.

Reactivity & timing

Debounced, Throttled, Previous, and watch — one input, four derived views.

live
Debounced (400ms)
Throttled (400ms)
Previous
watch() change log

Waiting for the debounced value to settle…

State

StateHistory undo/redo, PersistedState across reloads and tabs, and IsMounted.

StateHistory

undo / redo any $state
0

0 snapshots recorded

PersistedState

localStorage · survives reload · syncs tabs
0

Reload the page — the value sticks. Open a second tab to watch it sync.

IsMounted

false during SSR, true after hydration
not mounted (server)

Elements & observers

ElementSize + useResizeObserver, IsInViewport, activeElement, and IsFocusWithin.

ElementSize + useResizeObserver

drag the corner of the box
0 × 0 0 resize events

IsInViewport

scroll the target in and out
scroll down ↓
Out of viewport
↑ scroll up
hidden

activeElement + IsFocusWithin

tab through the fields
focused: none

Sensors & animation

PressedKeys, IsIdle, and AnimationFrames with a live FPS cap.

PressedKeys

hold any keys — try Ctrl + Shift
No keys pressed

IsIdle

stop moving the mouse for 2s
active

Last active: 5:56:41 PM

AnimationFrames

rAF loop with an FPS cap
0 fps 0 frames

State machine & async

A self-driving FiniteStateMachine traffic light and a debounced resource search over a Mochi API.

FiniteStateMachine

_enter hooks auto-advance via .debounce()
red

resource

debounced fetch with cancellation
12 matches
appleapricotbananablueberrycherrygrapelemonmangoorangepeachpearplum
<script>
  import CodeSnippet from '../../components/CodeSnippet.svelte';
  import Reactivity from './Reactivity.svelte';
  import StateCard from './StateCard.svelte';
  import Elements from './Elements.svelte';
  import Sensors from './Sensors.svelte';
  import AsyncFsm from './AsyncFsm.svelte';
  import { highlightCode } from '../../lib/highlight.server';
  import { files } from './files.ts';

  const sources = await loadSources(files);

  const codeInstall = await highlightCode('bun add runed', 'bash');
  const codeUsage = await highlightCode(
    `import { Debounced } from 'runed';

let query = $state('');
const search = new Debounced(() => query, 400);
// search.current — query, 400ms after it stops changing`,
    'typescript',
  );
</script>

<div class="intro">
  <p>
    Runed needs no special setup — it's a plain Svelte 5 runes library, so you install it and import from
    <code>runed</code>. In Mochi it bundles straight into whichever island imports it.
  </p>
  <CodeSnippet html={codeInstall} />
  <p>Then import a utility and use it like any rune — e.g. the <code>Debounced</code> value driving the first card:</p>
  <CodeSnippet html={codeUsage} />
  <p>Explore the full toolkit at <a href="https://runed.dev" target="_blank" rel="noopener noreferrer">runed.dev</a>.</p>
</div>

<section class="card">
  <header>
    <h2>Reactivity & timing</h2>
    <p><code>Debounced</code>, <code>Throttled</code>, <code>Previous</code>, and <code>watch</code> — one input, four derived views.</p>
  </header>
  <Reactivity mochi:hydrate />
</section>

<section class="card">
  <header>
    <h2>State</h2>
    <p><code>StateHistory</code> undo/redo, <code>PersistedState</code> across reloads and tabs, and <code>IsMounted</code>.</p>
  </header>
  <StateCard mochi:hydrate />
</section>

<section class="card">
  <header>
    <h2>Elements & observers</h2>
    <p><code>ElementSize</code> + <code>useResizeObserver</code>, <code>IsInViewport</code>, <code>activeElement</code>, and <code>IsFocusWithin</code>.</p>
  </header>
  <Elements mochi:hydrate />
</section>

<section class="card">
  <header>
    <h2>Sensors & animation</h2>
    <p><code>PressedKeys</code>, <code>IsIdle</code>, and <code>AnimationFrames</code> with a live FPS cap.</p>
  </header>
  <Sensors mochi:hydrate />
</section>

<section class="card">
  <header>
    <h2>State machine & async</h2>
    <p>A self-driving <code>FiniteStateMachine</code> traffic light and a debounced <code>resource</code> search over a Mochi API.</p>
  </header>
  <AsyncFsm mochi:hydrate />
</section>
Styles
<style>
  .intro {
    font-size: 0.95rem;
    color: var(--text-muted);
    margin: 0 0 1.75rem;
  }

  .intro p {
    margin: 0 0 0.75rem;
  }

  .intro a {
    color: var(--accent);
    text-decoration: underline;
  }

  .intro > p:last-child {
    margin-bottom: 0;
  }

  .intro p code {
    background: var(--surface-muted);
    border: 1px solid var(--border);
    color: var(--text);
    font-family: var(--font-mono);
    padding: 0.1em 0.35em;
    border-radius: 4px;
    font-size: 0.85em;
  }

  .card {
    margin-bottom: 2rem;
  }

  .card header {
    margin-bottom: 0.9rem;
  }

  .card header p {
    font-size: 0.95rem;
    color: var(--text-muted);
    margin: 0.25rem 0 0;
  }

  .card header code {
    background: var(--surface-muted);
    border: 1px solid var(--border);
    color: var(--text);
    font-family: var(--font-mono);
    padding: 0.1em 0.35em;
    border-radius: 4px;
    font-size: 0.85em;
  }
</style>