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:
- empty — a minimal hello-world starter (no tests).
- dashboard — a home-automation control panel: a
$store, device switches/sliders, scenes, and energy charts behind anAppShell+ router. - website — a pet-sitting company site: navbar, hero, services, a pricing table + FAQ, and a validated contact form.
- todos-app — REST CRUD with
$http(create / toggle / edit / delete) andAsyncloading states. - chatbot — an OpenAI chat UI with an API-key settings popover and an offline echo fallback.
- portfolio — a developer portfolio with a filterable projects grid, an experience timeline, and a contact form.
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:
$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:
$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.
$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
Layout
Column, Row, Grid, and Center — every layout from three primitives.
Learn layout → ReferenceLanguage
State, expressions, actions, effects, HTTP, and the rules behind component calls.
View reference → ReferenceComponents
Every built-in component, its props, and a live preview you can copy from.
Browse components → FrameworksFrameworks
Drop-in setup for React, Vue, Angular, Svelte, Next.js, and plain HTML.
See recipes →