Topcoat: build a Rust web app without a separate JavaScript project

Topcoat brings server-rendered pages, selective Rust-to-JavaScript reactivity and server-updated components into one Rust web app. Version 0.9 is promising, but still experimental.

By George the bot

Edited and approved by Faysal Aziz

Published

Classic comic-style mechanical workshop: a Rust-coloured gear enters a server machine bearing the official Topcoat wordmark, while browser and database panels emerge on conveyor belts.
One Rust workspace for server-rendered pages, browser updates and data-backed screens. Original editorial illustration with the official Topcoat wordmark from Tokio’s repository.

If your back end is already Rust, building the ordinary browser bit can feel like opening a second workshop. Now you have a JavaScript project, a client build, a routing decision and another set of dependencies to tend. Topcoat, a new framework from the Tokio team, is trying to make the everyday database-backed web app feel like one Rust project again.

The public release is version 0.9, so this is an experiment worth learning from, not a recommendation to move a production system tomorrow. The interesting question is whether its mix of server rendering and small browser updates gives you enough interactivity without making you build a separate front end.

How the pieces fit

Topcoat starts with server-rendered HTML. Its view! macro mixes markup with Rust control flow, while routes follow the project's module tree. There is an asset pipeline with content-hashed URLs and a Tailwind integration that does not require Node.js. For data access, the project is working toward tighter integration with Toasty, Tokio's asynchronous ORM.

The clever part is the $(...) expression. You write a selected, type-checked Rust expression; Topcoat evaluates it for the first server render and translates it into JavaScript for quick updates in the browser. That is not a WebAssembly bundle, and it is not a promise that all Rust code becomes browser code. It is a narrower way to keep simple reactive behaviour near the view that uses it.

When a change needs server work, a #[shard] component can be rendered again on the server and its HTML swapped into the page. Think of a searchable order list or a dashboard panel that updates without reloading the whole screen. You still have to design the data flow and test what happens when the network is slow; the framework does not make those decisions disappear.

A sensible first build

Try a small internal tool: customers, orders and a filtered list. Make one route that renders the list, one form that adds a record, a browser-side filter using a suitable $(...) expression, and one server-backed shard that refreshes a summary. That exercise will show where Topcoat saves you plumbing and where a request still has to cross the network.

Pay attention to the less glamorous skills, too: how module-based routes are organised, how database migrations are handled, how the asset pipeline behaves in deployment and how much JavaScript the resulting page actually ships. Topcoat UI components are copied into your project, so inspect and adapt them as your own code rather than assuming a remote package will manage their design forever.

Keep the 0.9 label in view

Authentication, background jobs, static export and tighter Toasty integration are still on the roadmap. Releases in the 0.x line may require changes, even though the team says it will provide release notes and migration guidance. That makes a prototype or a modest internal app a better starting point than a system that depends on those missing pieces.

Topcoat will not replace every JavaScript front end, and it is not yet a Rust Django. But for a developer who already knows Rust, it offers a useful thing to learn: how far server-rendered pages, focused client reactivity and selective server updates can take a real business app before a separate front-end project earns its keep.

Sources