This is the first public post about Quillo, the new web programming lanuage written in Rust, with Rust but faster than Rust in product delivery.
One file holds the server and the browser. Quillo reads like JavaScript, checks your program before it runs, and ships as a single binary.
A web app is written in several languages at once. HTML for the page, one language for the server and often another for the browser, SQL for the data, HTTP between them all, and a framework to hold the pieces together. Each piece is fine on its own. The glue between them is where the time goes, and where the bugs are.
Most new tools attack the glue with more glue: a better framework, a smarter build step, a generated API client. Quillo makes a different bet. It treats the glue as a language problem.
Quillo is a programming language made of the web's own parts. Routes, HTML, components and database queries are part of its syntax, not libraries on top of it. The server and the browser live in the same file, are checked together, and run with one command. It is written in Rust, open source under the MIT license, and at version 0.8.0.
Here is what that looks like.
A whole app in one file
//DEV.to does not know quillo syntax yet so no highlighting for now :) //notes.qwe db.open("notes.db") db.exec("create table if not exists notes (id integer primary key, text text)") server fn add_note(text: str) -> list with db { db.exec("insert into notes (text) values (?)", text) db.query("select * from notes order by id") } component Notes(initial: list) { state notes = initial state draft = "" <form @submit={() => { notes = add_note(draft); draft = "" }}> <input value={draft}/> <ul>{for n in notes => <li key={n.id}>{n.text}</li>}</ul> </form> } route GET / = <html><body><Notes initial={db.query("select * from notes")}/></body></html> That is the entire program: the table, the function that writes to it, the interactive page, and the route that serves it. Save it as notes.qwe, run quillo run notes.qwe, and open http://127.0.0.1:8080.
Several things happen here that you would normally wire up by hand:
- The page is rendered on the server. The browser receives finished HTML.
- The component takes over that HTML as it is. Nothing is drawn twice: Quillo resumes the server's page in the browser instead of building it again (hydrating).
-
add_noteis a server function. Calling it from the browser calls the server. There is no API to design, nofetch, no JSON to parse, and noasyncorawait. - The call is checked.
quillo checkreads the component and the server function together, soadd_note(42)is an error before anything runs:argument 'text' of 'add_note' must be str, got int.
To know whether its working or not. quilloer.com is a flagship community-based blogging website made entirely using Quillo. Go check and inspect the browser network tab.
The example above is an app in a single note.qwe file. Then maybe you will push back and say "easy-spaghetti". Like any other lanuguage nothing is spaghetti eliminator, but Quillo supports imports, use, client for its namespace.
Why it looks the way it does
Each unusual choice in Quillo follows from one decision: the web is big enough, and common enough, to deserve a language of its own.
- Routes and markup are syntax, because they are what a web program is made of.
- No
asyncorawait, because a web program mostly waits, and waiting should not change how code looks. Waiting on a database or another server never blocks other requests. - Errors are values, because a missing row is an outcome, not an exception.
- Components resume instead of hydrating, because the server already did the work.
- Pages follow the database on their own, because the language sees both the reads and the writes.
- One binary, because the server, the checker, the compiler and the language server are one tool.
Routes are patterns
route GET /hello/:name = `Hello, ${name}!` route GET /items ? { page: int = 1 } = { page: page } route POST /orders { json: { type: "order" | "refund", customer.id: int as id } } { reply 201 { accepted: true, customer: id } } A route matches the path, the query string and the JSON body by pattern, with types. /items?page=3 gives page as an int, and 1 when it is missing. A body whose type is neither "order" nor "refund" never reaches your code: the request gets a 400. A route that returns a map answers with JSON; one that returns HTML answers with a page.
Pages that follow the data
db.open("app.db") db.exec(sql`create table if not exists todos (id integer primary key, text text)`) server fn todos() = db.query("select * from todos order by id") server fn add(text: str) = db.exec("insert into todos (text) values (?)", text) component Todos() { state items = live(todos) state text = "" <form @submit={() => { add(text); text = "" }}> <input bind:value={text} placeholder="What next?"/> <ul>{for t in items ?? [] => <li key={t.id}>{t.text}</li>}</ul> </form> } route GET / = <html><body><Todos/></body></html> live(todos) makes the list follow the database. When anyone adds a todo, every open page showing the list updates. You write no WebSocket code, no cache invalidation and no polling: Quillo knows that todos reads the todos table and that add writes to it.
Components are fine-grained. A component's code runs once, its state is signals, and each place on the page that shows a value follows that value alone. A page loads only the components it uses, when someone reaches for them: about 9 KB compressed for a simple one.
Errors are values
fn parse_age(s: str) -> int ! str = match int(s) { int as n if n >= 0 => n _ => err(`bad age: ${s}`) } fn next_year(s) = parse_age(s)? + 1 // 42 for "41", err("bad age: x") for "x" let age = int("abc") ?? 18 // 18 Built-in functions such as int("abc") return errors instead of throwing. ? passes an error up to the caller, and ?? gives a fallback. ! str declares what a function's errors hold, and that is checked too.
Exact money
0.1m + 0.2m == 0.3m // true (0.1 + 0.2 == 0.3 is false, as everywhere) let rent = money.of(100m, "USD") money.allocate(rent, [1, 1, 1]) | map((m) => money.format(m, "en-US")) // ["$33.34", "$33.33", "$33.33"] Decimals are a type of their own (the m suffix): exact from PostgreSQL's numeric all the way to the page, and never mixed with floats by accident. money keeps amounts in their currency's smallest unit, splits them without losing a cent, and formats them the way each locale does.
Databases, all of them
SQLite is built in, and PostgreSQL and MySQL (or MariaDB) use the same functions. There are migrations and transactions. When an app grows, Quillo also gives it a database per tenant, read replicas, shards, and one transaction across several databases (db.atomic).
A monolith with an escape hatch
// billing.qwe db.open("billing.db") db.exec("create table if not exists invoices (id integer primary key, name text not null, cents int not null)") server fn invoice(name: str, cents: int) -> map = db.one("insert into invoices (name, cents) values (?, ?) returning id", name, cents) // shop.qwe service billing = "billing.qwe" { timeout: "2s", retries: 2 } route POST /buy with http { let inv = billing.invoice(request.form.name, 1200)? <p>Invoice #{inv.id}</p> } quillo run shop.qwe runs billing in the same process. quillo run shop.qwe --split runs it as a server of its own, and the code does not change. Calls between services are signed, retried without running twice, spread over several addresses, kept away from one that is failing, and traced with one request id. A call can also be queued to run later (billing.later.send_reminder(...)), and a busy service can get more copies while it runs (--join).
Rust when you need it
A .qwer file is Rust. use "imaging.qwer" builds it, and its pub fns become functions in your program, checked against their Rust types and run off the event loop, on every core.
// imaging.qwer pub fn brightness(path: &str) -> Result<f64, String> { /* any crate you like */ } Checked before it runs
quillo check knows types, records, effects, routes, components and services, and finds mistakes without running anything. The same checker powers a language server (completion, hover, go to definition), and quillo dap adds breakpoints and stepping. Quillo works in VS Code, the JetBrains IDEs, Zed, Neovim, Helix, Sublime Text, Emacs and Vim, and Quillo DevTools shows a page's components and state in Chrome and Edge.
Fast
- Components. On js-framework-benchmark, Quillo scores 1.07 against plain JavaScript (1.00, the fastest). That is next to Solid (1.04) and ahead of Svelte 5 (1.15), Vue 3 (1.78), Preact (1.94) and React 19 (2.08).
- Numbers. A function doing arithmetic is compiled to machine code with Cranelift the second time it is called.
fib(32)takes 11 ms, as in Node (Bun: 21 ms), and a ten-million-step loop takes 29 ms (Node and Bun: about 280 ms), with the interpreter's exact answers and errors.
How each benchmark runs, and every number, is in quillolang/benchmarks.
Everything else, in the box
Sessions, uploads, images, translations, streaming, scheduled jobs, metrics, UDP and WebTransport channels for real-time data, a bridge to JavaScript libraries, multi-tenant hosting, reloads that let requests in progress finish, and desktop apps in a native window (quillo app). Running the same program inside an iOS app is in preview.
How it is being tested
Tests check what a language's authors thought of. The bugs that matter are in what they did not think of.
So Quillo has more than 3,200 tests, which run 98.65% of its Rust lines and 99.88% of its browser runtime. It is also tested the way a language is really used: by building apps in it. A forum, a project board, a newsroom CMS, a bank of five services, a booking platform and more, each built with one instruction: bring out what breaks, and write it down. That has produced 62 reports and more than 750 numbered issues so far, each with a repro of a few lines, and each fix points back to its issue.
The point is not to prove that Quillo works. It is to find where it does not, before you do.
What's next: tools for AI
The next release lets a function be a tool that an AI client such as Claude can call:
/// The orders placed today, newest first. fn mcp orders_today(limit: int = 20) -> list with db { db.query("select id, total from orders order by id desc limit ?", limit) } The /// comment is the description the model reads, and the types become the tool's JSON Schema. Quillo works out from the code whether a tool only reads, and then runs it in a read-only transaction. quillo mcp app.qwe serves the tools to an AI client on your machine, and quillo run serves them over HTTP to a signed-in user or a token.
What Quillo is not
Quillo is not a general-purpose language, and it does not try to be. It will not write your kernel or your game engine. It keeps the web's own standards (HTTP, HTML, CSS, SQL) and reaches for Rust or JavaScript when a job needs them.
It is also young: the language may still change before 1.0. Now is a good time to try it and say what you think.
Try it
Download quillo for Linux, macOS or Windows from the releases page, put it on your PATH, and start an app:
quillo new hello cd hello && quillo run --watch # http://127.0.0.1:8080 quillo new writes a small to-do app laid out the way larger programs are. Edit it while it runs.
Q/A
Q: Why should I learn a new syntax rather than just use Next.js, SvelteKit, or Axum in Rust?
A: Quillo is statically and eagerly checked before it runs (compile-time safety) in ways that standard JS/TS cannot easily do, and how it deploys as a single, lightweight binary without the nightmare of Node/npm dependency hell.
Q: Does it support HMR?
A: Partly doing using page reload at 0.145 ms for quillolang.org website with 3000+ pages all in memory. Real HMR is in the pipline. It also does not load broken .qwe source with error stack trace
Q: Does it includes browser DevTools?
A: Yes, in both Edge and Chrome Browser. see Tools
Q: Does it include IDE Plugins?
A: Yes, every IDE you know to use maybe already included. see Tools
Q: Does it includes ready components and plugins?
A: Yes, it has 800+ components and 600+ rust plugins for backend.
Q: When is the version 1.0 going to be released?
A: With at least 50 more open items to be released and growing because of hardening, we are seeing 5 months to 1 year before we could reach beta release version.
Q: Who coded Quillo?
A: I built Quillo using Rust for the binary, with AI serving as a major force multiplier. I used Claude Opus to help accelerate development, optimize multi-core/threading implementations, and achieve greater speed gains. Google Gemini was used for design research and developer pain-points identification. As the creator, I stay fully in control—handling the overarching architecture, documentation, rigorous regression testing, hand-built app testing, guidance, guard-rails and workflows. This is why the single merge to main can span 2-5 days.
Q: Will you still use AI to maintain Quillo?
A: Yes. Similarly to other languages and frameworks these days, I will continue to work with AI using a highly controlled workflow to maintain a strong human touch in the process.
Q: Do you still sleep?
A: Yes and mostly around 2am, and I do full weekend work for Quillo and holidays :).
Q: What happend to Juris.js project?
A: Juris.js now has Juris-kit but havent released officially yet due to performance concerns and bugs. But we will get there soon.
Q: Where to get the StackOverflow Mascot used in Quillo page?
A: Want a mascots for your website like the ones seen Quilloer and HttpDash? You can get them at DomainMascots for Free (for now). DomainMascots is actually the flagship SaaS app built entirely using Quillo!
Q: Where can I get the mascot used on the Quillo page?
A: Want a mascot for your website like the ones seen on Quilloer and HttpDash? You can get them at DomainMascots for free (for now). DomainMascots is actually the flagship SaaS app built entirely using Quillo!
- Website and docs: quillolang.org
- Join Me at Quilloer: quilloer.com
- Source: github.com/quillolang/quillo
- Examples: github.com/quillolang/examples
- Community: Discord and r/quillo
About the name
Nobody chose it. It was typed. Late one night, hands resting on the keyboard as sleep won, six letters landed in an empty editor: quillo. It should have been deleted. Instead it got read out loud, and it sounded right.
A quill is a pen cut from a feather, the tool people wrote with for a thousand years. For a language whose whole point is writing the web, an accident could hardly have done better.


