camelon

camelon

A server-rendered hypermedia framework. A web-standard Request → Response core, @kitajs/html for JSX, and Datastar for client reactivity. No client framework. File-based routes. Your TypeScript types are the runtime contract.

It starts with plain HTML from the server — no client JavaScript at all:

export default function Hello() {
  return (
    <div class="d-card">
      <strong>Hello from camelon</strong>
      <p>A route with just a <code>default</code> export renders straight to HTML.</p>
    </div>
  );
}
live

Add Datastar and it becomes interactive when you want it. The counter below is a page, two resource routes, and an SSE patch — running for real, right here:

export default function Counter() {
  return (
    <div class="d-row" signals='{"count": 0}'>
      <button class="d-btn" on:click="@post('/counter/decrement')"></button>
      <output class="d-num" text="$count">0</output>
      <button class="d-btn" on:click="@post('/counter/increment')">+</button>
    </div>
  );
}
import type { PostFunction } from 'camelon';

export type PostInput = { signals: { count: number } };

export const post: PostFunction<PostInput> = ({ stream, signals }) => {
  stream.patchSignals({ count: signals.count + 1 });
};
import type { PostFunction } from 'camelon';

export type PostInput = { signals: { count: number } };

export const post: PostFunction<PostInput> = ({ stream, signals }) => {
  stream.patchSignals({ count: signals.count - 1 });
};
live

New here? Start with Install & bootstrap.

Hypermedia, not SPA

The server renders HTML. Datastar patches it over SSE. The browser never downloads a component runtime.

One core, many runtimes

The HTTP core is web-standard, so the same app runs on Node, Deno, Bun, or Cloudflare Workers. See runtime targets.

Types are the contract

Declare GetInput and GetOutput once. They drive the typed args, runtime validation, and the OpenAPI spec. See typed contracts.