Building WSL-UI: A Christmas Project with Tauri

Published: (January 17, 2026 at 09:39 PM EST)
4 min read
Source: Dev.to

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

TechnologyReason
React 19 + TypeScriptModern UI framework with static typing
ZustandSimple state management, fewer abstractions than Redux
Tailwind CSS 4Utility‑first styling, fast iteration
ViteLightning‑fast build toolchain

Backend

Crate / LibraryPurpose
Tauri 2.5Rust framework for the desktop app
wsl-core (custom)Parses WSL output
winregAccesses Windows Registry (needed for renaming)
reqwestHTTP 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.

Back to Blog

Related posts

Read more »