Looks like JavaScript — because it is
Aktion is a strict subset of JS you already know: function, for…of, template literals, arrow functions, destructuring, optional chaining. Nothing new to memorize, no JSX.
Aktion is a reactive UI language — a strict subset of JavaScript you already know. It replaces your framework, component kit, router and i18n with one tag: 270+ built-in components, fine-grained reactivity, 6 themes, and full JS interop — no JSX, no bundler, no boilerplate. Can also be effectively used as a Micro-Frontend in React, Vue, Angular, Svelte, Next.js or any other Framework.
$app(Column([
PageHeader("Project Atlas", { subtitle: "12 active · 4 at risk", badge: Badge("On track", { tone: "success" }) }),
Grid([
StatCard("Revenue", { value: "$48.2k", trend: "up", delta: "+12%", icon: "sack-dollar" }),
StatCard("Users", { value: "12,540", trend: "up", delta: "+184", icon: "users" }),
StatCard("Churn", { value: "1.4%", trend: "down", delta: "-0.3%", icon: "arrow-trend-down" })
], { gap: "md", minChildWidth: "150px" }),
Card([
CardHeader("Welcome back"),
Text("This dashboard streamed in as plain text and rendered as a live UI.")
])
], { gap: "lg" }))
One custom element · drop it anywhere
No imports, no CSS files, no build config. Describe the interface as plain data and Aktion owns the DOM, the styling, and the updates.
import { useState } from "react" import "./Counter.css" export function Counter() { const [count, setCount] = useState(0) return ( <div className="card"> <h3>Counter</h3> <p className="value">{count}</p> <button className="btn" onClick={() => setCount(c => c + 1)}> Increment </button> </div> ) } /* + Counter.css + bundler + JSX build */
// no imports · no CSS · no build $count = 0 $app(Card([ CardHeader("Counter"), Text(`${$count}`), Button("Increment", { onClick: () => { $count = $count + 1 } }) ]))
Components, state, routing, theming, i18n and data — built in, batteries included. No glueing together five libraries, no build pipeline, no boilerplate.
Aktion is a strict subset of JS you already know: function, for…of, template literals, arrow functions, destructuring, optional chaining. Nothing new to memorize, no JSX.
$stateDeclare state with $name = value, read it in your tree, write it in a function. The runtime tracks dependencies automatically at path granularity — no hooks rules, no useMemo dance.
Layout, forms, charts, tables, calendars, gestures, drawing canvases, marketing sections, and full patterns like dashboards and app shells — included, not installed.
Switch palettes with one attribute, or brand the whole UI in-script with $theme({…}) over 80+ tokens.
$i18n({ translations }) gives you t(), language switching and {name} interpolation — no extra library.
$router, NavLink and params ship in the box, alongside $store for shared global state.
The full JavaScript surface is in scope — fetch, Intl, crypto, JSON, Date, Math — plus a $util helper namespace. Bring your own logic.
$http({ url }) returns a reactive resource with .data, .loading, .error & .refetch().
A whole dashboard is a handful of plain-data calls. No flexbox bookkeeping, no component boilerplate, no global-CSS collisions — everything lives in an isolated Shadow DOM. Ship dramatically fewer lines and read them again next quarter.
The same simplicity that makes Aktion fast for you makes it trivial for an AI to write. A built-in system-prompt generator and streaming renderer turn any model into a UI engine — but you never need an LLM to use Aktion.
The same plain-data simplicity that speeds you up makes Aktion trivial for a model to generate. It streams token-by-token straight into the DOM — a free superpower, never a requirement.
The model emits Aktion text token by token.
Each line is parsed and applied as it lands.
A React-like reconciler diffs & paints live.
Instead of a wall of text, the model streams Aktion straight into the conversation — and it renders token-by-token as a live, interactive UI. Every card below is a real <aktion-app>, not a screenshot. Try the arrows.
Reading $user.name subscribes to that path alone — a write to $user.role never re-renders, recomputes, or re-fires an effect that only read name.
$ sigil is the only thing that makes a binding reactive.value: $form.email and it just works.$effect(fn, [$dep, "mount", "debounce(300)"]) with lifecycle & rate-limit triggers.Everything you normally bolt together — a framework, a component kit, a router, an i18n library, a styling system — built into one tag, with far less ceremony.
| Capability | Aktion | React | Vue | Angular |
|---|---|---|---|---|
| No build step / no JSX | One script tag | Bundler | Bundler | CLI + build |
| Code to ship the same UI | ~60% less | Boilerplate | Moderate | Verbose |
| Built-in i18n & router | Included | Libraries | Libraries | Built-in |
| Components included | 270+ built-in | Bring your own | Bring your own | Material only |
| Framework-agnostic | Any / none | React only | Vue only | Angular only |
| Style isolation by default | Shadow DOM | CSS-in-JS | Scoped | ViewEncaps. |
| Built-in themes & tokens | 7 + custom | Library | Library | Material |
Every preview below is a real <aktion-app> — not a screenshot. Compose data, not DOM.
Switch palettes with the theme attribute, or override 80+ design tokens in-script with $theme({…}).
Load the bundle, mount the tag, and write Aktion — a JavaScript subset you already know. No CLI, no config, no JSX. It runs the same inside React, Vue, Angular or a plain HTML file.
npm i aktion-runtime.<aktion-app theme="dark"> wherever HTML runs.$app(…). That's the whole app.Learn it in an afternoon. Ship complex, themeable, reactive interfaces in any framework — with a fraction of the code. Open source under MIT.