# eframe_template: the starting point for a Rust GUI app with egui

> eframe_template is the reference repository for building desktop and web applications with egui and eframe, and it ships the pieces that usually take an afternoon to assemble, a working example app, a Trunk pipeline for WebAssembly, a service worker and a GitHub Pages workflow. It suits teams who want to start from a running app rather than from a blank crate, and it moves with egui's frequent breaking releases.

**emilk/eframe_template** — The easy way to make a Rust app with a GUI

- Repository: https://github.com/emilk/eframe_template
- Stars: 1,167 · Forks: 235
- Language: Rust
- License: Apache-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/emilk-eframe-template

## What the repository actually is

eframe_template is a template repository for eframe, the framework for writing apps using egui. The stated goal is for it to be the simplest way to get started writing a GUI app in Rust.

That is a narrower and more useful claim than it looks. The repository is not a library, and it is not a framework. It is a small, working egui application with the surrounding scaffolding already in place, and the scaffolding is the value: you can compile the app natively or for the web, and share it using GitHub Pages.

The layout shows what is included. There is a src directory with the application, an assets directory with the service worker, an index.html for the web build, a Trunk.toml for the web bundler, a rust-toolchain file pinning the compiler, a flake.nix for Nix users, a check.sh script, and both fill_template.sh and fill_template.ps1 for renaming the project. The Cargo.toml declares egui 0.36.0 and eframe 0.36.0 with the persistence feature, log 0.4.27, and serde with the derive feature for app persistence.

The licence is MIT OR Apache-2.0, the edition is 2024 with a rust-version of 1.95, and the workspace lints deny unsafe code outright. The last push to the repository was on 2026-08-31.

## Renaming the crate, in four places

GitHub's template mechanism creates a copy of the repository, but it does not rename anything, so the first job after clicking Use this template is to make the name yours. The README lists four files.

In Cargo.toml, the package.name changes from eframe_template to your_crate, and package.authors is updated. In main.rs, the reference to eframe_template::TemplateApp becomes your_crate::TemplateApp. In index.html, the title element changes, which the README marks as optional. In assets/sw.js, the strings for the JavaScript bundle and the WebAssembly module are changed inside the filesToCache array, so the cache points at the new build output rather than the old names.

That last one is the trap. A stale name in the service worker does not break the native build, and it may not obviously break the web build either, since the service worker is precisely the component that quietly falls back to a cached version.

If you cloned the repository outside GitHub, there is a script instead. fill_template.sh asks for the needed names and email and performs the patches, and the README describes it as particularly useful in that situation. There is a PowerShell version, fill_template.ps1, for the same reason.

The application itself lives in src/app.rs, and the README is careful about its role. It contains a simple example app, given as some inspiration, and most of it can be removed if you like.

## Running natively, and the system packages Linux needs

The native build is one command.

```bash
cargo run --release
```

Running in release mode rather than debug is unusual for a first run, and it is a deliberate choice for a template: the release profile is tuned, and the app is meant to be judged on how it feels rather than on how it compiles.

On Linux, the system packages have to be there first, and the README gives the exact list for Debian based systems.

```bash
sudo apt-get install libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libssl-dev
```

Fedora Rawhide needs a different set, with clang and the development headers, libxkbcommon-devel, pkg-config, openssl-devel, libxcb-devel, gtk3-devel, atk and fontconfig-devel.

This is the part of the template that most often stalls a new user, and it is worth knowing in advance that it is a system dependency problem rather than a Rust one. The list is the X client libraries that the windowing layer on Linux needs, plus the keyboard common library and the SSL development files.

The Rust side is otherwise unremarkable, which is the point of a template. The Cargo.toml also carries an override section for the patch.crates-io table, with commented examples for using a git checkout of egui and eframe on the main branch, or a local path if you have forked either crate.

## The web build: Trunk, the service worker and the dev flag

The web target is the part of the template that most distinguishes it from a plain cargo new project, and it uses Trunk as the bundler.

The setup is three commands. The target is added with rustup target add wasm32-unknown-unknown, Trunk is installed with cargo install --locked trunk, and the app is served with trunk serve on the local address 127.0.0.1 on port 8080. Trunk rebuilds automatically when you edit the project.

```bash
rustup target add wasm32-unknown-unknown
cargo install --locked trunk
trunk serve
```

Then you open the index.html with a fragment appended, at the address ending in index.html#dev.

That fragment is not decoration. The service worker script in assets/sw.js tries to cache the app and loads the cached version when it cannot reach the server, which is what allows the app to work offline like a PWA. Appending the dev fragment skips the caching so the latest build is loaded during development.

The two sets of dependencies in the Cargo.toml are the visible trace of the web target. Under a target-specific dependency on the wasm32 architecture there is wasm-bindgen-futures, and web-sys with a comment saying it is there to access the DOM, specifically to hide the loading text. Under the not wasm32 target there is env_logger instead, so the native build logs to the terminal.

## Deploying to GitHub Pages with the included workflow

The production web build is a single command, and the output is a plain static site.

```bash
trunk build --release
```

That produces a dist directory as a static HTML website, which you can upload to any of the numerous free hosting websites, GitHub Pages among them. The template already provides a workflow that auto-deploys the app to GitHub Pages if you enable it.

Enabling it takes two settings in the repository, and the README is specific about them. In Repository, then Settings, then Pages, the Source is set to the gh-pages branch and the root. If gh-pages does not appear in the Source list, you create and push a branch with that name and it becomes available.

There is one more case. If you renamed the main branch, for instance by re-initialising the repository with master as the initial branch, the workflow file at .github/workflows/pages.yml has to be edited to match, because the trigger is a push to a named branch.

```yml
on:
  push:
    branches:
      - <branch name>
```

The template application is published as a live demo at the emilk.github.io address for the template, which is worth opening before you start, since it shows the intended result rather than a description of it.

## The upgrade tax: egui moves fast and the template follows

The section on updating egui is where this template is honest about its own nature.

The README states that as of 2023, egui is in active development with frequent releases with breaking changes, and that the template will be updated in lock-step to always use the latest version of egui.

That is a good thing for a template and a cost for a project built on it. The lock-step policy means the template is never stale, so a new user starts on the current API rather than on the one from two years ago. It also means the template is a moving target under your feet, and the release history in the project shows the pattern, with tags named after egui versions, an egui-0.31 tag and two msrv tags for the minimum supported Rust version bumps.

The recommended upgrade procedure is one version at a time, reading the changes in the egui changelog and the eframe changelog. One version at a time is the advice that matters, since these are breaking changes and a jump across several releases can leave you with a pile of errors that are hard to attribute.

The rust-version of 1.95 in the manifest and the rust-toolchain file are part of the same picture. When the toolchain requirement moves, the template moves with it, and a project that pinned an older Rust will need to follow.

So the cost of adopting this template is a recurring one: a periodic upgrade with a changelog read attached. That is a reasonable trade for a team that wants a maintained starting point and an awkward one for a team that needs stability more than currency.

## The manifest details that are not obvious

A few things in the Cargo.toml are worth reading closely, because they are decisions rather than defaults.

The release profile sets opt-level to 2 with the comment fast and small wasm. That is the unusual part, because the default release profile optimises for speed at level 3, and the author has turned it down one notch to get smaller WebAssembly output. The dev profile goes further and optimises all dependencies even in debug builds, with a profile.dev.package entry for all dependencies, which is a deliberate trade of compile time for runtime speed in debug.

The eframe dependency requests the persistence feature, and the comment next to serde says that you only need serde if you want app persistence. That is a hint about how the example app is wired, and a note that the dependency can be dropped if you do not use it.

The lints section inherits from the workspace and denies unsafe code, which is a sensible default for a starting point and a constraint for anyone who later needs to drop into an unsafe API. The remaining lints warn on things like elided lifetimes in paths, future incompatible items and nonstandard style, so the template builds with warnings that would otherwise need silencing.

The package include list restricts what is published, with the two licence files, the Rust sources and the manifest, which matters if you ever publish your derived crate.

One more detail for the web path. The documentation metadata lists both the Linux target and wasm32-unknown-unknown with all features enabled, which is how the API documentation is built to show the web-only code paths as well as the native ones.

## Conclusion

Adopt this template if you are starting a Rust application with a graphical interface and want a project that already builds for the desktop and for the browser, because the web pipeline, the service worker and the Pages deployment workflow are the parts you would otherwise assemble yourself. Do not adopt it as a long lived base without accepting the upgrade cadence, since the template tracks egui in lock-step and egui releases with breaking changes. Before you build on it, run fill_template.sh and rename the crate in all four places, because a leftover eframe_template name breaks the web build rather than the native one.

## FAQ

### How do I start a Rust GUI application with eframe_template?

Click Use this template on the repository, or clone it and run fill_template.sh to have the names patched for you. Then rename the crate in Cargo.toml, main.rs, index.html and the filesToCache array in assets/sw.js, and run cargo run --release.

### What system packages does the native Linux build need?

On Debian based systems the README lists libxcb-render0-dev, libxcb-shape0-dev, libxcb-xfixes0-dev, libxkbcommon-dev and libssl-dev, installed with apt-get. Fedora Rawhide needs clang and its development headers, libxkbcommon-devel, pkg-config, openssl-devel, libxcb-devel, gtk3-devel, atk and fontconfig-devel.

### How do I build the app for the web?

Add the target with rustup target add wasm32-unknown-unknown, install Trunk with cargo install --locked trunk, and use trunk serve during development or trunk build --release to produce a dist directory of static files that you can upload to GitHub Pages.

### How often do I need to update egui?

The README states that egui is in active development with frequent releases with breaking changes, and that the template is updated in lock-step to use the latest version. It recommends updating one version at a time and reading the egui and eframe changelogs.

## Sources

- [emilk/eframe_template on GitHub](https://github.com/emilk/eframe_template)
- [Issues](https://github.com/emilk/eframe_template/issues)
- [License: Apache-2.0](https://github.com/emilk/eframe_template/blob/main/LICENSE)
- [README](https://github.com/emilk/eframe_template/blob/main/README.md)
- [Releases](https://github.com/emilk/eframe_template/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/emilk-eframe-template
