tutuca-mb

The SPA framework that fits in your head — now in MoonBit

HTML-first components. Batteries included. One codebase, three backends.

Try it live ▸ Examples GitHub ★
let counter = @component.component(
  name="Counter",
  view="<button @on.click='$inc' @text='.count'></button>",
  fields={ "count": @component.FieldSpec::of_default(Num(0)) },
)

Try It

Every example on this page is a real MoonBit program. Edit it and press Ctrl/Cmd + Enter (or the Run button) — the MoonBit compiler runs in your browser, links against the tutuca-mb library, and mounts the result live. The first run loads the compiler, so give it a moment; every example after that compiles in a couple of seconds.

Hello, Counter

Toggle — no logic needed

A boolean field gets a generated $toggleOpen mutator for free; @show reads it. No hand-written update code.

Examples

The same examples as the Tutuca landing page, ported to MoonBit. Each is editable — change the code and re-run. For the full set compiled ahead of time, see the storybook gallery.

Basics

To-Do List

A list of editable child Item components. <x render-it> renders the loop's current instance; the generated $removeInItemsAt mutator deletes by key.

Two-way Text Binding

:value reads a field; @on.input="$setName value" writes it via the generated mutator; @text mirrors it live.

Interaction Patterns

Tree Navigation

Click folders to toggle them open or closed, and click files to select them.

Drag and Drop

Drag entries to reorder them, and use the input to filter the list.

Filter and Paginate

Search narrows the list, pages slice the matches, and editing or deleting a row works on the right item even on page 2 of a filtered view — @loop-with returns the matching rows' original keys, so identity survives filtering and paging.

Web Component & Custom Event

Hosting a third-party web component and reacting to its CustomEvent is a core tutuca feature (@on.emoji-click="onEmojiClick value"). Because it needs an external custom element loaded on the page, see it running in the storybook gallery.

Full Applications

JSON Editor

A recursive editor: every JSON node is a component that renders its children.

Personal Site

A filterable list of entries with category and role facets, sorting, and a solo/unsolo (alt-click) interaction. Data loads through a request handler after mount.

Visual WebAssembly

A larger app driving a visual WebAssembly demo.

Composability

All Together Now

Each example exports its components independently, with no knowledge of being composed. This root imports several of them and renders each in its own tab — the glue code is minimal.

Storybook

A storybook aggregates components in several states under a sidebar, with a live preview and a Lint panel per story. tutuca-mb ships a compiled one: see the full storybook (also served by ./cli/tutuca storybook).

Testing Components

tutuca-mb components are plain values, so their methods, input handlers, and iteration logic are unit-testable. Tests run through the toolchain rather than in this browser sandbox — moon test for the library, and the storybook's Lint panel for per-component checks.

Get Started

tutuca-mb is the MoonBit port of Tutuca. You author components as plain MoonBit values — a name, an HTML-ish view string, and optional fields, methods, and event handlers — and mount them with a host adapter. The same components compile to three backends:

This folder is a self-contained build. Browser demos need a static file server (opening files via file:// won't load ES modules):

python3 -m http.server 8000

Then open http://localhost:8000/ and explore:

What's Next