Qite.js
Frontend framework for people who hate React and love HTML.
- No build steps, no Virtual DOM, no npm, no mixing JavaScript with HTML.
- Completely self-sufficient and very powerful.
- Compatible with SSR and is super fast.
If you want your UI to feel like structured HTML enhanced with clear, declarative behavior instead of a compiled abstraction layer, Qite.js is for you.
Why Qite.js exists
Modern JavaScript frameworks made a simple idea complicated: take some HTML, do something with the user input, update the page.
Instead of treating the DOM as the source of truth, they invented virtual DOMs, compilers, hydration layers, bundlers, and complex rendering pipelines. They require a build step, special syntax, and a mental model detached from the actual browser. And on top of that, there's a million npm packages you need to install.
Qite.js goes the opposite direction.
- It treats the DOM as the source of truth and manipulates it directly, but does not simulate it.
- It does not re-render it from scratch, neither does it require compilation. It works with plain HTML.
- It doesn't require npm — everything you need is one single repository you can use git submodule it (or just copy it into) your project.
If you ever thought: "why is this web framework stuff so complicated and bloated?", Qite.js may be for you.
No build step. No virtual DOM.
Qite.js works directly in the browser. Just include this into your HTML page:
<script src="/assets/vendor/qite/src/qiteinit.js"></script>
then write components in plain JavaScript. There's no JSX, no transpilers and no bundlers required.
HTML is never mixed with JavaScript under any circumstances, so representation stays completely separate from UI logic. Qite also relies on CSS for effects and handles CSS transitions correctly - regardless of whether you write them yourself or use some of Qite's standard included components and their styles.
SSR first, SPA when you want it
Qite.js fits naturally into server-side rendered applications. You render HTML on the server, then Qite attaches behavior to it.
It then follows you can:
- Keep most pages fully SSR.
- Make selected sections behave like small SPAs.
- Or build a full SPA if you want.
There is no forced architecture. Qite.js enhances what is already there.
DOM as the source of truth
Qite.js does not maintain a shadow copy of your UI. Each component is attached to a real DOM element:
<div data-component="Counter"></div>
The component manipulates that element directly. When something changes, the DOM is updated immediately — there's no diffing, no reconciliation phase, no synthetic re-render cycle.
Qite philosophy is that the browser is already optimized for DOM operations and, therefore, we ought to trust it to do its job well.
Declarative states
Showing and hiding parts of the UI is declarative and predictable.
You define rules:
static display_states = [
{ active_mode: "all" },
// NOTE: . and # is not selectors here.
// They mean "part" and "field" respectively.
[{ status: "loading" }, "#spinner" ],
[{ status: "error" }, "#error" ],
[{ status: "ready" }, ".content" ],
[{ status: ["loading", "error"] }, null, [
[ { flags: ["debug"] }, "debug_panel" ]
]]
];
There's no manual toggling classes in five different places, no race conditions between effects and the state engine decides what is visible.
Children are dumb, parents are smart
Components form a hierarchy, each child component may emit events — but they do not normally decide what the application should do. It's the parents who listen, interpret, and decide.
This keeps logic centralized and predictable. A Button does not know about business rules — it emits an event and the parent decides.
Fields and flags live in the DOM
Component fields are defined once and automatically reflected as data attributes.
<input data-component="TextInput"
data-value="Hello"
data-disabled="false">
The component stores fields internally and mirrors them in the DOM. When a field changes:
- The component updates.
- The DOM attribute updates.
- States may re-evaluate.
There is no hidden state separate from your markup.
Events are simple and explicit
Components communicate through events. A component may, for example, publish:
this.events.publish("submit");
Then a parent can subscribe to it and decide what to do when event is triggered. Events are explicit, scoped, and predictable — they do not rely on global buses or implicit re-render cycles. And you can subscribe to both kinds of events — the ones generated by components as well as the ones coming from DOM — all with the same simple API:
// "henlo" event from a comopnent with the role "fren".
this.events.addOne("henlo", "fren", () => console.log("HENLO FREN!"));
// DOM event from the corresponding DOM-element
this.events.addOne("@click", "self", () => console.log("Click!"));
What Qite.js IS and is NOT
it is:
- a DOM-first component framework.
- a declarative state engine.
- a hierarchy-driven event system.
- a zero-build-step solution.
it is NOT:
- not a virtual DOM framework.
- not a template compiler.
- not dependent on a bundler.
- not a React clone.