Skip to main content
Version: 1.0

Frontend

rustdocs

The trillium-frontend crate serves a compiled JS/TS frontend project — Vite, webpack, or Next.js — from a trillium application, so a single binary carries both the api and the app that talks to it. The same handler adapts to context:

ModeWhenWhat happens
Buildsource available, dev-proxy feature offruns the frontend build at compile time and embeds the dist assets in the binary (via trillium-static-compiled)
Dev-proxysource available, dev-proxy feature onspawns the framework's dev server on a free port and proxies requests to it, including the WebSocket upgrades HMR uses
Prebuiltno package.json at the project pathembeds already-built dist assets without attempting a build — this is what cargo install of a published crate sees

Usage

use trillium_client::Client;
use trillium_frontend::frontend;
use trillium_smol::ClientConfig;

fn main() {
trillium_smol::run((
frontend!("./client")
.with_client(Client::new(ClientConfig::default()))
.with_index_file("index.html"),
));
}

The path is relative to the calling crate's Cargo.toml. The same code works in all three modes; .with_client() supplies the client the dev proxy uses and is ignored otherwise. Package manager and framework are auto-detected from lock and config files, and can be overridden:

frontend!(
path = "./client",
build = "bun run build",
dist = "dist",
)

Development with live reload

The dev-proxy feature is what switches on the proxy mode. Forward it through a feature of your own crate so day-to-day development is one flag:

[features]
dev-proxy = ["trillium-frontend/dev-proxy"]
cargo run --features dev-proxy # development: live-reloading dev server behind a proxy
cargo build --release # production: build and embed assets at compile time

SPA fallback

.with_index_file("index.html") opts into serving the index for paths no asset matched, which is what makes client-side routing survive a page reload. Only GET and HEAD requests are eligible — a POST to an arbitrary path is not a client-side route, and answering it with the index would misrepresent it as one. .with_index_predicate(...) narrows the fallback further to the paths the app actually routes, so that everything else 404s.

Rebuilds when only frontend sources change

In build mode the frontend build runs inside a proc macro, and on stable Rust proc macros can't tell cargo about file dependencies — so editing only a JS file may not trigger a rebuild. On nightly this is handled automatically. If you build on stable, the crate provides an optional build-script shim that registers the frontend sources as compile inputs; dev-proxy mode needs none of this. See the rustdocs for setup.

Publishing for cargo install

To let users cargo install your application without JS tooling on their machine, build the frontend, include the dist directory in the published crate, and exclude package.json — its absence is what selects prebuilt mode:

[package]
include = [
"src/",
"client/dist/",
]