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/",
]