Universal reactive signals with automatic SSR, seamless hydration, and zero-dependency DOM updates. Works everywhere - browser, Node.js, Deno, Bun.
Zero dependencies. Zero transpilation. Zero framework lock-in.
npm install @raven-js/reflex
import { signal, computed, effect } from "@raven-js/reflex";
// Create reactive state
const count = signal(0);
const doubled = computed(() => count() * 2);
// React to changes
effect(() => {
console.log(`Count: ${count()}, Doubled: ${doubled()}`);
});
// Update state
count.set(5); // Logs: "Count: 5, Doubled: 10"
import { reactive } from "@raven-js/reflex/universal";
// Same code runs on server and client
const App = reactive(async () => {
const data = signal([]);
// Automatic fetch caching during SSR
const response = await fetch("/api/data");
data.set(await response.json());
return () =>
`<div>${data()
.map((item) => `<p>${item}</p>`)
.join("")}</div>`;
});
// Server: renders HTML with embedded state
// Client: hydrates automatically, no duplicate fetches
import { mount, patch } from "@raven-js/reflex/dom";
// Mount reactive components (browser-only)
mount(component, document.getElementById("app"));
Reflex integrates seamlessly with @raven-js/wings for optimal fullstack reactivity:
import { Router } from "@raven-js/wings";
import { html } from "@raven-js/beak";
import { reactive, signal, computed } from "@raven-js/reflex";
const router = new Router();
// Reactive route handlers - same code runs server & client
router.get(
"/todos",
reactive(async (ctx) => {
// Create reactive state
const todos = signal([]);
const filter = signal("all");
// Automatic fetch interception + SSR caching
const response = await fetch("/api/todos");
todos.set(await response.json());
// Computed values work perfectly
const filteredTodos = computed(() => {
switch (filter()) {
case "active":
return todos().filter((t) => !t.completed);
case "completed":
return todos().filter((t) => t.completed);
default:
return todos();
}
});
// Return beak template with reactive data
return ctx.html(html`
<html>
<head>
<title>Todo App</title>
<script src="/client.js"></script>
</head>
<body>
<h1>Todos (${filteredTodos().length})</h1>
<ul>
${filteredTodos().map(
(todo) => html`
<li class="${todo.completed ? "done" : ""}">${todo.title}</li>
`
)}
</ul>
</body>
</html>
`);
})
);
// Wings handles the server, Reflex handles the reactivity
router.listen(3000);
Key Benefits:
MIT - Copyright (c) 2025 Anonyfox e.K.