FORMA 3.0 IS HERE ↗

The web.
More direct.

Reactive TypeScript. Native Rust rendering.
HTML from the server, without a JavaScript runtime.

Browser on its own. Rust when you need it.

FROM YOUR EDITOR TO THEIR SCREEN

A page. Then a living interface.

HTML brings the content. CSS brings the look.
Islands bring the interaction. Follow one counter through it all.

Rust renders the HTML.No server-side JavaScript runtime.Try the real Rust lab ↗

THE NATIVE RENDERING PATH

  1. TypeScriptSupported TS / JSX
  2. FMIRBinary page format
  3. RustRender with your data
  4. HTMLContent in the browser

Also built for the browser: CSS for style island JavaScript when interaction needs it.

Build artifacts Request & response

FMIR = Forma Module IR
A binary page description Rust can read. Its IR walker follows that structure, fills in your data and writes HTML. It does not execute TypeScript.

Interactive model · illustrative snippets.
No requests are sent; all values reset when this webpage reloads.

01 / YOUR SOURCE

You write the interface.

Describe a supported page in TypeScript/JSX. Write ordinary CSS. Add a counter component where you want interaction.

page.tsx + styles.css · excerpt
<Counter count={props.count} />

.counter { color: #c5f953; }

02 / ONE BUILD, SEPARATE OUTPUTS

Each piece has a destination.

Build after a source change. The compiler prepares the page for Rust and the assets for the browser.

  • FMIRBinary page module→ Rust
  • CSSYour styles→ Browser
  • JSCounter island code→ Browser

03 / RUST HANDLES THE REQUEST

The content arrives as HTML.

Your handler supplies count: 7. The native renderer walks FMIR and fills that value into the page. No Node.js or V8 runs on this request path.

Simplified response markup
<section class="counter">
  <span>7</span>
  <button>Add one</button>
</section>

04 / THE BROWSER LOADS THE STYLES

The same HTML gets its look.

The response links to CSS, which the browser loads and applies. The content and design work before island JavaScript runs.

The browser receives HTML + asset links
<link rel="stylesheet" href="/assets/page.css">
<script type="module" src="/assets/counter.js">
</script>

05 / ISLAND ACTIVATION

Now that counter is interactive.

Hydration connects JavaScript behavior to existing HTML. Forma islands adopt their nodes and connect signals and events. Try Add one: only browser state changes.

Browser entry · declared island
await activateIslands({ Counter });
// Click → update signal → update the existing text.

06 / AN EXPLICIT SERVER ROUND TRIP

A click is local. A save is a request.

Your app sends an edit, validates it in Rust and stores it. An accepted JSON response updates browser state. FMIR does not need to be rebuilt.

Example POST body{ "count": 7 }
Example responseNot sent yet

Without JavaScript, a normal form can POST, then redirect to a freshly rendered page. Your app provides validation, authorization and storage.

counter / browser previewWaiting for HTML

Your source is ready.
Follow the steps to bring it to the screen.

Your count7
Browser 7Server model 7

Interactive model. No requests are sent.

WHY THIS SPLIT?

A useful page before JavaScript.

Rust produces the initial content. CSS styles it. Browser code handles the interactive parts you choose.

WHAT DOES “OPTIONAL” MEAN?

Reading needs no island.

Articles, links and ordinary forms can work without JavaScript. Use island JavaScript for a live counter or drag-and-drop reordering.

TAKE IT APARTInspect real Rust-rendered HTML ↗Learn how islands activate ↗Build an app that saves edits ↗

01 FOLLOW THE IDEA THROUGH THE STACK

The parts connect.
You can see how.

Go from the walkthrough to working code. Explore signals, native rendering, islands and server saves, then open the real examples.

01 / REACTIVITY

Update what reads the value.

Components run once. Signal accessors connect changing values to specific DOM nodes.

Learn signals →
02 / NATIVE HTML

Make the server boundary explicit.

Supported TypeScript pages compile to FMIR. Rust checks typed props and renders the HTML.

Open the Rust lab →
03 / WORKING WITHOUT JS

Use the browser’s own capabilities.

Ordinary forms still work. The newspaper demo checks access before delivering its article.

Try the paywall →

Native rendering supports a defined TS/JSX subset. Browser routes remain available for arbitrary client code. Read the contract ↗

02 LESS THEORY. MORE CLICKING.

Small signal.
Real reaction.

A few values. A derived total. Direct updates.
This little interface is running on Forma.
Go ahead—change something.

counter.js@getforma/core
import { createSignal, h, mount }
  from '@getforma/core';

function Counter() {
  const [count, setCount] =
    createSignal(0);

  return h('button', {
    onClick: () => setCount(n => n + 1),
  }, () => count());
}

mount(Counter, '#counter');
A function child keeps the value reactive.
The same idea powers the example beside it.
LIVE PLAYGROUNDTRY IT ↙

Team workspace

A plan that grows
with your people.

4 seats × $12 = $48 / month.

Enable JavaScript to try this interactive example.

Illustrative product UI. No signup, purchase, or data sent.

03 YOUR PROJECT. YOUR STARTING POINT.

A small addition.
Or the whole foundation.

You don’t have to adopt everything at once. Add a reactive interface to an existing page, or connect the full stack from TypeScript to Rust.

Just add reactivity.

A pricing calculator. A filterable catalogue. An internal tool. Use core with static hosting and the CSS you already like.

Start with the client

Let the server do its part.

Build server-rendered pages with typed Rust handlers and declared islands. Static routes can arrive with no executable JavaScript.

Start with the full stack

04 YOUR NEXT STEP

Go from curious
to building.

A working editor. A native rendering lab. Documentation with the details left in.

9,388 B gzipForma’s production browser comparison fixture.
Published 3.0 evidence, with versions and methodology.
Inspect the measurement ↗

05 LESS CLAIMING. MORE SHOWING.

Ideas with
the source left in.

Working examples and engineering notes. Find the behavior, read the explanation, then inspect what makes it work.

CASE STUDY / SERVER ACCESS

A paywall with the boundary in the right place.

Disable JavaScript. The protected story stays on the server until this fictional newspaper grants your demo session access.

Read the case study ↗Try the lab →
01 / ARCHITECTURE

Why FMIR is a boundary worth seeing.

TypeScript describes the interface. Rust owns request data. Follow the contract between them.

02 / PERFORMANCE

Numbers you can look behind.

Exact versions, complete workload tables and raw results. Read the limits alongside the measurements.

All engineering articles

06 FROM HERE TO HELLO WORLD

Make something
that responds.

Open source. MIT licensed. Built in the open.
Pick a starting point and make it yours.

Read the getting-started guide
FULL STACK / MINIMAL TEMPLATE
# Scaffold a TypeScript + Rust app
npx @getforma/create-app@3.0.0 my-app --template minimal
cd my-app/admin
npm ci
npm run build:ssr
cd ..
cargo run
Node 22+ · Rust 1.85+ · localhost:3000

The minimal template includes a typed Rust handler and an interactive counter island.

BUILT FOR THE WEB. OUT IN THE OPEN.

Good ideas deserve
good foundations.

Explore Forma on GitHub