LLui

The web framework designed for LLMs

LLui is the first web framework built from the ground up for AI-assisted development. Its architecture — strict types, pure functions, effects as data, and a flat component model — maps directly to how LLMs reason about code.

import { component, mountApp, div, button } from '@llui/dom'

type State = { count: number }
type Msg = { type: 'inc' } | { type: 'dec' }

const Counter = component<State, Msg, never>({
  name: 'Counter',
  init: () => [{ count: 0 }, []],
  update: (state, msg) => {
    switch (msg.type) {
      case 'inc':
        return [{ ...state, count: state.count + 1 }, []]
      case 'dec':
        return [{ ...state, count: state.count - 1 }, []]
    }
  },
  view: ({ send, text }) => [
    div({ class: 'counter' }, [
      button({ onClick: () => send({ type: 'dec' }) }, [text('-')]),
      text((s) => String(s.count)),
      button({ onClick: () => send({ type: 'inc' }) }, [text('+')]),
    ]),
  ],
})

mountApp(document.getElementById('app')!, Counter)

Why LLMs produce better LLui code

One pattern for everything. Every component is initupdateview. No hooks, no lifecycle methods, no class hierarchies, no context providers. An LLM that understands one component understands them all.

Pure functions are predictable. update(state, msg) is a pure function — given the same state and message, it always returns the same result. LLMs don't need to reason about mutation, timing, or hidden side effects. They just pattern-match on message types.

Types constrain the output. State, Msg, and Effect are explicit discriminated unions. The type system rejects invalid states at compile time. An LLM can't accidentally produce a component that sends the wrong message or forgets to handle a case — TypeScript catches it.

Effects as data, not callbacks. Side effects are plain objects returned from update(), not imperative calls scattered through the code. An LLM can reason about what a component does by reading its return values. Testing is just deepEqual on the effect array.

No hidden runtime magic. view() runs once. There's no virtual DOM diffing, no dependency tracking, no re-rendering. Reactive bindings are explicit arrow functions: text((s) => s.count). An LLM can see exactly which state drives which DOM node.

Flat composition. Components compose via view functions (state slice + send), not through nested provider trees. There's one level of indirection, not five. LLMs can follow the data flow in a single pass.

How it works

  • view() runs once. DOM nodes are created at mount time with reactive bindings that update surgically when state changes. No re-rendering.
  • Two-phase update. Phase 1 reconciles structural changes (branch, each, show). Phase 2 iterates a flat binding array with bitmask gating — (mask & dirty) === 0 skips irrelevant updates in constant time.
  • Compiler optimization. The Vite plugin extracts state access paths, assigns bitmask bits, and synthesizes __dirty() per component. Zero runtime dependency tracking overhead.

LLM integration

LLui provides first-class tooling for AI workflows:

  • llms.txt — concise framework reference for system prompts
  • llms-full.txt — comprehensive reference with all APIs, patterns, and rules (~47KB, fits in most context windows)
  • @llui/mcp — MCP server exposing debug tools directly to LLMs via Model Context Protocol
  • @llui/lint-idiomatic — 15 anti-pattern rules that catch common LLM mistakes before they reach production
  • LLM Guide — system prompt and idiomatic patterns for AI code generation

Packages

Package Description
@llui/dom Runtime — component, mount, scope tree, bindings, structural primitives, element helpers
@llui/vite-plugin Compiler — 3-pass TypeScript transform, template cloning, source maps
@llui/effects Effect system — http, cancel, debounce, sequence, race, websocket, retry, upload
@llui/router Routing — structured path matching, history/hash mode, guards, link helper
@llui/transitions Animation helpers — transition(), fade, slide, scale, collapse, flip, spring
@llui/components 54 headless components + opt-in theme (CSS tokens, dark mode, Tailwind class helpers)
@llui/test Test harness — testComponent, testView, propertyTest, replayTrace
@llui/vike Vike SSR/SSG adapter — onRenderHtml, onRenderClient
@llui/mcp MCP server — LLM debug tools via Model Context Protocol
@llui/lint-idiomatic Linter — 15 anti-pattern rules for idiomatic LLui

Performance

Top-tier performance on js-framework-benchmark, competitive with Solid and Svelte. Full benchmarks →

Quick Start

mkdir my-app && cd my-app
npm init -y
npm install @llui/dom @llui/effects
npm install -D @llui/vite-plugin vite typescript
npx vite