# Leptos Full-Stack Rust (SSR + Hydration on Axum) — AI Agent Guidelines & Architecture Rules

> Full-stack Rust web app rules: Leptos 0.8 SSR with hydration, server functions on Axum, sqlx + PostgreSQL, cargo-leptos and Tailwind, written for AI agents.
> Technologies: Rust, Leptos, Axum, WebAssembly, PostgreSQL, sqlx, Tokio, Tailwind CSS

---

## AGENTS.md
```markdown
# Project Architecture & Guidelines (Leptos Full-Stack Rust)

## 1. System Architecture
- **Framework**: Leptos 0.8 with server-side rendering and hydration (`ssr` and `hydrate` features), served by Axum through `leptos_axum`.
- **Build tool**: `cargo-leptos` builds the server binary and the WASM client bundle together and serves `cargo leptos watch` with live reload.
- **Data**: PostgreSQL via `sqlx` 0.9, used only inside server-only code.
- **Styling**: Tailwind CSS (configured through cargo-leptos `tailwind-input-file`).
- **Toolchain**: Rust 2024 edition, pinned in `rust-toolchain.toml`, with the `wasm32-unknown-unknown` target installed.

## 2. Project Layout
- `src/app.rs`: `App` component, `<Router>` and `<Routes>`, the HTML `shell` function.
- `src/pages/<route>.rs`: one module per route; components only.
- `src/api/<feature>.rs`: `#[server]` functions for that feature, the only place that touches the database.
- `src/server/`: `#[cfg(feature = "ssr")]` modules (pool setup, auth, repositories). Never imported by client code.
- `src/main.rs`: Axum router with `leptos_routes_with_context`, providing the `PgPool` through context.
- `src/lib.rs`: the `hydrate()` entry point for the WASM bundle.
- `style/`, `public/`, `migrations/`, `end2end/` (Playwright).

## 3. Components and Reactivity
- Components are `#[component] fn Name(...) -> impl IntoView` with `view! {}`; keep them small and named after what they render.
- State with `signal()`, derived values with `Memo` or plain closures; never duplicate state that can be derived.
- Load data with `Resource::new(source, fetcher)` (serialised from server to client during hydration) and render it inside `<Suspense>` or `<Transition>`. Use `LocalResource` only for browser-only data.
- Mutations go through `ServerAction::<Fn>::new()` and `<ActionForm>`, so forms work before WASM loads (progressive enhancement).
- Avoid `create_effect`-style effects for data flow; reach for resources and actions first.

## 4. Server Functions (the API boundary)
- Every server function is `#[server] pub async fn name(args) -> Result<T, ServerFnError>` in `src/api/`.
- Get shared state with `use_context::<PgPool>()` (provided in `leptos_routes_with_context`), never a global.
- Validate and authorise inside the server function; it is a public HTTP endpoint even if only your UI calls it.
- Return domain structs that derive `Serialize`, `Deserialize`, `Clone`; never return `sqlx` rows or secrets.
- Server-only crates (`sqlx`, `argon2`, `tower-sessions`) are `optional = true` and enabled only by the `ssr` feature.

## 5. Feature Gating Rules
- Code that touches the database, filesystem or secrets lives behind `#[cfg(feature = "ssr")]`.
- Code that touches `web_sys`/`window` runs only in effects or behind `#[cfg(feature = "hydrate")]`.
- Hydration mismatches come from rendering different markup on server and client (random IDs, `Utc::now()`, browser-only checks); compute such values on the server and pass them down.

## 6. Agent Loop (run after every change)
1. `cargo check --features ssr` and `cargo check --features hydrate --target wasm32-unknown-unknown`; both must be clean.
2. `cargo clippy --features ssr -- -D warnings`.
3. `cargo test --features ssr`.
4. For UI changes, `cargo leptos end-to-end` (Playwright) or load the page and check the browser console for hydration warnings.
- Do not fix borrow errors in closures by sprinkling `.clone()` on large values; move `Copy` signals into closures instead (signals are `Copy` in 0.8).
- No `unwrap()` in server functions; map errors into `ServerFnError`.

## 7. Testing and CI
- Unit-test pure logic and server-only repositories with `#[sqlx::test]`.
- Playwright tests in `end2end/` cover each route with JavaScript disabled once (ActionForm must still work) and enabled once.
- CI: `cargo fmt --check`, both `cargo check` targets, clippy, tests, `cargo leptos build --release`.
- Deploy the server binary plus the `target/site/` directory; set `LEPTOS_SITE_ADDR` and `LEPTOS_SITE_ROOT` in the environment.
```

---

## CLAUDE.md
```markdown
# CLAUDE.md — Leptos Full-Stack Rust

@AGENTS.md

## Commands
- `cargo leptos watch` - dev server with live reload (server + WASM)
- `cargo check --features ssr` - server-side type check
- `cargo check --features hydrate --target wasm32-unknown-unknown` - client-side type check
- `cargo clippy --features ssr -- -D warnings` - lint
- `cargo test --features ssr` - tests
- `cargo leptos end-to-end` - Playwright tests
- `cargo leptos build --release` - production build into `target/` and `target/site/`

## Non-negotiables
- Database access only inside `#[server]` functions or `ssr`-gated modules.
- Both feature targets must compile before you say a change is done.
- Forms use `ActionForm` so they work without WASM.
```

---

## .cursor/rules/stack.mdc
```markdown
---
description: Leptos full-stack Rust (SSR + hydration on Axum) rules
globs: ["**/*.rs", "style/**/*", "end2end/**/*.ts"]
alwaysApply: false
---

# Leptos Full-Stack Rust

- Leptos 0.8 SSR + hydration on Axum via `leptos_axum`; build with `cargo-leptos`.
- Components in `src/pages/`, `#[server]` functions in `src/api/`, server-only code behind `#[cfg(feature = "ssr")]`.
- Data via `Resource` + `<Suspense>`; mutations via `ServerAction` + `<ActionForm>`.
- `PgPool` comes from `use_context`, provided in `leptos_routes_with_context`.
- Server functions validate and authorise their input; they are public endpoints.
- Check both targets: `--features ssr` and `--features hydrate --target wasm32-unknown-unknown`.
- No `unwrap()` in server functions; return `ServerFnError`.
```

---

## Architecture Overview & Best Practices
## Architecture Overview

**One language end to end.** Leptos renders components on the server for fast first paint and SEO, hydrates them in the browser as WebAssembly, and calls typed `#[server]` functions instead of a hand-written REST API. Axum hosts it, sqlx talks to PostgreSQL, and `cargo-leptos` builds both halves.

### Why it suits AI coding agents

- **The client-server contract is a Rust function signature.** Change a server function's return type and every call site in the UI fails to compile, so an agent cannot ship a frontend and backend that disagree.
- **Feature gates make boundaries explicit.** Database code behind `ssr` cannot leak into the browser bundle without a compile error.
- **Progressive enhancement by default.** `ActionForm` posts work before the WASM loads, which keeps end-to-end tests simple.

### Trade-offs

Two compile targets mean slower loops than the [Axum + htmx stack](/rules/rust-axum-htmx-askama), WASM bundles add weight, and the component ecosystem is young. Start with htmx unless the UI genuinely needs client-side state. Background: [Building web apps in Rust in 2026](/insights/rust-web-apps-ai-agents-2026).