Warning
Experimental: API is unstable and not production-ready.
Cloudflare-optimized streaming SSR/CSR meta-framework built on web platform APIs, whilst retaining the DX of JSX / React|Preact.
- Streaming SSR — File-based routing with deferred promise streaming
- Web Components — Hydration via
preact-custom-element - SPA Navigation — Navigation API + View Transitions
- HMR — Hot module replacement with scroll restoration
- Styles — Constructable Stylesheets, critical CSS extraction
- Performance — Early hints, route caching, preconnect hints, speculation rules
- Cloudflare — Workers-optimized with edge caching
- TypeScript — Full type safety
- Node.js ≥v24.12.0
- Modern browser (Chrome 102+, Edge 102+, Safari 15.4+)
solarflare [options]| Option | Description |
|---|---|
--clean, -c |
Clean output before build |
--debug, -d |
Enable debugging |
--production, -p |
Optimize for production |
--serve, -s |
Start dev server with HMR |
--sourcemap |
Generate source maps |
--watch, -w |
Watch and rebuild |
| Directory | Purpose |
|---|---|
./src |
Original (source) human readable code |
./dist |
Compiled (distribution) [client, server] output code |
./public |
Static assets, copied verbatim to dist/client directory |
| File | Purpose |
|---|---|
*.client.tsx |
Client component (web component) |
*.server.tsx |
Server handler (Workers runtime) |
_layout.tsx |
Layout wrapper |
_* |
Private (not routed) |
$param |
Dynamic segment → :param |
index.* |
Directory root |
| Path | Purpose |
|---|---|
/_* |
reserved internal use (e.g. /_console) |
export default async function server(request: Request, params: Record<string, string>) {
return {
_status: 200,
_headers: { "Cache-Control": "max-age=3600" },
title: "Hello",
};
}Promise-valued props are streamed independently (deferred):
export default async function server() {
const user = await fetchUser(); // blocking
const analytics = fetchAnalytics(); // deferred
const recommendations = fetchRecommendations(); // deferred
return { user, analytics, recommendations };
}export default function Client({ title }: { title: string }) {
return <h1>{title}</h1>;
}import type { VNode } from "preact";
import { Body, Head } from "@chr33s/solarflare/server";
export default function Layout({ children }: { children: VNode }) {
return (
<html>
<head>
<Head />
</html>
<body>
{children}
<Body />
</body>
</html>
);
}import { Deferred } from "@chr33s/solarflare/client";
<Deferred priority="high" fallback={<div>Loading additional content...</div>}>
...
</Deferred>;<!-- router -->
<meta name="sf:base" content="/" />
<meta name="sf:scroll-behavior" content="auto" />
<meta name="sf:view-transitions" content="false" />
<!-- performance -->
<meta name="sf:preconnect" content="https://cdn.example.com" />
<meta name="sf:early-flush" content="true" />
<meta name="sf:critical-css" content="true" />
<meta name="sf:cache-max-age" content="300" />
<meta name="sf:cache-swr" content="3600" />
<meta name="sf:prefetch" content="/about, /faq, /blog/*" />
<meta name="sf:prerender" content="/, /landing" />
<meta name="sf:prefetch-selector" content="a.nav-link" />
<meta name="sf:speculation-eagerness" content="moderate" />import { define } from "@chr33s/solarflare/client";
export default define(MyComponent, { shadow: true });| File | Purpose |
|---|---|
WRANGLER_LOG |
Set logging verbosity for both wrangler & console forwarding |
WRANGLER_SEND_METRICS |
Disable sending anonymous usage data to Cloudflare |
- Basic — Layouts, dynamic routes, API, components
- Bun — Bun runtime example
- Deno — Deno runtime example
- Minimal — Single route
- Node — Using
srvxinstead of Workers - Shopify App — Shopify app starter
npm install
npm run devnpm install --save-optional
npx solarflare --codemod ./appMIT