Showcase · Web framework

NeoKit

A full-stack web framework, in the spirit of Next.js.

File-based routes, server rendering, static export and typed templates checked by the compiler. A build produces one static server binary with CSS, public files and browser modules embedded: no Node, no JS engine at run time.

1.99×Rust's server-rendering throughput on a 1,000-row page; 1.85× Go, 4.58× Bun

Results

Server rendering Large catalog page: 1,000 rows, 105 KB req/s, higher is better
NeoKit 12.2k
Go 6.6k
Rust 6.1k
Bun 2.7k

1.85× more than Go1.99× more than Rust4.58× more than Bun

Server rendering Interactive page: 50 rows and a client widget, 6 KB req/s, higher is better
NeoKit 71.4k
Rust 50.2k
Go 27.5k
Bun 26.9k

1.42× more than Rust2.60× more than Go2.65× more than Bun

Server CPU per request Large page, user + kernel µs, lower is better
NeoKit 80.20 µs
Go 151 µs
Rust 162 µs
Bun 375 µs

1.88× lower than Go2.02× lower than Rust4.67× lower than Bun

p99 latency Large page, 32 connections µs, lower is better
NeoKit 3,820 µs
Rust 6,130 µs
Go 20,940 µs
Bun 22,930 µs

1.60× lower than Rust5.48× lower than Go6.00× lower than Bun

Peak memory Large page, under load MiB, lower is better
NeoKit 0.93 MiB
Rust 3.50 MiB
Go 12.4 MiB
Bun 132.8 MiB

3.76× less than Rust13.4× less than Go142.8× less than Bun

Peak memory Small page, under load MiB, lower is better
NeoKit 0.61 MiB
Rust 3.46 MiB
Go 12.5 MiB
Bun 55.1 MiB

5.67× less than Rust20.4× less than Go90.4× less than Bun

Deployed size What the server needs besides the operating system MiB, lower is better
NeoKit 0.16 MiB
Rust 0.89 MiB
Go 7.75 MiB
Bun 97.1 MiB

5.70× less than Rust49.5× less than Go620.4× less than Bun

x86-64 Linux, each server on one pinned AMD EPYC Genoa vCPU · wrk -t1 -c32 on the second vCPU of a Vultr dedicated 2 vCPU instance, Ubuntu 24.04, Linux 6.8 · 31 October 2026 · bench/ssr-compare/run.py

The same catalog application in each: query parsing, escaped text, a checksum and a fresh timestamp per request. Bodies are byte-identical apart from the timestamp, and every server is checked by an independent HTML model before it is timed. Medians of 5 interleaved 10-second rounds after a 2-second warm-up. Bun 1.3.13 (Bun.serve), Go 1.25.1 (net/http), Rust 1.99.1 (hyper 1.12 on tokio, one worker). The framework app builds its rows with an ordinary in-place loop. Its compiler escapes text known at build time while compiling; the other servers escape the notes on every request. Peak memory is each server's peak RSS under load. Deployed size: NeoKit's single static binary, with CSS and browser modules embedded and no C library; the Rust and Go binaries, which also load the system C library; Bun's runtime plus the 3 KB script.

What it does

  • File-based routing with per-page SSR or static modes, dynamic routes and API routes
  • Typed templates: every prop is checked against the page's loader at compile time
  • Layouts, components and Tailwind styling
  • Optional client runtime for navigation, enhanced forms and local widgets
  • Static export to plain HTML, CSS and JS for any host
  • Sub-second clean builds, with dependencies compiled once and cached

Usage

app/page.view
@props(title: text, count: int, server: bool)
<h1>{{ title }}</h1>
<p>{{ count }}</p>
{% if server %}<p>Rendered on request.</p>
{% else %}<p>Exported HTML.</p>{% endif %}
terminal
# one static server binary
$ neokit build
$ neokit serve --port 8094

# plain HTML, CSS and JS for static hosting
$ neokit export