A native desktop engine for Svelte
Svelte is the language. Rust is the kernel. Your app owns every pixel.
Early Development — RVST is an active research project, not production-ready software. APIs will change. Features are incomplete. If you're building something that needs to ship today, use Tauri or Electron. If you want to explore what native Svelte rendering could look like, read on.
Quick Start |
How It Works |
Native APIs |
Templates |
Configuration
What is RVST?
RVST is a new execution target for Svelte. Write components with Svelte 5, style with Tailwind or scoped CSS, and ship a native desktop app. No Electron. No webview. RVST replaces the browser engine entirely with a purpose-built Rust rendering stack.
Your Svelte code compiles to JavaScript as usual. RVST executes it in an embedded QuickJS runtime, maps the component tree to a Rust layout engine (Taffy), renders with a GPU vector graphics engine (Vello), and displays in a native window (winit). The result is a desktop app that starts instantly, uses minimal memory, and renders at native quality.
What you get:
- Svelte 5 with runes, reactivity, scoped styles, component composition
- Tailwind v4 with full utility class support and design tokens
- CSS custom properties, @media queries, !important, :not(), :nth-child
- Custom window chrome (traffic lights, Windows controls, or your own)
- Native file system access from Svelte components
- GPU-accelerated rendering with Vello
- Icon fonts (Phosphor, Material Symbols, etc.)
- Headless rendering and scene graph queries (RenderQuery)
Quick Start
Install
npm install -g @rvst/cli
This installs the rvst command globally. It downloads the correct Rust binary for your platform automatically.
Create a new app
rvst create my-app
cd my-app
npm install
Write your first component
<!-- src/App.svelte -->
<script>
let count = $state(0);
</script>
<div class="app">
<h1>Hello from RVST</h1>
<button onclick={() => count++}>
Clicked {count} times
</button>
</div>
<style>
.app {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
gap: 16px;
font-family: system-ui;
}
button {
padding: 8px 16px;
border-radius: 6px;
background: #3b82f6;
color: white;
border: none;
font-size: 14px;
cursor: pointer;
}
</style>
Entry point
// src/entry.js
import { mount } from 'svelte';
import App from './App.svelte';
export function rvst_mount(target) {
return mount(App, { target });
}
export default rvst_mount;
Vite config
// vite.config.js
import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';
import { rvstPlugin } from '@rvst/vite-plugin';
export default defineConfig({
plugins: [rvstPlugin(), svelte()],
build: {
outDir: 'dist',
target: 'esnext',
lib: {
entry: 'src/entry.js',
formats: ['es'],
fileName: 'app',
},
},
});
Build and run
rvst build
rvst run
Or with watch mode for development:
rvst dev
How It Works
Svelte Component (.svelte)
|
v
Vite + vite-plugin-rvst
(compiles to JS bundle)
|
v
QuickJS Runtime (executes JS)
|
v
DOM Stubs (translate DOM ops to Rust ops)
|
v
rvst-tree (DOM-like tree in Rust)
|
v
lightningcss (CSS parsing + cascade + var() resolution)
|
v
Taffy (CSS Flexbox/Grid layout)
|
v
Vello + wgpu (GPU vector rendering)
|
v
winit (native window)
RVST intercepts Svelte's compiled DOM operations at the lowest level. When Svelte calls createElement, setAttribute, or insertBefore, these become Rust ops that build a tree. CSS is parsed by lightningcss with full cascade, specificity, custom property inheritance, and @media query evaluation. Taffy computes layout. Vello renders to the GPU.
The entire pipeline runs on the main thread with sub-millisecond frame times for typical UIs.
Native APIs
RVST exposes native platform capabilities to Svelte via globalThis.__rvst:
Window Management
<script>
const rvst = globalThis.__rvst;
// Custom titlebar (remove OS chrome)
$effect(() => rvst?.disableDecorations());
</script>
<div onmousedown={() => rvst?.startDragging()}>
<!-- custom titlebar content -->
<button onclick={() => rvst?.minimize()}>-</button>
<button onclick={() => rvst?.maximize()}>+</button>
<button onclick={() => rvst?.close()}>x</button>
</div>
File System