---
title: Upgrading
description: Move between Proa versions and find what broke.
---

Proa is pre-1.0. Minor versions carry breaking changes. Each is listed in [`CHANGELOG.md`](https://github.com/proa-labs/proa/blob/main/CHANGELOG.md), with larger ones under a **Breaking** marker and a guide under `docs/migrations/` ([0.3 standalone](https://github.com/proa-labs/proa/blob/main/docs/migrations/0.3-standalone.md), [shared `WebContext`](https://github.com/proa-labs/proa/blob/main/docs/migrations/web-context.md)).

Read the changelog first, then upgrade one minor version at a time:

```bash
cargo update -p proa
cargo proa check
```

`cargo proa check` runs the template lints and the RSJS linker together, which is where most upgrade breakage surfaces. A plain `cargo check` will miss link-time errors.

## Upgrading to 0.4

Applications can replace direct rendering and framework dependencies with the
`proa` facade:

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

Import common types and macros from `proa::`, loaders from `proa::stream`, and
Axum integration from `proa::axum`. Add `actions` or `react` only when needed.
RSJS and build-time font dependencies retain their own package versions.
Direct implementation-crate dependencies remain supported for advanced uses.

Managed metadata uses `render_head`, explicit Page/Layout roles, and a head
outlet. Markdown macros use one string body, and `content_part!` wrappers are
removed. Review which outgoing URLs need the application mount and use
`cx.url(path)` for those. See [metadata](/docs/framework/metadata),
[Markdown](/docs/core/markdown), and [rendering responses](/docs/framework/responses).

## Upgrading to 0.3

0.3 was a consolidation release. Six surfaces were removed and one buffer became the standard.

| Change | What to do |
|---|---|
| Standard render buffer | Replace bespoke writers with `RenderBuffer`. See [WriteBuf](/docs/core/write-buf) |
| Component-tree ownership moved | Components take ownership of children rather than borrowing |
| Native-mobile surface removed | No replacement. The crate no longer targets it |
| Runtime-template and `Vm*` surface removed | Templates are compile-time only. Move dynamic templates to data-driven components |
| Documentation-build adapters removed | Use `proa_docs_build`. See [Proa Docs](/docs/proa-docs) |
| Product-specific route codegen removed | Author routes directly. See [Routes and layouts](/docs/framework/routing) |

## Upgrading past 0.3

Breaking changes since 0.3 affect how components are authored and linked, how fonts are declared, and how Markdown components receive their context:

- **`#[proa_component]` is gone**, along with the inherent `#[rsjs(component)]` shorthand and RSJS `async fn render` sugar. Author `WebRenderSync` or the canonical `WebRender` signature directly, then apply `#[rsjs(component)]` for composition or `#[rsjs(component, client)]` when the component originates browser work. See [HTML rendering](/docs/core/html-rendering).
- **Component-level `clients(...)` allowlists are gone.** A reachable call to a `#[rsjs::client]` helper now creates a linker edge automatically. `#[rsjs::client_module]` remains as optional grouping. See [Client helpers](/docs/rsjs/client-helpers).
- **Deployable RSJS binaries require the linked `cargo proa` pipeline.** `cargo check` and rust-analyzer still give provisional type checking, but `cargo build` on an RSJS application now fails at link time with an actionable diagnostic instead of quietly producing a provisional binary.
- **`cargo proa font add` / `font remove` and `proa-fonts.toml` are gone.** Declare fonts in code with `google_font!` / `local_font!`; a `build.rs` collector vendors them and records `proa-fonts.lock`. `cargo proa font sync` runs the same collector outside a build. See [Fonts](/docs/framework/fonts).
- **`MdContext` is gone.** `MdRenderSync` and `MdRender` now take the same `WebContext<L, B>` as the HTML traits, with the loader first and no lifetime parameters, and Markdown helpers are named `md_*` / `write_md_*`. See [Markdown](/docs/core/markdown) and the [migration guide](https://github.com/proa-labs/proa/blob/main/docs/migrations/web-context.md).
- **`RouteResponse` is an opaque deferred-render plan** and `Layout` carries metadata only; the crate is `proa_framework_axum`. See [Responses](/docs/framework/responses) and the changelog for the smaller entries (event-handler parameters, `RenderOptions`, `mdx!`, client IR v3).
- **Managed metadata uses component roles and head hooks.** Select one `ROLE = PAGE`, mark ancestor metadata layouts as `LAYOUT`, and place one `{cx.head()}` outlet in the document. Async hooks use native `async fn render_head(...) -> Result<(), WriteError>`. The former `head()` inspection getter is `head_state()`; mutations go through the checked `head_mut()` writer. Enable managed mode with `FrameworkBuilder::with_head(...)`, or use `.without_head()` while migrating a manual document. See [Metadata](/docs/framework/metadata).
- **`FrameworkBuilder::new()` and `RouteContextLayer::new(metadata)` take no manifest, and the manifest-driven React island bootstrap is gone.** Removed: `IslandManifest` and its entry types, `RouteResponse::with_islands` and `with_bootstrap`, `render_bootstrap_for_islands`, `css_asset_path`, `render_css_link`, and the router's `Extension<Arc<IslandManifest>>`. The finalizers (`finalize_route_response*`, `render_*_html_response`, `into_framework_response`) lose their manifest parameter. Link a stylesheet from the layout with `cx.url(..)`. `proa_react` islands keep rendering through `proa::react::react_bootstrap` directly. `RouteResponse::render_to_bytes` renders a finalized document with no server or runtime. See [Configuration](/docs/framework/configuration) and [Route responses](/docs/framework/responses#rendering-without-a-server).
- **Application URL composition now lives on `cx.url(path)`.** Move outgoing URL construction off `cx.request()`. The helper explicitly applies the configured base path and writes directly into HTML attributes or Markdown links; plain URL strings stay unchanged. See [Linking](/docs/framework/linking).
- **The unused legacy effect IR and fluent expression builder are gone.** Browser effects use RSJS. The `proa_ir` expression/value model, bounded evaluator, reactive bindings, and component trees remain available.
- **The deprecated pool-backed writers are gone.** Replace `SinkWriter`, `SinkPage`, `PagePool`, `MemorySink`, their in-order aliases, and `SinkCheckpoint` with `RenderBuffer` and its checkpoints. Custom transport adapters keep using `StreamChunkFlusher` / `SendStreamChunkFlusher`. See [WriteBuf](/docs/core/write-buf).
- **The file-backed action collision checker is gone.** Remove calls to `proa::actions::check_action_collisions` and `clear_action_registry`, and imports from `collision_check`. The action derive never populated that registry. Scoped action names and the action data/code generation APIs remain available.
- **The standalone `proa_codegen` crate and `proa-codegen` binary are gone.** Component libraries own their metadata-to-TypeScript generation; Proa UI has its own generators. `proa new site` continues to use the CLI's site scaffolder.
- **The old `proa_cli::scaffold::new_site` Rust API is gone.** Use the `proa new site` command, which uses `site_scaffold`. The component, layout, and data generators remain available.
- **Generators no longer add placeholder render or source-text tests.** The page, endpoint, and component generators no longer accept `--no-test`. ORIGIN keeps its loader and runtime integration tests without a full-page HTML snapshot.
- **The unused `ProaConfig` API is gone**, including `PathsConfig`, `CodegenConfig`, `ServerConfig`, and `FrameworkBuilder::{with_config, config}`. Those values did not affect the router. Mount assets with `with_static_service`, configure middleware through the builder, and bind your listener to select the host and port. See [Configuration](/docs/framework/configuration).
- **Unused framework side features are gone:** `ExternalApp`, `ExternalAppStrategy`, `FrameworkBuilder::with_external_app`, the `proxy`, `process_manager`, and `shard` modules, and the `route-export` feature/module. Compose static applications through Axum's router APIs and manage upstream processes and proxies in your deployment. Route `PageCache` and the generic readiness probes remain available.
- **Unused compatibility helpers are gone.** Use `RenderBuffer` instead of `DefaultChainedBumpWriter`, `load_json*` / `load_typed*` instead of the deprecated context `load*` aliases, and `drain_pending_boundaries_with_artifacts` or `drain_pending_boundaries_with_nonce_and_artifacts` for deferred streaming output. Use `[u8; N]` or `SmallBuf<N>` directly instead of the fixed-size aliases in `stack`; construct a `WebContext` with your writer instead of `render_into_vec_with_hint`.

> **Good to know**: The linked-pipeline change is the one most likely to surprise CI. If your pipeline runs `cargo build`, switch it to `cargo proa build`. See [CI / GitHub Actions](/docs/guides/ci).

## When an upgrade breaks the build

Work in this order, because each step rules out the next:

1. **Read the changelog entry** for every version you crossed. Breaking changes are marked.
2. **Run `cargo proa check`**, not `cargo check`. Template lints and linker errors only appear in the former.
3. **Check the RSJS boundary.** Most post-0.3 breakage is a component that used to be implicitly analysed and now needs an explicit `#[rsjs(component)]` or `#[rsjs(component, client)]`.
4. **Check your buffer.** If a custom writer stopped compiling, move to `RenderBuffer`.
5. **Search [Troubleshooting](/docs/guides/troubleshooting)** for the exact diagnostic text.

## Next steps

- [Troubleshooting](/docs/guides/troubleshooting)
  - Fix the most common Proa setup, registry, template, component, island, routing, and CI failures.
- [CI / GitHub Actions](/docs/guides/ci)
  - Gate pull requests with proa check, then deploy from the same pipeline.
- [HTML rendering](/docs/core/html-rendering)
  - Render typed Rust values to HTML with render traits and the html! macros.
- [WriteBuf](/docs/core/write-buf)
  - Pick the buffer a route renders into.
- [proa fmt / lint / check](/docs/cli/format-lint-check)
  - Format macro bodies, run lints, and gate CI.
