Wisp

Type to search every docs page and heading.

Getting Started

Wisp is a fast, fun web framework for Rust. A folder is a URL, and its +page.wisp is the page: an optional block of Rust, then markup. Templates compile to plain Rust, and the app, styles and static files become one small binary.

Install and Run

You need Rust 1.88 or later.

cargo install --git https://wisp.ar0.eu wisp-cli
wisp new my-app        # --template demo|minimal|api
cd my-app
wisp dev               # http://127.0.0.1:3000

wisp dev rebuilds as you edit, hot reload keeps your $state, and Alt+Shift+W opens the devtools with the routes table.

A Page

src/routes/+page.wisp:

---
let name = cx.query_or("name", "world".to_string());
---

<h1>Hello, {name}!</h1>

<button on:click="count++">Clicked {:count} times</button>

<script>
  let count = $state(0)
</script>

The --- block is Rust that runs per request, {name} renders on the server, and {:count} is JavaScript state in the browser. With JavaScript off, the server's HTML still works.

Data and a Form

A model lives in src/db.rs; its pub items are in every route file:

#[model]
pub struct Todo {
    #[validate(len = 1..=100)]
    text: String,
}
pub static TODOS: Table<Todo> = Table::saved();

An action handles the form; fields writes a labelled input per parameter:

---
#[action]
fn add(todo: Todo) {
    TODOS.add(todo);
}

let count = TODOS.len();
---

<title>Todos ({count})</title>
<form action="?/add" fields><button>Add</button></form>
{#each TODOS.all() as todo}
  <p>{todo.text}</p>
{/each}

A bad value is a 422 that shows each problem next to its input and keeps what was typed, with no JavaScript. There is no use line: a prelude brings in Cx, Table, Email, redirect, error and the rest.

Files

src/main.rs                 wisp::main!();   (generated; leave it)
src/app.html                shell with %wisp.head% %wisp.body% (optional)
src/app.css | app.scss      served at /_app/app.css (Tailwind if it imports it; Sass, no Node)
src/hooks.rs                fn init() once; fn before(cx) every request
src/db.rs                   models and tables; its pub items are in every route file
src/components/Card.wisp    <Card title={x}>...</Card>
src/routes/.../+page.wisp   page: optional --- Rust block, then markup
src/routes/.../+layout.wisp wraps pages below; must <slot />
src/routes/.../+error.wisp  error page
src/routes/.../+server.rs   endpoints: fn get/post/put/patch/delete/list
src/routes/.../+page.md     Markdown page
static/                     served at /

Folders: blog static, [slug] param, [[lang]] optional, [...rest] rest, [id=int] digits, (group) not in the URL. More in Files and routes.

Check, Test, Build

wisp check      # templates, routes, accessibility lints
wisp test       # your tests (wisp test --browser drives a real browser)
wisp fmt        # format
wisp build      # one release binary

wisp build --static writes HTML for any static host, --docker a container, --target cloudflare|deno|vercel|netlify|node|bun|lambda an edge or serverless build. See Deploying.

Next

All Pages

Start

  • Why Wisp: Wisp is fast to run, cheap for an AI to write, and shaped by how the code looks
  • Overview: What Wisp gives you for reactivity, AI tokens, rendering, data, styling, tooling and deploy
  • Benchmarks: What Wisp measures for speed, and how to run the benchmark tools yourself

Design

Browser code

APIs

Data and auth

Deploy and run

Hosting

Project

  • Tokens: What an app costs to write in AI tokens, measured against other stacks
  • Tokens, app by app: Four small apps in Wisp and six other frameworks, with the Wisp versions

Is This Page Useful?

Edit This Page