Skip to content
fahimaloyPublic

About

Rust framework/browser-engine for building reactive UIs with .vx single-file components, Skia rendering and HMR

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

270 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Velox logo

Velox

A modern UI framework for Rust

Reactive signals · Single-file components · Flexbox layout · Skia rendering

Velox Layout Showcase — a .vx single-file component laid out and painted by velox-renderer

Crates.io Downloads Docs.rs Documentation CI License: MIT OR Apache-2.0


Quick start · Documentation · Tutorials · CLI reference · Architecture · Template syntax · Feature status · Guides


Quick start

Prerequisites

Requirement Why Check
Rust 1.85+ the workspace crates use edition 2024 rustc --version
Git scaffolds fall back to git dependencies git --version
C toolchain linker and build tools for native dependencies see below
Platform-specific build tools
# Linux (Debian/Ubuntu)
sudo apt-get install build-essential pkg-config libssl-dev

# macOS
xcode-select --install

# Windows
# Microsoft Visual C++ Build Tools: https://visualstudio.microsoft.com/visual-cpp-build-tools/

Install

cargo install veloxc

That puts a single binary, veloxc, on your PATH. Every command on this page is veloxc <subcommand> — veloxc --help lists them, and veloxc <subcommand> --help is the authoritative flag reference.

veloxc version        # prints the CLI version and your platform

Tip

Working from a checkout? Install the main branch directly instead of waiting for a crates.io release:

cargo install --path veloxc --force

Your first project

# 1. Scaffold
veloxc init my-first-app
cd my-first-app

# 2. Watch, build, run — reload with `r`, quit with `q`
veloxc dev

# 3. One-shot build
veloxc build
veloxc build --release

# 4. Build and launch once
veloxc run

veloxc dev watches the project, rebuilds on save, and re-launches the app — hot reload means the running window updates as soon as the build lands:

Counter example hot-reloading a style change

A quick veloxc lint src/App.vx checks the generated component before you edit anything.

Commands run from the current working directory, not from the Velox source tree.

Important

Where the scaffold points at Velox. veloxc init wires your Cargo.toml to the workspace crates in one of three ways:

How you got the CLI Dependencies written into your project
cargo install --path veloxc (from a clone) relative path = … entries into that clone
veloxc init --local <path> the same, pinned to the checkout you name
cargo install veloxc (crates.io) git = "https://github.com/fahimaloy/velox" pinned to the commit the binary was built from

A crates.io-installed veloxc has no git metadata; generate_cargo_toml detects rev == "unknown" (the VELOX_GIT_REV_FALLBACK sentinel) and emits plain version dependencies, so the scaffold is always resolvable. See CLI Reference.

What veloxc init generates

my-first-app/
├── Cargo.toml            dependencies wired to Velox
├── build.rs              compiles .vx files during `cargo build`
├── README.md
├── assets/
│   ├── velox-logo.svg
│   └── velox-logo.png
└── src/
    ├── App.vx            entry component
    ├── main.rs           Rust entry point
    └── components/
        ├── Confirm.vx
        ├── Modal.vx
        ├── TodoInput.vx
        ├── TodoItem.vx
        └── Todos.vx

CLI reference

Every subcommand veloxc exposes. Flags below are transcribed from veloxc <command> --help; nothing here is inferred.

Command What it does
veloxc init Scaffold a new project
veloxc build Build the project, or compile one .vx file to Rust
veloxc run Build and run once
veloxc dev Watch, rebuild, restart
veloxc lint Check .vx files
veloxc add component Generate a component scaffold
veloxc version Version and platform info

veloxc init

veloxc init <name> [options]
Flag Default Description
-t, --template <name> default Template variant
--local <path> — Wire the scaffold to a local Velox checkout

<name> is a package name or a path — myapp, /tmp/myapp, ../projects/myapp. Dots and whitespace normalize to -; the name must start with a letter or _.

veloxc init myapp                   # in the current directory
veloxc init /tmp/myapp              # absolute path
veloxc init ../projects/myapp       # relative path

veloxc build

veloxc build [--release]              # the current project
veloxc build <file.vx> [-o <dir>]     # one component tree
Flag Applies to Effect
--release project build optimized binary in target/release/
-o, --out-dir <dir> single-file mode where generated .rs files land (default target/velox-gen)

Note

Single-file mode recurses through every component the input imports and emits Rust source for inspection. It is not an executable.

veloxc run

veloxc run [--release]

Build and launch in one step — equivalent to cargo build && cargo run, with --release passed through.

veloxc dev

veloxc dev [options]
Flag Default Description
-w, --watch <path> . Project root to build and watch
--release — Build in release mode

Watching is event-driven — notify uses inotify on Linux rather than a polling interval, so a save reaches the build loop the moment it lands. target/, .git/, .vscode/, .idea/ and dot-directories are excluded from the watch.

file change (inotify)
        ↓
50 ms debounce
        ↓
cargo build          ← full rebuild
        ↓
app process stopped and re-spawned

The dev server also opens an HMR control channel on 127.0.0.1:31313 and sends the app a FullReload message — the app exits and is restarted. Module-level HotReload exists in the protocol but is not implemented.

Type a command and press Enter:

Key Action
r rebuild
c clear
q quit

Warning

veloxc dev is a watch-and-rebuild loop, not hot reload. In-process state does not survive a restart — treat the rebuild time as the cost of every save.

Raising the inotify watch limit (Linux)

Watching is backed by inotify, a finite kernel resource. If the limit is exhausted the dev server reports the error and keeps running, but files may stop being detected — press r to rebuild by hand.

sudo sysctl -w fs.inotify.max_user_watches=524288
sudo sysctl -w fs.inotify.max_user_instances=1024

To persist, write those two lines to a file under /etc/sysctl.d/ and run sudo sysctl --system.

veloxc lint

veloxc lint [target] [--fix]
Target Effect
(none) every .vx file under src/
src/App.vx a single file
components/ every .vx under a directory
--fix also fix trailing whitespace and a missing final newline

Reports parse errors, reactive-idiom advisories in <script>, and every CSS property that parses but paints nothing (see Feature status).

veloxc add component

veloxc add component <name> [-s, --slots <NAME,NAME>]
Flag Description
-s, --slots <NAME,NAME> Named slots to declare, comma-separated

Generates src/components/<Name>.vx — <name> folds to PascalCase (side-bar → SideBar). The implicit default slot is always created; --slots header,footer adds named ones. The command refuses to overwrite an existing file and must be run inside a project (it walks up looking for src/App.vx).

veloxc add component button
veloxc add component card --slots header,footer

veloxc version

veloxc version

Prints the CLI version, the toolchain edition and your platform.


Architecture

Velox is a workspace of six published crates plus three example apps. Every crate below is on crates.io; use any combination of them.

graph TD
    subgraph tool["Tooling"]
        cli["veloxc — CLI and dev server"]
    end

    subgraph libs["Published libraries"]
        sfc["velox-sfc — .vx compiler"]
        rnd["velox-renderer — backends, events, paint"]
        stl["velox-style — CSS cascade"]
        dom["velox-dom — VNode and layout"]
        cor["velox-core — signals and effects"]
    end

    subgraph apps["Example apps"]
        ex["veloxc-init-default · dashboard · task-board"]
    end

    cli --> sfc
    cli --> stl
    cli --> rnd

    rnd --> cor
    rnd --> dom
    rnd --> stl
    stl --> dom

    ex --> cor
    ex --> dom
    ex --> stl
    ex --> rnd
    ex -. build-dependency .-> cli
Loading

Crates

Crate Purpose Workspace dependencies
velox-core Reactive primitives: signals, effects, lifecycle hooks — leaf
velox-dom VNode types, block + flex layout, positioning, overflow — leaf
velox-style CSS parsing, selectors, cascade; styles applied to VNode velox-dom
velox-renderer Rendering backends, event dispatch, hit testing velox-core, velox-dom, velox-style
velox-sfc Single-file component compiler (pest grammar) → Rust dev only: velox-core, velox-dom, velox-renderer
veloxc The CLI and dev server velox-sfc, velox-style, velox-renderer

Notable external dependencies: cssparser + selectors in velox-style, pest in velox-sfc, clap + anyhow + notify in veloxc, and the optional graphics stack in velox-renderer. The example apps depend on velox-core, velox-dom, velox-style and velox-renderer with the skia-native feature, and use veloxc as a build-dependency.

Renderer features

Feature Enables
default = [] nothing — opt in when you're ready
skia the Skia API surface, no native dependencies
svg resvg rasterisation for <img src="*.svg">
skia-native the windowed backend: winit, skia-safe, egl, glow, softbuffer — implies skia and svg

Template syntax

.vx files are Velox's single-file component format:

<!-- src/App.vx -->
<template>
  <div class="container">
    <h1>{{ title }}</h1>
    <p v-if="count > 0">Count is positive</p>
    <p v-else>Count is zero or negative</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script setup>
use velox_core::ergonomics::Ref;

pub struct State {
    count: Ref<i32>,
}

impl State {
    pub fn new() -> Self {
        Self { count: velox_core::r#ref!(0) }
    }

    // Interpolations and event handlers take `&self` — state lives in
    // interior-mutable `Ref<T>` signals, not behind `&mut self`.
    pub fn count(&self) -> i32 {
        self.count.get()
    }

    pub fn increment(&self) {
        self.count.set(self.count.get() + 1);
    }
}
</script>

<style>
.container {
  padding: 24px;
  font-size: 18px;
  border-radius: 8px;
  opacity: 0.9;
}
</style>

Directives

Directive Usage
{{ expr }} Template interpolation
v-if="cond" Conditional rendering
v-else-if="cond" Additional condition
v-else Fallback
v-for="item in items" List rendering
v-show="cond" Toggle display without unmounting
v-model="field" Two-way binding on form controls
@event="handler" Event binding
:attr="expr" Attribute binding — :class, :style, :key, props
v-slot:name / #name Named slot fill (the value is parsed; slot props are not bound)

Child components are imported in <script setup> and used as elements:

<Todos />

See event payload handling for on:<event>-payload and inline closures.


Feature status

What actually works today, stated plainly. Nothing here is aspirational.

Working

Category Features
SFC .vx parsing · {{ expr }} · v-if/v-else-if/v-else · component imports
Layout Block layout · Flex layout · static/relative/absolute/fixed/sticky · z-index · overflow clipping
CSS Selectors · cascade · font-size · font-weight · line-height · text-decoration · border-radius · opacity
Rendering Skia immediate-mode paint · event hit-testing · skia-native backend
Events click · input · change · keyboard · mouse

Parsed but not painted

These properties parse successfully, survive the cascade, and produce no visual change. veloxc lint reports each one when you use it.

box-shadow · font-style · letter-spacing · visibility · overflow-x · overflow-y · background-image · transition · border-style · border-color · transform (stacking only, no visual transform)

Why isn't the reconciler in production? (read before asking)

The render loop is immediate-mode. run_window_vnode_skia takes the &VNode, runs compute_layout over the whole tree, and paints it. There is no retained tree, no previous frame to compare, and no patch applier.

velox-dom ships a complete, duplicate-key-safe keyed reconciler (diff::diff) but it has zero production callers — only tests invoke it.

The renderer's reconcile_keyed_children was deleted: it had no production callers and had a stale-content bug on key match.

v-for reordering: correctness comes from VNode child order, not from :key. An <input> inside a reordered v-for loses focus/caret to whichever item occupies its index. This is a known limitation, not an oversight.

Full decision record: docs/internal/RECONCILER.md


Examples

Five example apps ship in examples/:

App What it demonstrates Screenshot
veloxc-init-default Default scaffold: components, theme, dialogs, v-for init-default
dashboard Analytics dashboard: grid, charts, sidebar, real-time updates, dark mode dashboard
markdown-editor Split-pane Markdown editor with live preview, toolbar, resize handle markdown-editor
task-board Kanban board: drag-to-reorder columns, localStorage persistence, modal forms task-board
settings-panel System settings UI: forms, toggles, tabs, validation, accordion, toasts settings-panel

A display or compositor is required to see a window.


Building from source

git clone https://github.com/fahimaloy/velox.git
cd velox

# Build everything
cargo build --workspace
cargo build --workspace --release

# Full test suite
cargo test --workspace --all-targets

# Skia backend only
cargo build -p velox-renderer --features skia-native

# A single crate
cargo test -p velox-core
cargo test -p velox-sfc
cargo test -p velox-renderer

Any workspace-wide cargo command turns on velox-renderer/skia-native, because veloxc declares it as a dev-dependency feature and the examples build-depend on veloxc. Expect a heavy first build.


Guides and documentation

Document What's in it
Documentation book Getting started, three tutorials, feature reference, and the advanced internals
ARCHITECTURE.md Workspace layout, data flow, backends
CHANGELOG.md Release history and unreleased changes
docs/src/advanced/reconciler.md Why :key reordering works without a production reconciler
docs/src/advanced/rendering-pipeline.md How Velox paints, and the measurements behind it
docs/src/advanced/viewport.md The make_view(w, h) viewport contract
docs/src/features/events.md Event payloads: on:<event>-payload and inline closures
docs/internal/ Audit records, plans, tasks, and the headless Skia-native CI notes

API documentation: docs.rs/velox-core · docs.rs/veloxc · docs.rs/velox-renderer


Contributing

CI runs on every push and pull request, on every branch. The blocking job is build-test:

cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace --no-fail-fast

Before opening a PR:

# Full test suite, including ignored compile and measurement tests
cargo test --workspace --all-targets
cargo test -p velox-sfc --test integration_compile -- --ignored
cargo test -p velox-renderer --test frame_cost_bench -- --ignored --nocapture

# Release build (not gated by CI)
cargo build --workspace --release

# Smoke-test the scaffolding
veloxc init test-app && cd test-app
veloxc lint src/ && veloxc build src/App.vx
cd ..

Update this README when behavior changes.


License & Trademark

Code: Dual-licensed under MIT OR Apache-2.0 — pick whichever suits your project. See LICENSE for the MIT terms and LICENSE-APACHE for Apache 2.0.

Copyright © 2026 FAHIM AHMED.

"Velox" is a trademark of this project. Both licenses grant the right to copy, modify, and redistribute the code and the logo as part of a Velox project or derivative. They do not grant the right to use the name or logo in a way that suggests Velox project endorsement of a third-party product.

If you want the logo in a project that is not derived from Velox, ask first — that is a trademark question, not a license one.

About

Rust framework/browser-engine for building reactive UIs with .vx single-file components, Skia rendering and HMR

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages