Open-source project
blitzarx1/egui_graphs avatar
blitzarx1/egui_graphs

egui_graphs gives an egui app an interactive graph widget

Interactive graph visualization widget for rust powered by egui and petgraph

708 stars76 forksRustMIT

At a glance

What is it?
egui_graphs is a Rust widget for the egui framework that renders a petgraph as an interactive graph, with layout selection, zoom and pan, node and edge interaction callbacks and custom stroke hooks. Its own status page says the project is not in active development and invites you to fork it, which is the single most important fact for anyone considering it.
Who is it for?
Adopt egui_graphs if you are building a Rust desktop or web application with egui and need a graph view you can extend, since the widget covers layout, interaction and rendering without locking you into a specific look.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 53 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A GraphView is a widget, and the graph is a petgraph

egui_graphs is graph visualization with rust and egui, and the project provides a `GraphView` for the egui framework, enabling easy visualization of interactive graphs in rust.

The stated goal is narrow: to implement the very basic engine for graph visualization within egui, which can be easily extended and customized for your needs. That framing is worth taking seriously, because it tells you what you are not getting, which is a finished product with a designed look. You are getting a rendering and interaction engine.

The data model is petgraph, and the example makes that plain. The struct holds a graph and nothing else:

rust
pub struct BasicApp {
    g: egui_graphs::Graph,
}

And the graph is built with petgraph's own API, adding nodes and edges with unit payloads:

rust
fn generate_graph() -> egui_graphs::Graph {
    let mut g = egui_graphs::Graph::new();

    let a = g.add_node(());
    let b = g.add_node(());
    let c = g.add_node(());

    g.add_edge(a, b, ());
    g.add_edge(b, c, ());
    g.add_edge(c, a, ());

    g
}

So the graph type is a re-export or alias of a petgraph type, and your node and edge data is whatever you want. The unit payloads in the example mean the widget is not drawing your data, it is drawing the structure, and how a node is labelled is a separate question that the widget answers through its display hooks.

The project is Rust, MIT licensed, with the last push on 2026-08-31 and releases v0.32.0 from 2026-08-31, v0.31.0 from 2026-06-29 and v0.30.0 from 2026-03-30, so it shipped regularly and then stopped.

The status line is the first thing to read

The README has a Status section, and the sentence in it is the most important line in the whole document.

The project is not in active development. Feel free to fork it and tweak for your needs.

That is a direct invitation to fork, and it changes the evaluation entirely. You are not adopting a maintained dependency with a bug fix pipeline behind it, you are adopting a well-built piece of code that you now own.

For a lot of uses that is exactly right. A graph widget inside a desktop tool you ship to your own team does not need a stream of upstream commits, it needs to work, be readable, and be extensible when your needs diverge. The stated extension intent matches that: the goal is to implement the very basic engine for graph visualization within egui, which can be easily extended and customized.

The release history tells the same story from the other side. Three releases in 2026, v0.30.0 in March, v0.31.0 in June and v0.32.0 at the end of August, and then a push in late August and nothing since. So the project was actively maintained and has recently stopped, rather than having never been maintained.

The one thing you give up is upstream fixes, and the fix for that is the fork the author suggests. The license is MIT, which means a fork carries no obligations beyond keeping the notice.

Layouts are selected by type parameter, which is unusual and effective

The layout system is the part of the API most likely to surprise you, because the layout is chosen with a type parameter rather than a value.

There are three built-in layouts. Random is a quick scatter for any graph and is the default via `DefaultGraphView`. Hierarchical is a layered, ranked layout. Force-directed is a Fruchterman-Reingold baseline with optional Extras such as Center Gravity.

The default is a one-liner:

rust
// Default random layout
egui_graphs::DefaultGraphView::new().show(ui, &mut graph);

And a specific layout needs two type parameters, the state type and the layout type:

rust
// Pick a specific layout (Hierarchical)
type L = egui_graphs::LayoutHierarchical;
type S = egui_graphs::LayoutStateHierarchical;
egui_graphs::GraphView::<S, L>::new().show(ui, &mut graph);

The reason for the type-level choice is that the layout carries state, and the state has to be persisted somewhere between frames. Making the layout a type parameter means the compiler picks the right state storage for you, and the `GraphView::<S, L>` form reads the state back out of the egui context.

That is visible in the force-directed quick start, where the state is fetched and set explicitly:

rust
use egui_graphs::{LayoutForceDirected, FruchtermanReingold, FruchtermanReingoldState};

type L = LayoutForceDirected<FruchtermanReingold>;
type S = FruchtermanReingoldState;
egui_graphs::GraphView::<S, L>::new().show(ui, &mut graph);

The `Layout` trait powers both layout selection and persistence, and the documentation says you can plug different algorithms or implement your own.

The force-directed layout is naive on purpose, and the extras are composable

The force-directed implementation is described honestly in the documentation, and the honesty is useful.

A naive O(n squared) force-directed layout, Fruchterman-Reingold style, is included. It exposes adjustable simulation parameters, step size, damping and similar, and the demo has a live tuning panel for them. Built-in options are the baseline Fruchterman-Reingold and an extended variant with composable extras.

The naive quadratic cost is the real limitation. For a graph of a few hundred nodes it is fine, and the live tuning panel makes the step size and damping adjustable at runtime, which is what makes a force layout usable at all in an interactive context. For a graph of thousands of nodes it will not be, and no amount of parameter tuning fixes that.

The extras mechanism is the extensible part. You use `FruchtermanReingoldWithExtras<E>` to apply the base FR forces plus your extras each frame, and the built-in extra is Center Gravity, which pulls nodes toward the centre of the canvas and stops a force layout from drifting off screen. The quick start shows the full sequence:

rust
use egui_graphs::{
    LayoutForceDirected,
    FruchtermanReingoldWithCenterGravity,
    FruchtermanReingoldWithCenterGravityState,
};

type L = LayoutForceDirected<FruchtermanReingoldWithCenterGravity>;
type S = FruchtermanReingoldWithCenterGravityState;
let mut state = egui_graphs::get_layout_state::<S>(ui, None);
state.base.is_running = true;
state.extras.0.params.c = 0.2;
egui_graphs::set_layout_state(ui, state, None);
egui_graphs::GraphView::<S, L>::new().show(ui, &mut graph);

The `state.extras.0.params.c = 0.2` line is worth noting. The extras are a tuple, so the centre gravity extra is the first element, and its parameters are reachable as a field.

Writing your own extra means implementing `ExtraForce` and composing it through `Extra<MyExtra, ENABLED>` in a tuple, and the documentation notes that extra-force parameters are persisted with the layout state, so they must implement `Serialize` and `Deserialize`. That is the practical constraint on writing a custom extra, and it exists because the state is saved between frames and between sessions.

Rendering is a hook, and the public geometry types let you avoid copying internals

The custom renderers section explains how you draw your own nodes and edges without reimplementing the widget.

Custom `DisplayNode` and `DisplayEdge` implementations receive a `DrawContext`. Inside those renderers you use `ctx.style.labels_always()`, `ctx.style.resolve_node_stroke(...)` and `ctx.style.resolve_edge_stroke(...)` to preserve the graph-wide label and stroke settings, which is the detail that keeps a custom renderer consistent with the rest of the widget.

The second paragraph is the more useful one for anyone who has tried this before. The public `EdgeShapeBuilder`, `EdgeShapeProps` and `TipProps` types can be used to construct the same straight, curved, looped and arrow-tipped edge geometry as the default renderer without copying its internals.

That is a deliberate API decision. The alternative, which many widget libraries force on you, is that a custom renderer either uses the built-in shape or re-implements the geometry from scratch. Exposing the shape builder means you can use the default arrowhead and the default loop and still customise the colour, the width or the label.

The feature list rounds out what the widget does. It builds wasm or native, with layouts and a custom layout mechanism, zooming and panning, node and edge interaction reporting for click, double click, select, hover and drag, node and edge labels, dark and light theme support via egui context styles, and user stroke styling hooks for nodes and edges for dynamic customisation.

The dark and light theme point is worth a sentence, because it is a common gap in egui widgets. Supporting it via the egui context styles rather than a hardcoded palette means the widget follows whatever theme the host application has set, which for a library embedded in someone else's app is the difference between looking native and looking pasted in.

The workspace is three crates with a strict lint floor

The Cargo.toml shows a workspace with three members, `crates/egui_graphs`, `crates/demo-core` and `crates/demo-web`, with resolver 2 and the workspace package version at 0.32.0 on edition 2021.

The split into a core crate and two demo crates is the right shape for a widget library, because the demos are the web demo and the native examples, and neither belongs in the published crate.

The lint configuration is the more revealing part. The workspace lints deny unsafe code outright with `unsafe_code = forbid`, and the clippy configuration denies pedantic, enum_glob_use, perf and style at the workspace level, with a list of individual allows for lints that do not fit a graphics library, including cast_precision_loss, cast_possible_truncation, cast_sign_loss, out_of_bounds_indexing, struct_excessive_bools and several others.

The pattern of a small allow list with reasons implied is a good sign about the code, and the fact that the allows are explicit rather than the whole linter being turned off is the difference between a project that takes the rules seriously and one that does not.

The dependency versions track the egui 0.36 line, with egui and egui_extras at 0.36 and default features disabled, eframe at 0.36, and petgraph at 0.8 with default features off. The rest are the supporting cast: serde and serde_json for the persisted layout state, rand for the random layout, web-time for the wasm target, ureq for HTTP, and criterion with html reports enabled for the benchmarks.

The web demo is hosted on the project pages and the documentation is on docs.rs, and the repository carries a .cargo/ directory for cargo configuration and a .github/ directory for the build workflow.

Editorial conclusion

Adopt egui_graphs if you are building a Rust desktop or web application with egui and need a graph view you can extend, since the widget covers layout, interaction and rendering without locking you into a specific look. Skip it if you need a maintained dependency, because the project states plainly that it is not in active development and tells you to fork it instead, and skip it if you need graph layout performance on large graphs, since the force-directed implementation is described as a naive quadratic one. Verify first which egui version you are on, since the crate tracks the 0.36 line, and that the layout type parameters and state aliases in the examples match the version you will actually depend on.

Frequently asked questions

Is egui_graphs still maintained?

The project states plainly that it is not in active development and that you are free to fork it and tweak it for your needs. The last push was on 2026-08-31 with release v0.32.0 the same day, after v0.31.0 in June and v0.30.0 in March 2026, so it was actively maintained and has recently stopped.

How do I use egui_graphs in an egui application?

Hold a `Graph` in your app struct, build it with the petgraph API, and show it inside a CentralPanel with `egui_graphs::DefaultGraphView::new().show(ui, &mut self.g)`. The full example implements the eframe::App trait and calls eframe::run_native with the app constructor.

How do I customise the node and edge rendering in egui_graphs?

Implement `DisplayNode` and `DisplayEdge`, which receive a `DrawContext`. Inside them you use `ctx.style.labels_always()`, `ctx.style.resolve_node_stroke(...)` and `ctx.style.resolve_edge_stroke(...)` to keep the graph-wide settings, and the public `EdgeShapeBuilder`, `EdgeShapeProps` and `TipProps` types let you build straight, curved, looped and arrow-tipped edges without copying the default renderer's internals.

Official sources

  1. blitzarx1/egui_graphs on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/blitzarx1-egui-graphs.svg)](https://hysenlabs.com/projects/blitzarx1-egui-graphs)

Was this useful?