npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

velix

v0.0.5

Published

Frontend framework that also outputs html instead of relying fully on JavaScript.

Readme

Velix

Velix is an HTML-first reactive framework.

You write JSX like React. Velix compiles it into mostly-static HTML + tiny DOM update code. So the browser does less work and you still get reactive UI.

Short version: React-like reactivity, but your HTML goes to the gym first.

Experimental project: APIs can still change.

Why this exists

Most UI frameworks keep doing runtime work:

  • render
  • diff
  • patch
  • repeat

Velix tries to move that cost to compile time.

  • HTML is generated ahead of time.
  • DOM paths are precomputed.
  • Runtime only updates exactly what changed.

No virtual DOM here. Just real DOM and targeted updates.

Quick start

1. Install deps

yarn

2. Add Vite config

vite.config.js

import { defineConfig } from "vite";
import Velix from "velix";

export default defineConfig({
  plugins: [tailwindcss(), Velix()]
});

3. Bootstrap entry

src/index.js

import App from "./App.jsx";
import { mount } from "velix";

const root = document.getElementById("app");
mount(App, root);

4. Run

yarn dev

Current plugin default root is src/App.jsx. Keep that file name/path, or update it in Package/plugin.js.

Core idea you must know

Velix state values are getter functions.

  • Read with count() not count
  • Effects track dependencies by calling getters

If you forget the (), your UI will politely do nothing.

Reactivity API

Import from velix:

import { useState, useEffect, useMemo, useArray } from "velix";

useState(initial)

const [count, setCount] = useState(0);

count(); // read
setCount(5); // write
setCount(p => p + 1);

useEffect(callback, deps)

  • Runs immediately once.
  • Re-runs when tracked deps change.
  • Automatically tracks accesed signals inside the effect (Optional deps like in react)
useEffect(() => {
  console.log("count changed:", count());
});

with deps

useEffect(() => {
  console.log("count changed:", count());
}, [count]);

useMemo(fn)

Returns a getter for computed value.

const doubled = useMemo(() => count() * 2);
console.log(doubled());

useArray(initialArray)

Array state helper with incremental updates.

const items = useArray(["a", "b"]);

items(); // read array
items.push("c");
items.setAt(0, "A");
items.remove(1);
items.pop();
items.setNew(["x", "y"]); // full replace
items.setNew(prev => [...prev, "z"]);

setNew triggers a full list refresh signal.

Built-in JSX attributes (directives)

These are Velix-specific attributes.

$if

Mount/unmount element by condition.

<div $if={count() > 10}>Now you see me</div>

Use when element should not exist in DOM when false.

$when

Toggle visibility using display: none.

<div $when={isOpen()}>I stay mounted, just hidden</div>

Use when you want to preserve DOM state but hide it.

$for

Loop rendering. Syntax:

  • $for={item in source}
  • $for={item of source}
  • $for={(item) in source}
<ul>
  <li $for={item in items()}>{item}</li>
</ul>

Notes:

  • source must evaluate to an array.
  • Nested loops are supported.
  • Nested conditionals inside loops are supported.

$ref

Access actual DOM node.

<div $ref={el => console.log(el)} />

Or object ref style:

const boxRef = { current: null };
<div $ref={boxRef} />;

Regular attrs, events, spread

Velix also supports normal JSX attributes and spread:

<button
  className={count() > 5 ? "hot" : "cold"}
  onClick={() => setCount(p => p + 1)}
  {...extraProps()}
>
  Click
</button>

Component composition

Components inside components are supported, including cross-file imports.

import Card from "./Card";

export default function App() {
  return <Card />;
}

Nested component HTML is expanded during compile/scan phase.

Full example

import { useState, useArray, useMemo, useEffect } from "velix";

function Badge() {
  return <strong>Badge</strong>;
}

export default function App() {
  const [count, setCount] = useState(0);
  const todos = useArray(["Ship", "Sleep"]);
  const titleRef = { current: null };

  const status = useMemo(() => (count() > 3 ? "busy" : "chill"));

  useEffect(() => {
    console.log("status:", status());
  }, [status]);

  return (
    <main>
      <h1 $ref={titleRef}>Count: {count()}</h1>

      <p $when={count() % 2 === 0}>Visible only on even counts</p>
      <p $if={count() > 2}>Appears only after 2</p>

      <button onClick={() => setCount(p => p + 1)}>+1</button>
      <button onClick={() => todos.push(`Todo ${count()}`)}>Add Todo</button>
      <button onClick={() => todos.setNew(["Reset"])}>Reset List</button>

      <ul>
        <li $for={todo in todos()}>
          <Badge /> {todo}
        </li>
      </ul>
    </main>
  );
}

Practical rules

  1. Always call state getters (x()).
  2. Keep useEffect deps as getter functions ([count, todos]).
  3. Use $if for mount/unmount, $when for visibility.
  4. Use $for only with array-like sources (ideally arrays).
  5. Use $ref only when you really need DOM access.

Known limits (for now)

  • Early-stage project, breaking changes can happen.
  • Error messages are improving but still basic.
  • API ergonomics are still evolving.

Final vibe check

If you want:

  • real HTML first
  • tiny runtime updates
  • React-like mental model without React-sized runtime work

Velix is your weird little friend.