Getting started

Install.

Get Aktion rendering in under a minute. The quickest path is to scaffold a project with create-aktion and add the editor extension — then you write a compact $app(Column([…])) program and the <aktion-app> element turns it into a real, interactive UI. Prefer no build step? You can also drop in a CDN tag or install from npm.

Scaffold a project with create-aktion

The fastest way to a real, hot-reloading app is create-aktion (requires Node 18+). It generates a Vite + TypeScript project with multi-file .aktion modules, the aktion-runtime/vite plugin wired up, and — for the richer templates — a Vitest suite:

npm create aktion@latest my-app
cd my-app
npm install
npm run dev          # http://localhost:5173

Run it with no arguments to choose a name and template interactively, or pass --template to pick one up front (pnpm create aktion, yarn create aktion, and npx create-aktion work too):

npm create aktion@latest my-app -- --template dashboard

Six templates ship today:

Every template but empty splits its UI across .aktion files the way you would in React (data / store / components / pages) and ships unit tests you run with npm test. Editing any .aktion file hot-reloads the UI while preserving live $state.

Editor support (VS Code & Cursor)

Install the Aktion extension for first-class .aktion editing — syntax + semantic highlighting, inline diagnostics, completions, hover docs, signature help, go-to-definition (including across imported files), find references, rename, an outline view, and format-on-save. Open the Extensions view, search “Aktion”, and click Install — or from a terminal:

code --install-extension AsfandiyarKhan.aktion-vscode

It is available on the Visual Studio Marketplace (VS Code) and on Open VSX (Cursor, VSCodium, Gitpod, …). Projects scaffolded with create-aktion already recommend it and enable format-on-save and semantic highlighting for .aktion files, so new contributors get the prompt to install it automatically.

Your first program

Here is a complete program. The live preview on the right is the same bundle you just installed, rendering this exact source:

Live
$app(Column([
  PageHeader("Project Atlas", { subtitle: "Updated 2 hours ago" }),
  Card([
    CardHeader("Overview"),
    Text("Every Aktion program is a tree of components.")
  ]),
  Button("Get started", { variant: "primary", icon: "rocket" })
], { gap: "lg" }))

Every program must call $app(…) — that expression is the root the element renders. The root is normally a Column([…]), the recommended layout primitive that stacks children top to bottom; its siblings are Row, Center, and Grid. Everything else is just components nested inside, each a function call with an optional props object.

Add reactivity

State lives in $-prefixed bindings. Reassign one and every part of the UI that reads it re-renders — no hooks, no setState. Wire a Button to a function with onClick:

Live
$count = 0
function inc() { $count = $count + 1 }
$app(Card([
  CardHeader("Counter", { subtitle: "Reactive state in a few lines" }),
  Text("Count: " + $count),
  Button("+1", { onClick: inc, variant: "primary" })
]))

Action bodies are real JavaScript: the full global surface is available (alert, fetch, Math, …), as are timers like setTimeout and setInterval. For data, reach for the built-in $http(…) resource — it tracks loading and error state and exposes an .onDone callback for when a request settles.

Theming in one line

Switch the entire look with a single attribute — <aktion-app theme="dark">. Built-in themes include light, dark, corporate, soft, glass, and modern; you can also customize the theme with setting the theme variables in $theme({...}). See Themes for the full list and custom theme variables.

Live — this demo follows the page theme
$app(Card([
  CardHeader("Themed surface", { subtitle: "One attribute restyles everything" }),
  Text("Try toggling the doc theme in the top bar."),
  Button("Action", { variant: "primary" })
]))

$theme({ colors: { primary: "orange" } })

Driving the element from JavaScript

For static UI the response attribute is enough. To swap the program at runtime, grab the element and call setResponse() (the response property does the same thing):

const app = document.querySelector("aktion-app");

app.setResponse(`
  $app(Column([
    CardHeader("Loaded from JavaScript"),
    Text("Call setResponse() to replace the whole program.")
  ]))
`);

For a streaming source (e.g. an LLM responding token by token), turn on the streaming attribute and push text with appendChunk(). The UI commits each line as soon as the parser can; streaming simply tells the error banner not to flash on transient mid-line parse errors. Clear it when the stream ends.

const app = document.querySelector("aktion-app");
app.setAttribute("streaming", "true");

for await (const chunk of stream) {
  app.appendChunk(chunk);
}

app.removeAttribute("streaming");

To keep your program in its own file instead of inline markup, point the src attribute at an .aktion file. It is resolved relative to the page and linked through the in-browser project linker, so an entry that imports other modules fetches its whole graph on connect:

<aktion-app src="./app.aktion"></aktion-app>

Precedence is response (attribute or setResponse()), then src, then inner text. Fetch or link failures surface through the same error banner and error event as parse errors.

Two more attributes are worth knowing up front: theme selects a built-in theme (see Theming above), and showerrors renders a developer error banner inside the shadow DOM while you iterate. Other methods on the element include setTheme(), registerComponents() to extend the library at runtime, and getSystemPrompt() to build the prompt that teaches an LLM to emit Aktion.

Other ways to install Aktion

create-aktion is the recommended start, but you can also add Aktion to a page or an existing app with no scaffolding.

Drop in a script tag (CDN)

Zero build, zero bundler. Load the ES module bundle from the CDN — it registers the <aktion-app> element — then mount the tag and hand it a program through the response attribute:

<script type="module" src="https://asfand-dev.github.io/aktion/dist/aktion.js"></script>

<aktion-app response="$app(Column([
  PageHeader('Hello', { subtitle: 'Generative UI in plain HTML' }),
  Card([CardHeader('It works'), Text('No build step required.')])
]))"></aktion-app>

That single bundle ships the parser, the reactive runtime, every built-in component, the themes, and an optional Font Awesome loader. Nothing else to wire up.

Install aktion-runtime from npm

Adding Aktion to an existing app (React, Vue, Svelte, plain HTML…) or bundling it yourself? Install the runtime package directly:

npm i aktion-runtime

Then import it once, anywhere in your app entry. The import has a side effect: it registers the <aktion-app> custom element. After that the tag works in any framework or in plain HTML.

import "aktion-runtime"; // registers <aktion-app>

// then render the element however your framework does it:
// <aktion-app response="$app(Column([ Text('Hi from npm') ]))"></aktion-app>

Next