Tu

A reactive UI language. Trailing-closure DSL, scoped styles, TC39 Signals, full LSP.

Trailing-closure DSL

div(class: "row") { h1 { "hi" } p { x } } — markup, props, and children read top-to-bottom in one syntax.

Reactive by default

Top-level let count = 0 auto-binds to a TC39 Signal cell. computed(...) cells re-derive on mutation. Reads inject .get() automatically.

Scoped styles

style { .card { ... } } at the bottom of any component. Symbolic class refs (.card()) get a per-component hash so rules never bleed.

Types via TypeScript

Volar pattern — Tu compiles to a TypeScript shadow, tsserver does the inference. Hover, completion, goto-definition, and rename all work cross-.tu.

Object literals + member access

let p: Point = { x: 1, y: 2 } and p.x work end-to-end with reactive cells. Lambda return-type annotations close the typed-data loop.

Native markdown blocks

markdown { … } as a first-class language form (M6.3) — embed prose alongside Tu components, pre-rendered at compile time.

SSR + Suspense + streaming (M6.11)

renderToString() for sync server-side rendering, renderPageAsync() + Suspense for async data fetching, renderToStream() for ReadableStream-based per-boundary flushing, hydrate() for the client handoff (focus / scroll / <input> value preserved). See the suspense example.

Ecosystem

tu-xing — shadcn-style UI library. tu-shu — Tu-native SSG (renders this page). Tailwind drops in via @source. The whole stack dogfoods itself.

Quick taste

type Point = { x: number; y: number }

export let origin: Point = { x: 0, y: 0 }

export let App = () => .panel() {
  h1 { "Hello, Tu!" }
  p { "origin.x = " origin.x ", origin.y = " origin.y }

  button(onClick: () => origin = { x: origin.x + 1, y: origin.y + 1 }) {
    "bump"
  }

  style {
    .panel { padding: 1rem; font-family: system-ui, sans-serif; }
    .panel > h1 { color: #312e81; }
  }
}
  • type Point = … — TS-style alias; threaded into the TS-mode emit verbatim.
  • let origin: Point = { x: 0, y: 0 } — top-level let auto-binds to a Signal.State<Point>. Object literal as the cell’s value.
  • .panel() { … } — pug-shorthand: <div class="panel panel-tu-XXX"> plus children. The XXX is a per-component hash; the style { … } block’s selectors get the same suffix, so .panel styles never bleed across components.
  • origin.x — postfix member access. . doesn’t collide with prefix-dot ClassRef because they sit at different positions in the grammar.
  • origin = { … } — assignment desugars to origin.set(…) when the target is a state cell.

Status

Tu is pre-alpha (0.1.0-alpha.8 on npm). The compiler, runtime, type system, full LSP, SSR + Suspense + streaming (M6.11), Custom Elements wrapper, tu-xing UI library, and tu-shu SSG (which builds this site) are all shipped. The repo is the public preview — the API surface may change before v0.1, but every example in examples/ actually runs today.

For features explicitly deferred to a later milestone (live editor, per-component HMR, local reactivity, etc.) see the Deferred backlog.