Docs

Quickstart

Create a Proa site, run it, and edit your first route in five minutes.

Open Markdown

This quickstart gets you from an empty directory to a hello world Proa. It should take about five minutes once Rust, GitHub access to the Proa repository, and the Proa CLI are ready.

Building with an AI agent? The complete documentation is served at /llms-full.txt for agent consumption. LLM docs lists the other machine-readable interfaces.

1. Install Proa

Install the CLI with the one-line installer.

curl -fsSL https://proa.so/install.sh | sh
irm https://proa.so/install.ps1 | iex

The current prebuilt binary targets 64-bit Ubuntu 24.04+. On macOS, Windows, ARM Linux, and older Linux releases, the installer builds the CLI from source automatically; while the source repository is private, that fallback requires GitHub access to it.

Prefer building from source?
CARGO_NET_GIT_FETCH_WITH_CLI=true \
  cargo install --git https://github.com/proa-labs/proa --locked proa-cli

The prefix matters: Proa crates are git dependencies, and the git CLI fetch picks up credential helpers and proxy settings that Cargo's built-in fetcher does not. Troubleshooting has the details.

Pin a specific version?

Set PROA_CLI_VERSION to a version that is already published on the stable channel, then pass it to the installer:

curl -fsSL https://proa.so/install.sh | PROA_VERSION="$PROA_CLI_VERSION" sh

2. Create A Site

The kitchen-sink starter, and the fastest way to see a production Proa app fit together:

proa new site origin-atlas --template origin --yes

This scaffolds ORIGIN, a live routing atlas that exercises the whole framework: a custom RIPEstat DataLoader with request-scoped deduplication and a TTL cache, streamed Suspense sections, proa-ui components that each carry a Markdown representation (the same page serves /site/<host>.md for agents), rsjs islands for the interactive surfaces, Tailwind styling with a themed token set, self-hosted fonts, and a golden test that pins the server-rendered document byte for byte. The generated README.md doubles as an editing guide.

Checkpoint: src/lib.rs, src/loader.rs, and src/components/ should exist, and proa dev --open shows the atlas.

A minimal Axum-backed application with the core boundaries in place:

proa new site hello-proa --yes
Cargo.toml
proa.config.json
proa.lock.json
build.rs
src/
components/
endpoints/
layouts/
pages/
app.rs
main.rs
routes.rs
migrations/
public/
favicon.svg
styles.css
styles/
input.css
tests/

Checkpoint: Cargo.toml, src/main.rs, src/pages/home.rs, and src/layouts/root.rs should exist.

You do not have to build the common UI yourself: proa-ui is a shadcn-style catalog of ready-made Proa components, and proa ui add button vendors the source into src/components/ where you own and edit it like any other file. Worth knowing before you write your first card, dialog, or form by hand.

3. Run The Server

cd hello-proa && proa dev --open

proa dev builds configured assets, runs the linked RSJS pipeline, starts the Axum server on http://localhost:3000, and restarts when Rust, style, or config files change.

Open tabs reload themselves once the rebuilt server is accepting connections again, so you do not have to refresh by hand. Stylesheet edits are applied without navigating, which keeps scroll position and form state. Turn it off or retune it with --no-reload, --no-css-hot-swap, and --debounce-ms; see Live reload.

4. Edit The First Page

Set up your editor for Rust navigation and completion, including expressions inside raw Markdown templates.

Open src/pages/home.rs and find the HomePage render implementation. The default site ships with a database, so the page is an async WebRender impl built with html!; sites scaffolded without one use WebRenderSync and html_sync!. Either way, replace the placeholder copy inside the <main> with content for your first route, keeping the macro the file already uses:

html! {
    <main class="mx-auto max-w-4xl px-6 py-16">
        <p class="font-mono text-sm uppercase tracking-[0.18em] text-zinc-500">"Proa"</p>
        <h1 class="mt-4 text-5xl font-semibold">"Fast SSR, typed in Rust"</h1>
        <p class="mt-4 text-lg leading-8 text-zinc-600">
            "This page renders from a compiled template macro."
        </p>
    </main>
}

Refresh the browser. You have now edited a server-rendered Proa route and seen the compiled template update in a real page.

5. Add A Route

Create src/pages/about.rs:

use axum::response::IntoResponse;
use proa_core::{to_async, DataLoader, WebContext, WebRenderSync, WriteError};
use proa_framework_axum::RouteResponse;
use proa_macros::html_sync;

use crate::layouts::RootDocument;

pub async fn handler() -> impl IntoResponse {
    RouteResponse::ssr_async(RootDocument {
        title: "About",
        content: to_async(AboutPage {
            heading: "About",
            body: "This route renders from src/pages/about.rs.",
        }),
    })
}

struct AboutPage {
    heading: &'static str,
    body: &'static str,
}

impl<L: DataLoader> WebRenderSync<L> for AboutPage {
    fn render(self, cx: &mut WebContext<L>) -> Result<(), WriteError> {
        html_sync! {
            <main data-component="about-page" class="mx-auto max-w-3xl px-6 py-16">
                <h1 class="text-4xl font-semibold">{self.heading}</h1>
                <p class="mt-4 text-zinc-600">{self.body}</p>
            </main>
        }
        .render(cx)
    }
}

Export it from src/pages/mod.rs, keeping the generated home module:

pub mod about;
pub mod home;

Register it in src/routes.rs, between the markers the generator maintains:

let (router, metadata) = router_from_spec(vec![
    // proa:route-specs:start
    Route::endpoint("/", get(pages::home::handler)),
    Route::endpoint("/about", get(pages::about::handler)),
    // proa:route-specs:end
]);

That is the whole route flow. RouteResponse renders the page and finalizes the HTTP response, RootDocument supplies the shared page shell (it is async-capable, so a synchronous page goes in through to_async), and the fields passed to AboutPage are compile-time-checked props. Add page caching to the route spec when the response should carry CDN cache headers. Add Axum extractors such as Path, Query, or State to handler when the route needs request data.

Open http://localhost:3000/about. Check the route table:

proa site routes

Checkpoint: /about appears in the route report and renders in the browser.

6. Validate Before Moving On

Run Proa's source-aware checks before the full build:

proa fmt --check --verify src
proa lint src --deny warnings
proa build

proa fmt --check --verify checks the canonical template layout and verifies that formatting would preserve the rendered output. proa lint understands Proa render paths, so --deny warnings catches performance problems such as intermediate rendered strings, avoidable allocations, and async renderers that never await. Once those focused checks are clean, proa build validates the complete linked application.

Next steps

Search

Type at least 2 characters