scrml.dev v0.7.1
scrml.dev › Getting started

Get started with scrml

Install the compiler, write your first .scrml file, run the dev server, ship a build.

Prerequisites

scrml's reference compiler runs on Bun. Bun is the only required runtime — no Node, no separate package manager, no bundler.

curl -fsSL https://bun.sh/install | bash

Verify the install. The compiler requires Bun 1.3.13 or newer:

bun --version

Install scrml

Until the npm package lands, install from source. Two installs, not one — a couple of the compiler's dependencies are declared in the nested compiler/ manifest rather than the root one:

git clone https://github.com/bryanmaclee/scrml
cd scrml
bun install
cd compiler && bun install && cd ..

Link the scrml CLI onto your PATH:

bun link

Confirm it works:

scrml --version

Hello, scrml

Create a file hello.scrml:

<program>

  <div class="flex flex-col items-center justify-center min-h-screen gap-4">
    <h1 class="text-4xl font-bold">Hello, scrml</h1>
    <p class="text-lg text-gray-600">No framework. Just output.</p>
  </div>

</program>

Compile:

scrml compile hello.scrml -o dist/

That produces dist/hello.html, dist/hello.css, and dist/hello.client.js. Open the HTML in a browser. There is no framework runtime on the page — the compiler emits only the JS that your specific program needs.

Reactivity in 30 seconds

Add state and event handlers. The <x> = init shape declares a reactive cell; @x reads it, @x = expr writes it. Any DOM read of @x re-renders automatically on write.

<program>

  <count> = 0
  <step>  = 1

  function increment() { @count = @count + @step }
  function decrement() { @count = @count - @step }
  function clearCount() { reset(@count) }

  <div class="flex flex-col items-center gap-6 p-8 min-h-screen">
    <h1 class="text-3xl font-bold">Counter</h1>
    <p class="text-6xl font-bold text-blue-600">${@count}</p>

    <div class="flex gap-2">
      <button class="px-5 py-2 bg-red-500 text-white rounded" onclick=decrement()>−</button>
      <button class="px-5 py-2 bg-gray-200 rounded" onclick=clearCount()>Reset</button>
      <button class="px-5 py-2 bg-green-500 text-white rounded" onclick=increment()>+</button>
    </div>

    <label class="flex items-center gap-2 text-sm">
      Step:
      <input type="number" class="w-16 p-1 border rounded" bind:value=@step min="1" max="100">
    </label>
  </div>

</program>

Five things to notice:

  • No store wrapper. <count> = 0 is reactive on declaration. No useState, no writable(), no ref().
  • No JSX. The markup IS the language. Curly braces inside ${ } evaluate; everywhere else they're text.
  • Standard event names. onclick=decrement() — no @click, (click), or onClick.
  • Two-way binding without a directive. bind:value=@step wires the input's value both ways.
  • Tailwind out of the box. Utility classes work with no setup — the compiler scans your markup and emits only the CSS you use.

Dev server with hot reload

scrml dev

Boots a local server on localhost:3000, watches every .scrml file in the directory, and triggers a recompile + browser reload on every save. Compile errors print to the terminal with a line/column pointer.

Custom port and output dir:

scrml dev . --port 3100 -o dist/

Build for production

scrml build

Produces optimised HTML, CSS, and JavaScript under dist/, content-addressed so cache invalidation is automatic. The v0.3 release ships per-route per-role chunk splitting — an unauthenticated visitor downloads only the unauthenticated bundle; an admin visitor downloads only the admin bundle.

Where to go next

  • <engine> — state machines as a first-class element. The Tier 2 form of "UI as a fully-handled state machine."
  • ?{ … } — SQL as syntax. No ORM. The server boundary disappears.
  • <errors of=…/> — the auto-synthesised validity surface. Validation lives on the type, not on a separate Zod- style schema.
  • Reference — every element, keyword, context, and error code.
  • Articles — long-form essays on the design decisions.

Standing notes

  • No null, no undefined. scrml has no null literal and no undefined concept. Use not to express absence and "" · 0 · [] for empty values.
  • Cells declare with <x> = init at top level. Inside ${ } logic context, the assignment-form @x = init also declares.
  • One file format. Markup, logic, styles, server functions, SQL, all in the same .scrml file. The compiler decides what runs where.