---
title: Quickstart
description: Create a Proa site, run it, and edit your first route in five minutes.
sidebar_title: Quickstart
---

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

<Callout type="info">

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

</Callout>


## 1. Install Proa

Install the latest stable CLI from crates.io using a current Rust toolchain:

```bash
cargo install proa-cli --locked
```

Run the same command to update an existing Cargo installation.
Proa is public; no GitHub credential or Cargo publishing token is needed.
[Troubleshooting](/docs/guides/troubleshooting) covers dependency resolution
failures. The Basic app also requires the Tailwind CSS CLI on your `PATH`;
see [Tailwind prerequisite](/docs/cli/new-site#tailwind-prerequisite) before
continuing.

## 2. Create A Site

<Tabs data-items="Basic app|ORIGIN (kitchen sink)">


<Tab value="Basic app">


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

```bash
proa new site hello-proa --yes
```

```proa-filetree
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.

</Tab>


<Tab value="ORIGIN (kitchen sink)">


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

```bash
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
command-line clients, retrieval pipelines, and agents), rsjs islands for the
interactive surfaces, Tailwind styling with a themed token set, self-hosted
fonts. 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.

</Tab>


</Tabs>


Generated sites use the `proa` crate for their application API. Common types and
macros come from `proa::`; the Axum integration is `proa::axum` and loaders live
under `proa::stream`. To add Proa to an existing application:

```toml
[dependencies]
proa = { version = "0.4", features = ["axum"] }
```

You do not have to build the common UI yourself: [proa-ui](https://ui.proa.so) 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.

The Basic app starter writes its document title directly in `RootDocument` and uses manual head rendering. Its pages keep the default component role. To adopt managed metadata, follow [Layouts and Pages](/docs/framework/layouts-and-pages) and add an explicit Page boundary and head outlet together. ORIGIN already uses that managed document pattern.

The remaining steps use the **Basic app** named `hello-proa`. If you chose
ORIGIN, follow its generated `README.md` and run commands inside `origin-atlas`.

If `styles/starter.css` already exists and `styles/input.css` imports it,
continue to the next step.

<Accordions>


<Accordion title="My starter has no styles/starter.css yet">


Inside `hello-proa`, preserve the starter design before Tailwind rebuilds
`public/styles.css`:

1. Move `public/styles.css` to `styles/starter.css`. If `styles/starter.css`
   already exists, keep that file.
2. Replace the contents of `styles/input.css` with:

```css
@layer theme, base, proa-compat, components, utilities;
@import "tailwindcss";
@import "./starter.css" layer(components);
@source "../src/**/*.rs";
```

Tailwind will regenerate `public/styles.css` with the starter design included.

</Accordion>


</Accordions>


## 3. Run The Server

```bash
cd hello-proa && proa dev --open
```

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

The first build downloads dependencies and compiles the Rust crates, which can
take several minutes. The terminal reports each build stage; `--open` waits
until the server accepts connections before opening your browser.

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](/docs/cli/new-site#live-reload).

## 4. Edit The First Page

Set up [your editor](/docs/guides/editor-setup) for Rust navigation and
completion, including expressions inside raw Markdown templates.

Open `src/pages/home.rs`. Inside `impl<L: DataLoader> WebRender<L> for
HomePage`, find the `<section class="hero">` in the existing `html!` macro.
Replace only its `<h1>` and `<p class="lede">` with these two elements:

```rust
<h1>"Fast SSR, typed in Rust"</h1>
<p class="lede">"This page renders from a compiled template macro."</p>
```

Keep the surrounding template, including `DatabaseBadge` and `DatabaseCard`.
Those children load the database status, so this page uses async `WebRender`
and `html!`. A static component can use `WebRenderSync` and `html_sync!`, as
shown by the About page below.

In **the same file**, find `impl<L: DataLoader> MdRender<L> for HomePage`.
Inside `render_md`'s existing `md! { r###"…"### }` template, replace the old
heading and the paragraph immediately after it with these Markdown lines:

```markdown
Fast SSR, typed in Rust

This page renders from a compiled template macro.
```

Keep the other Markdown content and child expressions. Proa's content-coverage
lint compares rendered content across the two implementations; changing both
keeps the HTML and Markdown representations consistent with the same page edit.

Refresh the browser and open [/index.md](http://localhost:3000/index.md) to see
both representations. Edit `styles/starter.css` to change the starter's design;
`public/styles.css` is the generated output and is rebuilt by Tailwind.

## 5. Add A Route

Create `src/pages/about.rs`:

```rust
use axum::response::IntoResponse;
use proa::{to_async, DataLoader, WebContext, WebRenderSync, WriteError};
use proa::axum::RouteResponse;
use proa::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:

```rust
pub mod about;
pub mod home;
```

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

```rust
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](/docs/framework/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](http://localhost:3000/about). Check the route table:

```bash
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:

```bash
proa fmt --check --verify src
proa lint src --deny warnings
proa test
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. `proa test` also checks both page representations
and that the two database components share one origin query.

## Next steps

- [Tutorial](/docs/guides/tutorial)
  - Add typed data, components, routes, and a form action.
- [proa new site](/docs/cli/new-site)
  - Scaffold a project and see where pages, layouts, components, and assets live.
- [HTML rendering](/docs/core/html-rendering)
  - Render typed Rust values to HTML with render traits and the html! macros.
- [Troubleshooting](/docs/guides/troubleshooting)
  - Fix the most common Proa setup, registry, template, component, island, routing, and CI failures.
- [Deploy](/docs/framework/deployment)
  - Pick a deployment target, then follow the guide for it.
