Studio Eject
cratestack studio eject hands you a standalone Rust binary project
that embeds Studio against your own .cstack schemas — something you
can cargo run, commit to your own repo, and customize. There’s a
separate, unrelated embed-ui cargo feature for framework
contributors building cratestack-cli itself from a source checkout;
see Bundle the UI into the binary
below if that’s what you’re after.
Eject a starter project
cargo run boots a fully working Studio instance — the Leptos UI
is already bundled in, with no Trunk or wasm toolchain needed. That
works because cratestack-studio’s crates.io release ships with its
own Trunk build baked in (embed-ui is a default-on cargo feature),
so a project that depends on the published crate gets the UI for
free. Verified by ejecting into a scratch directory and running a
genuinely clean cargo build against the real crates.io dependency
graph — no local framework checkout involved.
main.rs is intentionally small — compose your own routes,
middleware, or auth on top of cratestack_studio::build_router.
Customizing the UI (--with-ui)
my-studio/ui/ — the same source layout as
crates/cratestack-studio-ui in the framework repo (app/,
editors/, tools/, api/ submodules), plus its own Cargo.toml
(a standalone [workspace] sentinel table keeps it out of any parent
workspace) and a Trunk.toml that proxies /api/* back to
127.0.0.1:7878.
This gives you the exact same local-dev loop the framework repo uses
for itself: run the backend with cargo run in one terminal, then
cd ui && trunk serve in another to iterate on the UI at
127.0.0.1:8080 against live data. It’s a fork for active
iteration, not a way to bake your customized UI back into the
single cargo run binary — see the caveat under
When to bundle vs. when not to.
Flags
--out <DIR>(required) — where to write.--name <NAME>— project name written intoCargo.toml/README.md. Defaults to--out’s directory basename.--force— overwrite files inside--outif the directory already exists and has contents. Without--force, eject refuses to write into a non-empty directory.--with-ui— also unpack the Leptos+Trunk UI sources into<out>/ui/.
Upgrading after an eject
The eject output is a point-in-time snapshot. There’s no automated re-eject path back to upstream — an ejected project is a fork the moment you customize it. When a new framework version ships meaningful changes, you’ll want to merge them manually or re-eject into a fresh directory and diff your customizations across. If your changes are upstream-friendly, please open a PR tocratestack/cratestack instead.
Bundle the UI into the binary
This section is about building
cratestack-cli itself from a
cratestack/cratestack git checkout — relevant to framework
contributors, or anyone who wants a locally-modified UI baked into
their own cratestack-cli build. If you just want to run your own
Studio instance, cratestack studio eject already gives you one with
the UI bundled — see above. That works with zero Trunk/wasm setup
because the published cratestack-studio crate ships with its
Trunk build already baked in; a fresh git checkout doesn’t have that
build yet, which is what this section produces.cratestack studio run + trunk serve in two terminals) is fine
for hacking on Studio itself, but a plain cargo build -p cratestack-cli from a clean checkout has no Trunk build to embed yet
and falls back to a placeholder page. Building the UI first, then
building cratestack-cli with embed-ui, gets a real single binary.
Build prerequisites
Build
crates/cratestack-studio-ui/dist/, which
rust-embed reads at compile time during the second step. Skip step
one and Cargo will fail at the embed step with rust-embed’s standard
“folder does not exist” error, naming the missing path.
Run
/returns the bundledindex.html./api/*keeps serving JSON (mounted before the UI routes, so any future overlap resolves in favor of the API).- Unknown paths fall back to
index.htmlso the browser’s client-side routing still works.
When to bundle vs. when not to
The last row isn’t supported out of the box yet —
embed-ui’s
mechanism is one step more indirect than “read straight from a fixed
path”: cratestack-studio’s build.rs copies either the sibling
cratestack-studio-ui/dist/ (in a dev checkout) or a bundled
embedded-ui-dist.tar.gz (in a published crate) into $OUT_DIR/ui-dist
at build time, and rust-embed then embeds that $OUT_DIR path at
compile time — not the sibling dist/ directory directly. There’s no
environment-variable or config override for any of this. A future
small change would let build.rs point at an arbitrary dist/
directory so an ejected --with-ui fork could produce a single-binary
distribution of its own customized UI.
Defaults stay light
embed-ui is a default-on cargo feature of cratestack-studio —
you don’t need to pass any flag to get it — but it degrades
gracefully rather than forcing a Trunk/wasm toolchain on every build:
with nothing to embed, it falls back to a placeholder page instead of
failing the build. That’s why a plain cargo build -p cratestack-cli
in a fresh framework-repo checkout works fine (schema-validation and
migration-diff CLI surfaces are entirely unaffected), and why the
eject output — which depends on the
published crate, already containing a real Trunk build — serves a
working UI without you installing Trunk at all.