I build a lot of websites, and at some point I started wondering: why does building a mostly-static website require so much stuff?
A framework, a bundler, a router, a rendering strategy, hydration, server components, client components, adapters, deployment configuration… all to eventually just send some HTML to a browser.
So I built Sitelo a small, zero-config framework for building fast websites with JavaScript:
npm install -D sitelo
JavaScript in. HTML out.
The basic idea behind Sitelo is deliberately boring. Create:
src/
index.ht.js
about.ht.js
blog/
[slug].ht.js
The .ht.js marks a file as a page — Sitelo turns each one into a route.
And Sitelo turns that into a website.
Your pages (files named *.ht.js or .html.js) are JavaScript functions that return HTML:
// index.ht.js
export default () => `
<html>
<head>
<title>Home Page</title>
</head>
<body>
<h1>Hello world</h1>
</body>
</html>
`
Then:
sitelo build
And you get static HTML in dist/ There’s no application runtime required to serve it, no hydration, and no JavaScript shipped to the browser unless you actually ask for it.
Zero JavaScript should be the default
A lot of modern frameworks start with a JavaScript application and then give you increasingly sophisticated ways to reduce how much JavaScript reaches the browser.
Sitelo starts from the opposite direction: there is no browser JavaScript by default.
If your page doesn’t need JavaScript, Sitelo doesn’t send any. If it does, link a script:
export default () => `
<html>
<head>
<title>Hello</title>
<script src="/js/some-frontend-script.js"></script>
</head>
...
</html>
`
Sitelo sees it and lets Vite bundle it, while the rest of your page-generation code stays on the build side.
This gives you a very simple mental model:
Generate HTML first. Add JavaScript only where the browser actually needs JavaScript.
But I still want modern framework features
So do I. Simple shouldn’t mean primitive.
Sitelo has file-based routing:
src/about.ht.js → /about
src/blog/[slug].ht.js → /blog/:slug
src/docs/[...path].ht.js → /docs/*
It has build-time data loading and dynamic routes, supports JavaScript and TypeScript, and if you like JSX, you can use .jsx and .tsx too.
Sitelo uses Vite internally, so CSS, TypeScript and browser JavaScript still get the modern tooling you’d expect — you just don’t need to configure Vite yourself.
Static doesn’t mean everything has to be static
One of my favourite parts of Sitelo is server islands.
Most of a page can remain static:
┌─────────────────────────────┐
│ │
│ Static HTML │
│ │
│ ┌───────────────┐ │
│ │ Server island │ │
│ └───────────────┘ │
│ │
│ Static HTML │
│ │
└─────────────────────────────┘
Individual regions can then be rendered on the server at request time.
That means you don’t have to turn your entire site into a server-rendered application just because one part of one page needs dynamic content. The site stays static where it can be, with small dynamic pieces where they’re actually needed.
Batteries included, without becoming a kitchen sink
There are a bunch of small things that every real website eventually needs, and Sitelo handles those too.
You can generate things like:
- 404.html
- sitemap.xml
- RSS feeds
- static search with Pagefind
There are also deployment configurations for Netlify, Vercel, Cloudflare Pages and AWS Amplify.
The dev server includes a small toolbar showing things like the current route, source file, parameters and island count.
The goal isn’t to add every possible feature. It’s to remove the repetitive work between:
“I want to build a website”
and
“Here’s the website.”
It’s also designed for coding with AI
There’s another reason I wanted Sitelo to stay small and predictable: AI coding agents work particularly well when the rules of a project are simple.
They’re considerably less useful when they have to reason about seven layers of framework abstractions before changing an <h1>.
Sitelo can generate llms.txt and project rules that explain the framework to coding agents. More importantly, the framework itself has a small mental model:
Files are routes. Functions generate HTML. Browser code is explicit.
That makes the project easier to reason about, whether the code is being written by a human or an AI agent.
Why not Astro?
Astro, Next.js, Eleventy, Hugo and the many other tools in this space are great at what they do. Sitelo isn’t an attempt to prove that they’re bad or replace them.
It’s an attempt to explore a smaller point on the framework spectrum.
I wanted something where I could:
write JavaScript → generate HTML → deploy it anywhere.
At the same time, I still wanted file-based routing, dynamic routes, data loading, Vite, TypeScript, JSX, search and the occasional bit of server rendering — without starting every project by making a bunch of architectural decisions.
That’s the space Sitelo is trying to occupy.
Who is it for?
I think Sitelo makes the most sense for:
- documentation
- blogs
- marketing sites
- portfolios
- company websites
- content-heavy sites
- sites backed by a CMS or REST API
- developers who like JavaScript but don’t necessarily want a JavaScript application
If you’re building something that behaves primarily like an application, use an application framework.
But if you’re building a website, maybe start with HTML.
Sitelo is open source and MIT licensed.
I’d love to see what people build with it — and if you find something that makes Sitelo more complicated than it needs to be, open an issue.
The whole point is to keep it boring, in the best possible way.
Top comments (0)