tauri-runtime-servo 0.1.0

Servo bindings to the Tauri runtime — an experimental Tauri runtime backed by the Servo web engine
docs.rs failed to build tauri-runtime-servo-0.1.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

tauri-runtime-servo

An experimental Tauri runtime backed by the Servo web engine, embedded in-process via libservo.

Instead of the system webview used by the default tauri-runtime-wry runtime (WebView2, WKWebView, WebKitGTK), this runtime renders your app with Servo — the same engine on every platform, statically linked into your binary.

⚠️ Status: experimental. None of the exposed API of this crate is stable, and it may break semver compatibility in the future. The major version only signifies the intended Tauri version.

Why a separate project?

This work started as a Servo backend inside wry (tauri-apps/wry#1797). The wry maintainers' direction is to keep wry focused on system webviews and host alternative engines as separate runtime crates at the Tauri layer instead — the same approach taken by tauri-runtime-cef and tauri-runtime-verso. A standalone repository also allows the fast-moving Servo dependency to be updated independently of Tauri's release process.

This project is the result: the Servo backend from that PR, restructured as a self-contained tauri-runtime implementation that works with published Tauri crates — no patched fork of tauri or wry required.

How it compares to tauri-runtime-verso

tauri-runtime-verso also brings Servo to Tauri, but it drives a separate versoview process. tauri-runtime-servo embeds libservo directly in your app's process, keeps Tao as the windowing layer (like tauri-runtime-wry), and needs no external binary to bundle.

Usage

# Cargo.toml
[build-dependencies]
tauri-build = "2"

[dependencies]
tauri = { version = "2", default-features = false, features = [
  "common-controls-v6",
] }
tauri-runtime-servo = "0.1"

The crate depends on stock libservo from crates.io. To build against the servo-patches/ series instead, see Using a patched Servo. To track unreleased changes, depend on the repository directly:

tauri-runtime-servo = { git = "https://github.com/copse-dev/tauri-runtime-servo" }

Note that the wry feature of tauri must stay disabled — this runtime replaces it.

// src/main.rs
type ServoRuntime = tauri_runtime_servo::Servo<tauri::EventLoopMessage>;

fn main() {
  tauri::Builder::<ServoRuntime>::new()
    // Servo cannot read custom protocol request bodies, so swap in an
    // invoke system that routes IPC through Servo's postMessage bridge
    .invoke_system(tauri_runtime_servo::INVOKE_SYSTEM_SCRIPT)
    .run(tauri::generate_context!())
    .expect("error while running tauri application");
}

See examples/helloworld for a complete app; run it with:

cargo run -p helloworld-servo

Building

Servo is compiled from source (pinned to a known-good revision in Cargo.toml), so the first build is large. The first build downloads a prebuilt SpiderMonkey archive; leave MOZJS_FROM_SOURCE unset unless you explicitly want mozjs_sys to compile SpiderMonkey locally.

On Linux you need Servo's build dependencies, e.g. on Debian/Ubuntu:

sudo apt-get install -y libdbus-1-dev libegl1-mesa-dev libfontconfig1-dev \
  libfreetype6-dev libgtk-3-dev libharfbuzz-dev libwebkit2gtk-4.1-dev \
  libx11-dev libxkbcommon-x11-dev lld
export RUSTFLAGS="-C link-arg=-fuse-ld=lld"

Using a patched Servo

This crate depends on stock libservo from crates.io, which is what makes it publishable there: crates.io accepts registry dependencies only, so an engine fork pinned by git revision cannot travel inside a release.

The servo-patches/ series — native SVG layout, contenteditable, the CSS :has() selector — is therefore opt-in. To build against it, override the engine crates in your own workspace root. [patch] is honoured only there, never from a dependency's manifest.

1. Check out the revisions behind the published crates

A [patch] entry is accepted only if the checkout's own version satisfies the requirement it replaces, so start from the exact trees the published crates were cut from. This crate requires servo = "0.5", which is servo 77fccacc (2026-08-04); that tree in turn wants stylo 0.20, which is stylo 67faaab3:

git clone https://github.com/servo/servo
git -C servo checkout -b tauri-runtime-patches 77fccacc1f1fdce10498d50173aafaa09d02879e

git clone https://github.com/servo/stylo
git -C stylo checkout -b tauri-runtime-patches 67faaab3ff7aa66780ec1d0f51ca47e177b812d3

git clone https://github.com/rust-ammonia/rust-content-security-policy

2. Apply the series

The servo and csp files are git format-patch output. The stylo files are plain diffs with a prose preamble, so git am rejects them — apply those with git apply:

git -C servo am ../tauri-runtime-servo/servo-patches/0*.patch
git -C rust-content-security-policy am ../tauri-runtime-servo/servo-patches/csp-*.patch

for p in ../tauri-runtime-servo/servo-patches/stylo-*.patch; do
  git -C stylo apply --3way "$p"
done

The series is authored against servo f4dde27 and stylo 2d289c1 (the 0.19 line), but applies cleanly to the revisions above — 24/24, 5/5 and 1/1 with no conflicts, verified against servo 77fccacc and stylo 67faaab3. Expect that to need rebasing once the pin moves further.

3. Add the overrides to your workspace root

Every entry goes under [patch.crates-io]: as of 0.5.0 servo takes its stylo crates from the registry too, so there is no git source left to override.

[patch.crates-io]
servo = { path = "../servo/components/servo" }
content-security-policy = { path = "../rust-content-security-policy" }

# All eight stylo entries are required. Overriding `stylo` alone leaves the
# others resolving from the registry, which puts a second copy of
# `stylo_traits` and friends in the graph and fails to compile.
selectors = { path = "../stylo/selectors" }
servo_arc = { path = "../stylo/servo_arc" }
stylo = { path = "../stylo/style" }
stylo_atoms = { path = "../stylo/stylo_atoms" }
stylo_dom = { path = "../stylo/stylo_dom" }
stylo_malloc_size_of = { path = "../stylo/malloc_size_of" }
stylo_static_prefs = { path = "../stylo/stylo_static_prefs" }
stylo_traits = { path = "../stylo/style_traits" }

stylo_derive, to_shmem, and to_shmem_derive need no entries — the patched crates reach them by path.

4. Enable the feature

[dependencies]
tauri-runtime-servo = { version = "0.1", features = ["patched-servo"] }

patched-servo sets preferences that exist only once the series is applied (layout_svg_native_enabled, added by patch 0009). Without all four steps the crate builds and runs against stock Servo.

When the pin moves

Whenever this crate's servo requirement changes, the checkout revisions above have to move with it, or the overrides stop resolving. The revision behind any published version is recorded in the crate itself:

curl -sL https://static.crates.io/crates/servo/servo-0.5.0.crate \
  | tar xzO servo-0.5.0/.cargo_vcs_info.json

Publishing

Releases go to crates.io from CI: push a v* tag and the publish.yml workflow verifies the packaged crate builds on Windows, Linux, and macOS, then publishes it.

Publishing uses trusted publishing: the workflow exchanges GitHub's OIDC token for a short-lived crates.io token, so no long-lived API secret is stored in this repository. One-time setup:

  1. Log in to crates.io, open your crate's settings (or the publish form before the first release) and add a trusted publishing rule for copse-dev/tauri-runtime-servo:
    • workflow name: publish.yml
    • environment name: crates
  2. Create the matching crates environment in the repository's GitHub settings (Settings → Environments → New environment).

For a new crate version:

# 1. Bump the version in Cargo.toml and commit.
# 2. Rehearse without publishing (runs verify-package only):
gh workflow run publish
# 3. Tag and push; CI does the rest.
git tag vX.Y.Z
git push origin vX.Y.Z

The first publish must be done by an owner of the crate name — after that, trusted publishing works for subsequent versions.

Platform support

Platform Supported
Windows
macOS
Linux (X11) ✅ (x11 feature, default)
Linux (Wayland) ❌ not yet
Android / iOS ❌ desktop only

What works

URL and HTML navigation, custom request headers and protocols, initialization scripts, IPC (via the postMessage bridge), navigation and page-load handlers, per-URL cookies, browsing data clearing, zoom, visibility, focus, background colors, HiDPI scaling, and composition into Tao-owned windows — validated against a large real-world Tauri UI with performance close to Electron.

Known limitations

  • Servo does not expose custom protocol request bodies, so the default Tauri invoke system must be replaced with INVOKE_SYSTEM_SCRIPT (see Usage above). The channel data fetch command still uses the custom protocol — its arguments travel in request headers.
  • The initialization-script main-frame-only option is not exposed by Servo's embedding APIs.
  • Printing, global cookie enumeration, and multiple Servo webviews in one native window are not supported yet.
  • In-process devtools window controls are not supported.
  • Engine gaps in Servo itself (at the pinned release) include contenteditable support and the CSS :has() selector. The servo-patches/ series fixes these and more; it is entirely opt-in — see Using a patched Servo. Without it, the crate builds and runs against stock Servo.

Repository layout

Features

  • x11 (default): X11 support on Linux.
  • dbus (default): dbus for theme support on Linux.
  • devtools: enables devtools in release builds (see limitations above).
  • macos-private-api: transparent windows etc. on macOS.
  • patched-servo: sets preferences that only exist once servo-patches/ is applied (native SVG layout); pair it with a [patch] override pointing at the patched servo checkout.
  • tracing: instrument with tracing.

License

Copyright 2019-2024 Tauri Programme within The Commons Conservancy.

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

Portions of this code are derived from wry and tauri (Apache-2.0 OR MIT).