Primate Is the Last Great Web Framework

terrablue3 pts0 comments

The Answer Is C

I'm the creator of Primate, so take the title with the<br>appropriate grain of salt. By "great web framework", I mean something specific:<br>a tool that takes ownership of the whole stack. Routing, rendering, data,<br>validation, sessions, deployment targets, runtime support. The pieces should be<br>designed to fit together.

I grew up building web apps with PHP frameworks: mostly Yii, some Laravel. What<br>I loved about them was not PHP itself, but the feeling that the framework owned<br>the stack. Databases, templates, routing, validation, patterns like MVC. You<br>could swap pieces inside one coherent system. You could go to the framework's<br>website and see what was officially supported. You knew what was familiar<br>territory, and what was outside the map.

As JavaScript moved to the server, that idea mostly disappeared. The JavaScript<br>ecosystem strongly prefers composition over cohesion. You choose a server<br>framework, then a database client, then validation, then sessions, then a<br>frontend, then a build tool, then glue it all together. Each piece may be<br>excellent on its own, but nobody owns the seams.

A UNIX purist might say that is the right model: each tool should do one thing<br>well. But web applications are not just pipelines of isolated tools. They are<br>full of shared assumptions: request shapes, validation boundaries, session<br>handling, rendering, routing, serialization, deployment targets. In practice,<br>things often fail in subtle ways exactly where those tools meet.

Meta-frameworks improved the situation, but only by straitjacketing the world.<br>Next gives you a coherent stack, but it is a React stack. If you discover Solid<br>and want to try it, you do not simply switch the view layer. You move to a<br>different meta-framework with different conventions, different routing details<br>and different backend assumptions.

The same pattern repeats across the ecosystem: filesystem routes plus one<br>blessed frontend, rebuilt again and again. React gets one meta-framework. Solid<br>gets another. Svelte gets another. Vue gets another. Each one solves mostly the<br>same backend problems, but in slightly different ways you have to relearn every<br>time.

Runtime fragmentation made this worse. Node, Deno, and Bun are all capable<br>JavaScript runtimes, but some frameworks are now effectively runtime-specific.<br>That means your app can be tied not only to a frontend, but also to a runtime.<br>And when a framework says it "supports" Deno or Bun, that often just means the<br>runtime is compatible enough to run Node code. It does not necessarily mean the<br>framework was designed to use each runtime's own APIs and execution model.

There is no reason for this. Frontends are ways to describe browser UI.<br>Backends are ways to handle requests, data, and application logic. Runtimes are<br>execution targets. These things should be allowed to vary independently.

You should be able to write one route with React, another with Svelte, another<br>with Marko, and keep the same backend model.* You should be able to write backend<br>routes in TypeScript, Go, Ruby, or Python, and compose them inside one app. You<br>should be able to run the same app on Node, Deno, or Bun without changing a<br>single line of code or using runtime-specific packages to bridge the gap.

And the framework should still provide official support for validation, database<br>stores, sessions, i18n, routing, and rendering. That is what Primate is built<br>to be.

Primate is a web framework that does not belong to one frontend, one backend<br>language, or one runtime. It gives you one application model, then lets you<br>choose the pieces route by route. Use React where React makes sense. Use Svelte<br>where Svelte feels better. Use TypeScript for most routes, but Go, Ruby, or<br>Python where their ecosystems or runtime characteristics fit better. Run on<br>Node today, Deno tomorrow, Bun later.

Primate is not hype. It is not benchmark theater. The point is not novelty. The point is removing coupling, lock-in, and decisions other people make for you.<br>You can still reach outside the official stack when you need to, but when you<br>stay inside it, the seams are Primate's responsibility.

A web framework should own the stack without owning your choices.

A Small Example

Here is a tiny app that already shows the point: one backend model, multiple<br>frontends, no framework switch.

Create a project:

Copymkdir primate-demo<br>cd primate-demo<br>npm init -y --init-type=module<br>npm install primate @primate/react @primate/svelte react react-dom svelte<br>Configure the frontends:

Copy// config/app.ts<br>import react from "@primate/react";<br>import svelte from "@primate/svelte";<br>import config from "primate/config";

export default config({<br>modules: [<br>react(),<br>svelte(),<br>],<br>});<br>Create a React route and collocated page:

Copy// routes/index.ts<br>import response from "primate/response";<br>import route from "primate/route";

export default route({<br>get() {<br>return response.page({<br>title: "One app",<br>links: [<br>{ href: "/react", label: "React route" },<br>{ href: "/svelte",...

primate react framework svelte runtime route

Related Articles