Iced_webview
A library to embed Web views in iced applications
This library supports
- Blitz — Rust-native HTML/CSS renderer (Stylo + Taffy + Vello), modern CSS (flexbox, grid), no JS
- litehtml — lightweight CPU-based HTML/CSS rendering, no JS or navigation (good for static content like emails)
- Servo — full browser engine (HTML5, CSS3, JS via SpiderMonkey), rendered to CPU buffer, displayed via iced shader widget
- CEF — Chromium Embedded Framework via cef-rs, full Chromium browser compat (HTML5, CSS3, JS)
| Blitz | litehtml | Servo | CEF |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
Compatibility
| iced | iced_webview |
|---|---|
| 0.14 | 0.0.9+ |
| 0.13 | 0.0.5 |
Usage
Add to your Cargo.toml (the library pulls in iced internally, but your app will need it too):
[]
= "0.1"
= { = "0.14", = ["advanced", "image", "tokio", "lazy"] }
The default engine is litehtml. To use a different one, disable defaults and pick one:
= { = "0.1", = false, = ["blitz"] } # or "servo", "cef"
Minimal example
use ;
use ;
use Duration;
type Engine = Litehtml; // or Blitz, Servo, Cef
The periodic Action::Update subscription is required — it drives rendering, image fetching, and engine state. Use PageType::Url to load a URL, or PageType::Html to render a raw HTML string. Track navigation with on_url_change / on_title_change.
Basic vs Advanced WebView
Basic (iced_webview::WebView) manages views with simple u32 indexing — create with Action::CreateView, switch with Action::ChangeView(index), render with webview.view(). Good for most use cases.
Advanced (iced_webview::advanced::WebView) gives you explicit ViewId control. Every action and callback includes the ViewId, and you render a specific view with webview.view(id). Use this for multi-view scenarios like a tabbed browser.
Rendering paths
Handled transparently — webview.view() returns the right widget type based on the engine feature — but worth knowing about:
- Image Handle (Blitz, litehtml) — the engine rasterizes to a CPU pixel buffer, displayed via iced's
image::Handle. Simple, works everywhere. - Shader widget (Servo, CEF) — uses iced's
shaderwidget with a persistent GPU texture updated in-place viaqueue.write_texture(). Avoids texture cache churn and flickering during rapid updates like scrolling.
Requirements
- Rust 1.90+ (Blitz crates from git use edition 2024, declared MSRV 1.90)
- litehtml requires
clang/libclangfor buildinglitehtml-sys - Servo requires
fontconfig,make,cmake,clang(recent version), andnasmat build time - CEF downloads the Chromium Embedded Framework binary (~200-300 MB) at build time; requires subprocess handling (see below)
- CEF on Guix: use
manifest-cef.scmwith FHS emulation:
Default engine
The default feature is litehtml — it's lightweight, pure-crate.io, and compiles fast. Blitz and Servo are git-only deps and can't be published to crates.io, so they require --features blitz or --features servo explicitly.
examples:
examples/webview
Minimal example — just the web view, nothing else
# or with blitz
# or with servo
# or with cef
examples/embedded_webview
A simple example to showcase an embedded webview (uses the basic webview)
# or with litehtml
# or with servo
# or with cef
examples/email
Renders a table-based marketing email — works with any engine, but designed to showcase litehtml's table layout
# or with blitz
# or with servo
# or with cef
Known Issues
Blitz and litehtml are not full browsers — there's no JavaScript, and rendering is CPU-based. Both are best suited for displaying static or semi-static HTML content. Servo and CEF are full browser engines with JS support but add significant binary size.
Blitz
- No incremental rendering — the entire visible viewport is re-rasterized on every frame that needs updating (scroll, resize, resource load). Blitz is pre-alpha and doesn't yet support dirty-rect or partial repaint like Firefox/Chrome.
- No
:hoverCSS rendering — hover state is tracked internally (cursor changes work), but we skip the visual re-render for:hoverstyles to avoid the CPU cost. This matches litehtml's behaviour. - Keyboard input — iced keyboard events are wired through to blitz-dom (text input, Tab navigation, arrow keys, copy/paste). Dark mode is detected from
ICED_WEBVIEW_COLOR_SCHEMEenv var or GTK theme. - No JavaScript — by design; Blitz is a CSS rendering engine, not a browser engine.
- Image/CSS fetching is internal — Blitz uses
blitz_net::Providerto fetch sub-resources (images, CSS@import) automatically. It does not participate in the widget layer's manual image pipeline (take_pending_images/load_image_from_bytes). The widget layer fetches the initial HTML page for URL navigation, but all sub-resource loading is handled by Blitz internally. - Build weight — Stylo (Firefox's CSS engine) adds significant compile time on first build.
litehtml
- Limited CSS support — basic flexbox, no grid, no CSS variables. Works well for table-based layouts and simple pages (emails, documentation).
- No
:hoverCSS rendering — cursor changes work, but hover styles are not visually applied. - No JavaScript or navigation history — static rendering only.
- C++ dependency — requires
clang/libclangfor buildinglitehtml-sys.
Servo
- Git-only dependency —
libservois not on crates.io, so theservofeature cannot be published. Build from git only. - Large binary — adds 50-150+ MB to the final binary due to SpiderMonkey and Servo's full rendering pipeline.
- System deps — needs
fontconfig,make,cmake,clang(recent version), andnasmat build time. - Text selection — Servo manages text selection and clipboard (Ctrl+C/V) internally, but the selection is not queryable from the embedding API (
get_selected_text()returns None). - Intermittent SpiderMonkey crashes — servo's JS engine can segfault during script execution on certain pages (
JS::GetScriptPrivate). This is an upstream servo/SpiderMonkey issue, not specific to the embedding. Pages with heavy JS are more likely to trigger it. - Rendering — Servo software-renders to a CPU buffer, which is then uploaded to a persistent GPU texture via
queue.write_texture()and displayed through iced'sshaderwidget. The texture is only updated when Servo signals a new frame. This avoids the texture cache churn (and visible flickering) that would otherwise occur with iced's image Handle path during rapid frame updates like scrolling.
CEF
- Multi-process mode — CEF runs with standard multi-process architecture (renderer, GPU, utility subprocesses). On non-FHS systems (Guix, Nix), use an FHS-emulated container (
guix shell --container --emulate-fhs) so subprocesses can find.pakresources,icudtl.dat, and shared libraries at standard paths. Callcef_subprocess_check()at the top ofmain(). - Large runtime — ships ~200-300 MB of Chromium binaries alongside your application.
- Not Rust-native — C++ under the hood, Rust bindings via cef-rs.
- CEF binary download — the
cef-dll-sysbuild script downloads the CEF binary distribution at build time. - Rendering — same as Servo: CPU buffer uploaded to a persistent GPU texture via
queue.write_texture(), displayed through iced'sshaderwidget. Only updated when CEF delivers a new frame via itson_paintcallback.
TODO
- Blitz incremental layout —
blitz-domhas a feature-gatedincrementalflag that enables selective cache clearing and damage propagation inresolve(). Currently experimental (incomplete FC root detection, no tests), but once stabilized it would make re-layout after hover/resource loads much cheaper by only updating affected subtrees instead of the full tree. :hoverCSS rendering — both engines skip the visual re-render for hover styles. With incremental layout + viewport-only rendering, it may become cheap enough to re-enable for Blitz.- Async rendering — rendering currently blocks the main thread. Moving the
paint_scene+render_to_buffercall to a background thread would keep the UI responsive during re-renders. - Servo/CEF text selection API — expose the engine-managed selected text through
get_selected_text()so the embedding can query it.
Engine Comparison
| Feature | Blitz | litehtml | Servo | CEF |
|---|---|---|---|---|
| CSS flexbox / grid | Yes (Firefox's Stylo engine) | Flexbox only (no grid) | Yes | Yes |
| CSS variables | Yes | No | Yes | Yes |
| Table layout | Yes | Yes | Yes | Yes |
| JavaScript | No | No | Yes (SpiderMonkey) | Yes (V8) |
| Keyboard input | Yes (wired to blitz-dom) | No | Yes | Yes |
| Text selection | Supported in blitz-dom, not yet wired | Yes | Yes (engine-managed, not queryable from API) | Yes (Chromium-managed, not queryable from API) |
:hover CSS styles |
Tracked, not rendered (CPU cost) | Tracked, not rendered | Yes | Yes |
| Cursor changes | Yes | Yes | Yes | Yes |
| Link navigation | Yes | Yes | Yes | Yes |
| Image loading | Yes (blitz-net, automatic) | Yes (manual fetch pipeline) | Yes (built-in) | Yes (built-in) |
CSS @import |
Yes (blitz-net) | Yes (recursive fetch + cache) | Yes (built-in) | Yes (built-in) |
| Scrolling | Yes | Yes | Yes (engine-managed, cursor-targeted) | Yes (engine-managed) |
| Rendering path | iced image Handle | iced image Handle | iced shader widget (direct GPU texture) | iced shader widget (direct GPU texture) |
| Incremental rendering | No (experimental flag exists) | No | Yes | Yes |
| Navigation history | No | No | Yes | Yes |
| Build deps | Pure Rust | C++ (clang/libclang) |
Rust + system deps (git-only) | C++ (CEF binary download) |
| Rendering performance | Low (Stylo + Vello CPU, needs --release) |
Moderate | Best (full rendering pipeline) | Best (full Chromium pipeline) |
| Binary size impact | Moderate | Small | Large (50-150+ MB) | Large (~200-300 MB runtime) |
| License | MIT/Apache-2.0 + MPL-2.0 (Stylo) | MIT + BSD-3-Clause | MPL-2.0 | Apache-2.0 (this crate) + BSD (CEF) |
Original developer: LegitCamper/iced_webview (Sawyer Bristol and others)



