Skip to content

Web UI: port from SvelteKit to Rust (Dioxus) #4633

Open

Description

@billy-doyle

Description

I've built this out on my fork to see whether it holds up before asking for anyone's time:
https://github.com/billy-doyle/iggy/tree/dioxus-web-ui

It's a complete port. If a Rust UI isn't wanted, just let me know. I've used Dioxus before and thought it was a good fit here. I like not having the second toolchain and npm dependency tree, but understand if that isn't desired.

I built this with Claude Code and have reviewed the changes myself. Each PR would say what the agent wrote, as CONTRIBUTING asks.

Here it is running locally:

Image

Affected area / component

Web UI

Proposed solution

A Dioxus web UI in core/web_ui (crate iggy-web-ui), built to WebAssembly with dx and
embedded by iggy-server under /ui, the same way the Svelte static build is today.

Scope is the Svelte UI's working features, nothing more:

  • Sign-in and sign-out, dashboard layout, overview stats
  • Streams, topics, partitions: browse, create, edit, delete, add and remove partitions
  • Messages: paging, and an inspector with string/JSON/XML decoders and user headers decoded by kind
  • Users: list, search, create (with the global/stream/topic permissions editor), edit
  • Web UI settings: light/dark/system theme and the refresh interval, plus a refresh button

Pages that are placeholders in the Svelte UI (Clients, Logs, Terminal, the Server settings mock,
Delete user, Edit permissions) are left out rather than ported as placeholders.

Operators should see no change:

  • apache/iggy-web-ui keeps its name, port 3050 (PORT), GET /healthz, and PUBLIC_IGGY_API_URL.
    It becomes an nginx image serving the bundle, and a start-up script writes the API URL into a
    config.json the UI reads at run time.
  • The Helm chart is unchanged. http.web_ui works as before.
  • The crate takes over web/package.json's version, so web-ui-* image tags continue.

Differences from the Svelte UI, all small:

  • Logout calls DELETE /users/logout, the route the server has (see Web UI: ApiSchema.ts doesn't match the server's HTTP routes for logout and user updates #4214).
  • XML validation uses roxmltree. The Svelte check only works in Firefox, so in Chrome any
    payload passes as XML.
  • The JSON decoder still expands JSON documents stored as strings, but no longer turns strings
    like "123" into numbers.
  • Byte sizes are shown in readable units (456.39 GiB rather than 478456976 KiB).
  • The theme and refresh interval persist across reloads. The interval used to reset to 4s.
  • Rejected requests show the server's reason (for example "Invalid credentials").

Build and CI:

  • dx is pinned by version and SHA-256 in scripts/ci/install-dx.sh, and the script fails if it
    drifts from the dioxus version in Cargo.lock. Dioxus only ships glibc builds, so the Alpine
    server image builds the bundle in a Debian stage.
  • A rust-web-ui CI component runs the normal Rust checks plus wasm32 clippy and the bundle.
  • The bundle is about 1.08 MB of WebAssembly (381 KB gzipped).
  • The crate is a workspace member, so workspace builds compile Dioxus for the host, much like the
    bench dashboard's Yew frontend does today.
  • Third-party licenses pass scripts/ci/third-party-licenses.sh --validate for both bundles, and
    the server's LICENSE-binary lists the UI's crates instead of npm packages.

How it was checked: unit tests (33), and a headless-Chrome suite of 37 checks run against the
server's embedded UI, a separate origin through nginx, and the built iggy-web-ui and apache/iggy
images. Fork CI run.

Possible PR plan, one open at a time, each merging on its own:

  1. Crate, sign-in, streams browser (additive, the server keeps serving Svelte)
  2. CI for the crate
  3. Overview
  4. Stream/topic/partition management
  5. Users
  6. Message inspector
  7. Theme and refresh settings
  8. Standalone image (built but not yet published)
  9. Switch-over: server embeds Dioxus, publishing repointed, web/ removed

Happy to regroup this into fewer or larger PRs, or to go about it another way.

Alternatives considered

  • Keep Svelte. No migration cost, but like I said above, single toolchain.
  • Yew, which core/bench/dashboard/frontend already uses. I'm more comfortable with Dioxus and really like the ergonomics.
  • Leptos. Haven't used myself but know it is a popular alternative in rust as well.
  • Sharing types with the server through iggy_common. It doesn't build for wasm32 (tokio with
    net), so the UI keeps its own serde types for the HTTP JSON. Those could later be checked
    against the OpenAPI document proposed in feat(server): add an OpenAPI document for the HTTP API #4212.

Contribution

  • I'm willing to submit a pull request to implement this feature

Good first issue

  • I think this could be a good first issue for a new contributor

Activity

  1. billy-doyle commented on Oct 25, 2026

    @billy-doyle
    Author

    Thanks for the go-ahead on Discord. I'm about to open the first PR, so here's the plan I'll follow.

    Since opening this issue I've rebased onto current master. #4625 added a Metadata changes page to the
    Svelte UI, so I've ported that too, which makes the plan 10 PRs instead of 9. The full series is here:
    https://github.com/billy-doyle/iggy/tree/dioxus-web-ui-v2

    I'll keep one PR open at a time and open the next one after the previous one merges. Each one says which
    part it is. Until a part has its PR, its "preview" link shows just that part's diff on my fork.

    PR 2 only touches CI config and scripts, so I can open it first if you'd rather have CI in place before
    any UI code. I'm also happy to group these differently if that's easier to review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

webWeb UI related changes

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions