100x Faster: HTML6 Template Caching on Bun
The speed difference caching makes
Web apps that skip template caching pay the cost on every request. Load the file, parse the syntax, turn it into executable code, run it, send the result. Over and over, thousands of times a day, doing the same work fresh each time.
uLearn compiles once at startup. Every request after that just runs the already-compiled function. The difference isn't 10% or 50%. It's a thousand times faster.
The numbers: before and after
I ran the same routes twice: once forcing the server to recompile templates on every request, once with production caching enabled. These are the averages across protected routes (dashboard, learn pages, review interface):
Without caching:
- Server response (TTFB): 548ms
- Total page load: 763ms
With caching:
- Server response (TTFB): 0.55ms
- Total page load: 130ms
That's 1000x faster server response and 6x faster total load time. Same hardware, same data, same routes. The only change: compile once instead of every time.
The architecture: Elysia on Bun
Bun is the fastest JavaScript runtime today. Elysia is the fastest framework built on it. Together they're the foundation: SSR with no client-side hydration, minimal bundle size, native speed.
On top of that sits HTML6, a lightweight template engine built for speed. It compiles templates to plain JavaScript functions, fitting naturally into the Bun/Elysia stack.
HTML6 is already fast. Compiling and rendering a heavy page takes around 500ms, not seconds. Caching skips recompilation on every request. Compile once, serve 1000x faster.
How template caching works
The compile function wraps HTML6's compiler with the app's components and pipes:
// html6.service.ts
import html6 from "html6";
import { utilPipes } from "../utils/pipes";
// Load all 84 HTML components once at startup
var components = await loadComponents();
async function compile(page: string = ""): Promise<Renderer> {
var renderer = html6.compile(page, { pipes: utilPipes, components });
return { render: (data) => renderer.render(data) };
}
Each controller caches the compiled renderer at startup:
import { compile } from "../services/html6.service";
class HomeController {
private isProd = process.env.NODE_ENV === "production";
private cache = {
template: null as string | null,
renderer: null as any,
};
private async getRenderer(template: string): Promise<any> {
if (!this.cache.renderer || !this.isProd) {
this.cache.renderer = await compile(template);
}
return this.cache.renderer;
}
public async warmupCache() {
if (this.isProd) {
await this.getRenderer(this.cache.template!);
}
}
// ...
}
// Called at server startup
await homeController.warmupCache();
Load the template file. Call compile() to turn it into a JavaScript function. Save in memory. Done.
Every request after that skips straight to the end:
class HomeController {
// private cache, getRenderer, etc.
async render(context: any) {
const renderer = await this.getRenderer(template);
const renderData = { lang, theme, title };
context.set.headers["Content-Type"] = "text/html";
return renderer.render(renderData);
}
}
No parsing. No compilation. Just execution. The function is already there, waiting in memory.
The trade-off: memory for speed
This approach has a cost: memory. The web service container is allocated 1024 MB instead of the 256 MB used by other backend services. At startup, it pre-compiles around 40 HTML6 renderers and 84 reusable components, holding all of them in memory simultaneously.
In production, that caching uses about 400 MB. Four times the size of a typical Go microservice. Worth it? Absolutely. That 400 MB buys a 1000x speedup on every single request. The server's not working less hard, it's working at a completely different point in the pipeline.
Real-world impact: top 5% globally
These aren't artificial benchmarks. Production numbers from real users hitting the live site put uLearn in the top 5% of all websites globally for performance.
Google's "good" threshold for Time to First Byte is under 200ms. uLearn's protected routes average 0.55ms locally and around 160ms in production from 10,000km away (Buenos Aires to Virginia). Even accounting for network distance, that's 26-300x better than what Google calls excellent.
Cloudflare's edge caching adds another layer. Static assets serve from the nearest edge location, cutting network round-trips and keeping total load times fast even for users on the other side of the world.
The stack
No magic. Two pieces:
Bun + Elysia: Fast runtime, fast framework, built for SSR.
HTML6 + caching: Compile once at startup, hold in memory, render thousands of times.
Each layer is fast. Together, they skip work that slower stacks repeat on every request.
Enjoyed this article?
Follow me for more content on web development, design, and technology.