Building WSL-UI: A Christmas Project with Tauri
Source: Dev.to
Introduction
I had some time over Christmas and wanted to try something new. My day job is DevOps—pipelines, infrastructure, the usual—but I was itching to build a proper desktop app: a visual manager for WSL2 distributions on Windows.
If you’ve used WSL2 for any length of time, you know the pain of juggling several distributions (Ubuntu, Debian, Alpine, etc.) and remembering command‑line incantations:
wsl --list --verbose
wsl --terminate Ubuntu
wsl --export Ubuntu ./ubuntu-backup.tar
wsl --import NewDistro C:\WSL\NewDistro ./some-rootfs.tar
It works, but a visual tool that shows what’s running, memory usage, and lets you perform common tasks with a click would be far nicer.
Why Tauri?
When it comes to cross‑platform desktop apps with web technologies, Electron is the obvious choice, but it’s known for bloat—each app ships with its own Chromium and Node.js runtime, so a simple “Hello World” can be ~150 MB.
Tauri takes a different approach:
- Native WebView – Uses the OS’s WebView (WebView2 on Windows), shared across apps.
- Rust backend – Compiles to a small native binary.
- Size – A Tauri app can be under 10 MB; my portable executable is ~4 MB.
Other reasons:
- Learning Rust – I wanted to learn the language properly.
- Native Windows integration – Easy calls to Windows APIs and shell commands.
- Security model – Frontend can only invoke explicitly allowed backend functions.
Tech Stack
Frontend
| Technology | Reason |
|---|---|
| React 19 + TypeScript | Modern UI framework with static typing |
| Zustand | Simple state management, fewer abstractions than Redux |
| Tailwind CSS 4 | Utility‑first styling, fast iteration |
| Vite | Lightning‑fast build toolchain |
Backend
| Crate / Library | Purpose |
|---|---|
| Tauri 2.5 | Rust framework for the desktop app |
wsl-core (custom) | Parses WSL output |
winreg | Accesses Windows Registry (needed for renaming) |
reqwest | HTTP client for OCI registry pulls |
Testing
- Vitest – Unit tests for the frontend.
- WebdriverIO with Tauri Driver – End‑to‑end tests.
First Implementation: Listing Distributions
The initial version simply listed distributions and showed their state. Tauri’s command system made this straightforward.
Rust side
#[tauri::command]
pub async fn list_distributions() -> Result, String> {
// Run `wsl --list --verbose` and parse the output
let output = std::process::Command::new("wsl")
.args(["--list", "--verbose"])
.output()
.map_err(|e| e.to_string())?;
// Parse and return
parse_wsl_list(&output.stdout)
}
React side
import { invoke } from '@tauri-apps/api/core';
import { useState, useEffect } from 'react';
interface Distribution {
name: string;
state: string;
version: number;
// …other fields
}
export function useDistributions() {
const [distributions, setDistributions] = useState([]);
useEffect(() => {
async function loadDistributions() {
const distros = await invoke('list_distributions');
setDistributions(distros);
}
loadDistributions();
}, []);
return distributions;
}
No complex IPC setup or serialization boilerplate was required—Tauri handles the bridge between JavaScript and Rust.
Feature Growth
After the list view, I added:
- Resource monitoring – Memory usage, CPU percentage, disk sizes.
- Distribution management – Start, stop, terminate, set default.
- Import/Export – Backup and restore distributions as tar files.
- Container imports – Pull OCI images directly from registries.
- Renaming – Change distribution names (involves Windows Registry surgery).
- Custom actions – User‑defined shell commands per distribution.
Rust Challenges
Rust’s borrow checker caught me out repeatedly, but once the code compiled it usually worked correctly. Bugs that plagued other languages—null pointers, race conditions, use‑after‑free—simply didn’t happen.
During development, Tauri 2.0 was released, prompting a migration from 1.x to 2.x. The process was mostly straightforward, though several APIs changed and the plugin system was overhauled.
Frontend Reflections
React is React, and Tailwind makes styling fast. Zustand felt like a breath of fresh air after years of Redux—just create a store with state and functions, no actions/reducers/middleware ceremony.
Series Overview
This is the first post in a series about building WSL‑UI. Upcoming posts will cover:
- Mock Mode – Building a fake WSL environment for testing and development.
- Registry Surgery – How renaming distributions works under the hood.
- OCI Without Docker – Pulling container images directly from registries.
- Microsoft Store Publishing – From Tauri to MSIX to Store listing.
- E2E Testing – Screenshot generation and video recording with WebdriverIO.
- Polish and Analytics – UI development pains from a backend perspective, and privacy‑first analytics.
Availability
WSL‑UI is open source and available on:
- Microsoft Store – Search for “WSL UI” or visit the store listing.
- Official Website – [Website Download]
- GitHub –
If you’re curious about Tauri or want to see how the pieces fit together, the code is all there, reasonably documented, and ready for exploration.
Next Up
How I built a complete mock mode that lets me develop and test without touching real WSL distributions.