Docs
Quickstart
Create a Proa site, run it, and edit your first route in five minutes.
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.tomlproa.config.jsonproa.lock.jsonbuild.rssrc/components/endpoints/layouts/pages/app.rsmain.rsroutes.rsmigrations/public/favicon.svgstyles.cssstyles/input.csstests/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
- Tutorial
- Add typed data, components, routes, and a form action.
- proa new site
- Scaffold a project and see where pages, layouts, components, and assets live.
- HTML rendering
- Render typed Rust values to HTML with render traits and the html! macros.
- Troubleshooting
- Fix the most common Proa setup, registry, template, component, island, routing, and CI failures.
- Deploy
- Pick a deployment target, then follow the guide for it.