An interactive, explained guide to egui.
Every topic is one page with four things on it at once: a live widget, the settings that drive it, the code that would produce exactly what you see, and an explanation of why it works that way.
Turn a knob and both the widget and the code change — because both are generated from the same state. The snippet cannot drift from the thing it documents.
Available in English and Russian. It opens in your system's language, and the menu in the top bar switches at any time.
egui has two good sources of truth, and a gap between them:
- egui.rs shows you widgets, but not what makes them work.
- docs.rs/egui explains every item, but shows you nothing.
Neither teaches the fundamentals — Ui, Response, Id, Sense, Atom — that everything
else in egui is built on. This project is an attempt at the missing middle.
Early. Six lessons exist, all in Fundamentals, in reading order: eframe::App,
Button, Response, Id, Ui, Atom. The sidebar numbers them, and each lesson ends
with Previous / Next.
Planned: layout, style & theme, text & fonts, input & interaction, painting, containers & windows, memory & state. The sidebar lists them greyed out, so the shape of the guide is visible while it is being built.
Pinned to egui 0.36.2. That version changed a lot (eframe::App::ui replaced update,
Panel replaced SidePanel/TopBottomPanel), so most egui material you will find online no
longer compiles. Everything here is written against 0.36.
cargo runIn a browser:
rustup target add wasm32-unknown-unknown
cargo install --locked trunk # or: brew install trunk
trunk serve --releaseA lesson is a single type implementing Lesson. It owns its settings as
plain fields, and two methods read those same fields:
demo(&mut Ui, Tr)— builds and shows the real widgetcode(Tr) -> String— builds the snippet, from the same values
Because both derive from one source, the code on screen always matches the widget above it.
src/lessons/button.rs is the reference implementation.
A lesson holds no text of its own. Everything a reader sees, including the comments in the
generated code, lives in Fluent files under locales/, and
reaches the lesson through the Tr it is handed. The demo and the snippet read the same
message, so they stay in sync in every language. Names from egui's API (clicked(),
max_rect, frame, ...) are never translated: they are what the reader types.
src/lessons/<name>.rs— implementLessonsrc/lessons/mod.rs— add it toall()locales/<language>/<id>.ftl— its text, one file per languagetests/snapshots.rs— add arender_lesson(...)test
src/app.rs does not change; it only knows dyn Lesson.
No Rust needed:
- Copy
locales/en/tolocales/<code>/, where<code>is the language's tag (de,pt-BR, ...). - Translate the text after each
=. Keep the ids, keep{ $variables }, and keep egui's API names as they are.language-nameinapp.ftlis the language's own name for itself. UPDATE_SNAPSHOTS=1 cargo testrenders every lesson in the new language. Look at thetests/snapshots/*_<code>.pngimages: that is how a too-long translation shows up.
The folder is picked up by the build automatically, and the language appears in the menu.
For plurals, see how id-distinct in locales/ru/id.ftl uses Fluent's
selectors.
cargo test checks that every language has exactly the messages English has, so a
translation can't quietly fall behind when a lesson changes.
cargo test renders every lesson headlessly with
egui_kittest, once per language, and compares the result
against the PNGs in tests/snapshots/ — the same images used in this README. Layout
regressions show up as an image diff. It also checks the translations: every file parses,
every language has the same messages, and every message the code asks for exists.
cargo test # check
UPDATE_SNAPSHOTS=1 cargo test # accept the current rendering as the new baselineDual-licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT)
at your option — the same terms as egui itself.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this work by you, as defined in the Apache-2.0 license, shall be dual-licensed as above, without any additional terms or conditions.
