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)

This additionally unpacks the full Leptos+Trunk UI source tree into 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 into Cargo.toml / README.md. Defaults to --out’s directory basename.
  • --force — overwrite files inside --out if 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 to cratestack/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.
The framework repo’s own local-dev story (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

The first step writes 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 bundled index.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.html so 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.