Tutuca

A small JavaScript framework for interactive web apps.

Typed state, built-in testing tools, and pluggable sync. Small enough to read in full.

Define your components, their state, and the messages they handle. Tutuca uses those definitions to render your UI, check for mistakes, and generate test cases. Explore components in the built-in storybook, then add a sync engine to save state or share changes across tabs and devices.

Start in the playground or import Tutuca into your own page. No build step required.

Inspired by Peter Salus:
  1. Write components that do one thing and do it well
  2. Write components to work together
  3. Write components to handle messages, because that is the universal interface
Inspired by Alan Kay:

Components to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things

Get started

npm install tutuca

Or import directly from a CDN:

import { component, h, Host } from "https://cdn.jsdelivr.net/npm/tutuca/dist/tutuca.min.js";

A first component

This counter introduces the four parts of a component: a name, typed fields, a view, and message handlers. Try the buttons, then edit the code and press Ctrl+Enter to run your changes.

Open Steps to replay a test sequence, Stress test to try random messages, or Dev to inspect the state, component definitions, and lint results.

const host = new Host({ document, rootNode: document.getElementById("app") });
host.scope.register({ Counter });
host.start(host.scope.components.Counter());

To mount the counter on your own page, register it with a host and start it in an element with id app.


Build with components and messages

Save and share state

Try the notes demo: reload it to check persistence, or open the Worker or Automerge version in two tabs to see changes sync, or the Automerge relay version in two browsers. For Automerge, copy the full URL after the demo loads so both open the same document.

All demos support these ?sync= options, plus ?history=N to cap the time travel timeline (0 turns it off). Read the sync guide for setup, field settings, and how each engine handles concurrent edits.

Test as you build

Understand how it works


Learn by example

Edit and run each example as you go, from lists and forms to nested components and async requests. Explore more in the playground, including the component libraries.

Lists and forms

Build a todo list from an array of records. Render items with each, read input values with targetValue(), and use getters for derived values in the view.

Components inside components

Build a cart with a component for each item. Each item handles its own messages and uses notify to request changes from the cart. Typed message arguments enable runtime checks in development and generated stress tests.

Talking to the outside world

Use ask to delegate a request to a named handler and receive the result as a message. Stories can substitute test handlers or send replies directly, so you can test loading, success, and failure states.

Web components and custom events

Use custom elements as tags in a view, and handle the custom events they fire with on: and the event's name. This picker comes from a CDN; its emoji-click event bubbles out of its shadow root, and the handler reads the chosen emoji from ctx.event.detail.

Drag and drop

Make an element draggable with draggable="true", and a drop target with on:drop. In the drop's message, dragKey() is the dragged item and sourceKey() the one it landed on. While you drag, Tutuca marks the dragged element with data-dragging and the target under it with data-draggingover, so styles can show where it will land. Filter the list, then drag: the keys stay those of the full list.

When things go wrong

This component has three deliberate mistakes. Open Dev to see what the linter found, fix the code, and run it again.


Demos

Explore small apps built with the component libraries. Most also have stories you can inspect and test in the storybook. Run any demo on its own or try it with a sync engine.

The runtime also runs as WebAssembly: tutuca-wax is tutuca's runtime written in Wax and compiled to wasm-gc, with components that are wasm modules of their own, loaded at run time and styled with Tailwind and margaui CSS compiled in the page. Its playground compiles components written in TCD and Wax in the browser and shows them running.

Project status

Beta parts work and are used by the demos, but their APIs may still change. Experimental parts are still being designed and may change a lot.

SubmoduleWhat it doesStatus
coreComponents, views, rendering, and the task loopbeta
storybookStories, test sequences, and the state timelinebeta
syncSchema-guided diffs and the sync enginesexperimental
devDevTools formatters and the inspector panelbeta
playgroundThe editable, runnable playground elementbeta
testHeadless driver and property-based test generatorsbeta
lintChecks views and components for mistakesbeta
embedThe <tutuca-app> custom elementbeta
cssLists class names for CSS compilersbeta

Resources