Raw speed, refined desktop.
OxideDock is a Rust + Vue 3 desktop starter built on Tauri v2 — native performance, polished DX, ship-ready apps.
Starting a Tauri app from scratch means wiring up routing, state management, testing, linting, CI/CD, and release pipelines yourself. OxideDock gives you all of that out of the box so you can skip the boilerplate and start building your app from day one.
- Production-ready defaults — linting, formatting, testing, and CI all preconfigured
- Cross-platform releases — push a tag and get Linux, macOS, and Windows binaries
- One command —
make devand you're running
- Tauri v2 — lightweight, secure desktop runtime
- Vue 3 — reactive frontend with Composition API
- Vite — fast dev server and build tool
- TypeScript — type-safe frontend and configuration
- Guarded IPC — a test fails the build if Rust commands and the TypeScript layer drift apart
- Tailwind CSS v4 — utility-first styling with Vite plugin
- Vue Router — client-side routing
- Pinia — type-safe state management
- VueUse — essential Vue composables
- Vitest — fast unit and component testing
- Playwright — e2e testing against the web frontend
- ESLint — flat config with Vue 3 rules
- Biome — fast formatting and linting
- Clippy + Rustfmt — Rust linting and formatting
- cargo-audit — Rust dependency security auditing
- Lefthook — fast, parallel pre-commit hooks
- GitHub Actions — CI (lint + test + build) and release pipelines
- Makefile — unified task runner for all operations
- Rust (latest stable)
- Bun (v1.0+)
- Tauri v2 system dependencies for your OS
# Clone the template
git clone https://github.com/fridzema/oxide-dock.git
cd oxide-dock
# Verify prerequisites (Rust, Bun)
make check
# Install dependencies
make setup
# (Optional) Rename the project to your own app
make bootstrap
# Start development
make devoxidedock/
├── .github/workflows/ # CI and release pipelines
├── .vscode/ # Editor settings and extensions
├── src/ # Vue 3 frontend
│ ├── components/ # Reusable components
│ ├── composables/ # Shared composables
│ ├── layouts/ # Layout components
│ ├── pages/ # Route views
│ ├── router/ # Vue Router configuration
│ ├── stores/ # Pinia stores
│ ├── App.vue # Root component
│ ├── main.ts # App entry point
│ └── style.css # Tailwind CSS imports
├── src-tauri/ # Rust backend
│ ├── src/ # Rust source code
│ ├── capabilities/ # Permission capabilities
│ ├── Cargo.toml # Rust dependencies
│ └── tauri.conf.json # Tauri configuration
├── tests/
│ ├── unit/ # Vitest unit tests
│ └── e2e/ # Playwright e2e tests
├── Makefile # Task runner
├── lefthook.yml # Git hooks
└── README.md
| Command | Description |
|---|---|
make dev |
Start Tauri dev with hot reload |
make dev-frontend |
Start Vite dev server only |
make build |
Build production binary |
make build-debug |
Build with debug console |
| Command | Description |
|---|---|
make lint |
Run all linters (ESLint + Biome + Clippy) |
make lint-fix |
Auto-fix linting issues |
make format |
Format all files |
make format-check |
Check formatting |
| Command | Description |
|---|---|
make test |
Run all tests |
make test-unit |
Run Vitest unit tests |
make test-e2e |
Run Playwright e2e tests |
make test-watch |
Run Vitest in watch mode |
| Command | Description |
|---|---|
make rust-lint |
Run Clippy |
make rust-format |
Run Rustfmt |
make rust-audit |
Check for vulnerabilities |
make rust-test |
Run Rust tests |
| Command | Description |
|---|---|
make check |
Verify prerequisites are installed |
make setup |
Install all dependencies |
make bootstrap |
Rename project to your own app |
make ci |
Run full CI pipeline locally |
make coverage |
Run all coverage (Rust + Vue) |
make clean |
Remove build artifacts |
| Technology | Version | Purpose |
|---|---|---|
| Tauri | v2 | Desktop runtime |
| Vue | v3 | Frontend framework |
| Vite | v8 | Build tool |
| TypeScript | v6 | Type safety |
| Tailwind CSS | v4 | Styling |
| Vue Router | v5 | Routing |
| Pinia | v4 | State management |
| VueUse | latest | Composable utilities |
| Vitest | latest | Unit testing |
| Playwright | latest | E2e testing |
| ESLint | v10 | Vue linting |
| Biome | v2 | Formatting and linting |
| Lefthook | latest | Git hooks |
Commands cross the Rust/TypeScript boundary through src/shared/ipc.ts: every command gets a typed wrapper, and Rust's structured AppError gets a matching TypeScript type.
Three places have to agree for a command to work: it must be defined with #[tauri::command] in src-tauri/src/handlers.rs, registered in tauri::generate_handler! in src-tauri/src/lib.rs, and declared in CommandResults in src/shared/ipc.ts. Miss one and the failure shows up at runtime, usually as a confusing "command not found".
tests/unit/ipc-surface.test.ts reads all three and fails the build if they disagree, so that class of bug cannot reach a release.
This checks the command surface — that every command exists in all three places under the same name. It does not verify argument or payload field types; those still rely on the hand-written types in src/shared/ipc.ts matching the Rust structs.
Optional features are opt-in, not preinstalled. Each one is a recipe — an idempotent script that wires the feature into your checkout: dependency, plugin registration, capability, config.
make add-window-state # remember window size and position across restarts
make add-tray # system tray icon with a menu
make add-updater # in-app auto-updates (needs signing keys)Each recipe prints what it changed and points at its own README under recipes/. Recipes modify
your working tree and are meant to be run once and committed — review the diff like any other
change. git checkout -- is the undo.
recipes.yml applies every recipe to a clean checkout on every push and pull request, then runs
ESLint, Biome, the TypeScript type check, the Vitest suite, cargo clippy -- -D warnings and
cargo test against the result. It applies the recipe a second time and fails if that produces any
diff, so a recipe that stops being idempotent cannot land.
Pull requests deliberately skip the app build to keep the feedback loop short. recipes-nightly.yml
covers it: once a day it runs the full make ci — including bun tauri build — for every recipe on
Linux.
Runs on every push to main and on pull requests:
- Lint — ESLint, Biome check, TypeScript type check
- Rust Lint — Clippy + Rustfmt (Linux only)
- Test — Vitest unit tests, Playwright e2e (chromium), Rust tests on all platforms with coverage on Linux
- Audit —
cargo auditruns daily and on Cargo dependency changes (separate workflow) - Build — Linux smoke build (main branch only); full cross-platform build happens at release time
Releases are fully automated via release-please:
- Push conventional commits to
main(fix:,feat:,feat!:) - Release-please opens a PR bumping versions and updating
CHANGELOG.md - Merging the PR tags the release, triggering cross-platform builds
- A draft GitHub Release is created with platform binaries:
- Linux:
.deb,.AppImage - macOS:
.dmg(Intel + Apple Silicon) - Windows:
.msi,.exe
- Linux:
Check release status with make release-status.
- Update
nameinpackage.json - Update
productNameinsrc-tauri/tauri.conf.json - Replace icons in
src-tauri/icons/(usebun tauri icon /path/to/icon.png)
# Shadcn Vue
bunx shadcn-vue@latest init
# Or PrimeVue
bun add primevuebun add vue-i18nCreate locale files in src/locales/ and register the plugin in src/main.ts.
Follow the Tauri code signing guide to set up signing for macOS and Windows distribution.
Apps shipped on top of OxideDock:
- SoapDuck — a native macOS SOAP client for developers: load WSDLs, build and send SOAP requests, and inspect responses, with WS-Security and certificate signing.
Built something with OxideDock? Open a PR to add it here.
- Fork the repository
- Create a feature branch (
git checkout -b feature/your-feature) - Make your changes
- Run
make cito verify everything passes - Commit with a descriptive message
- Push and open a pull request
