Primate Is the Last Great Web Framework
I'm the creator of Primate, so take the title with the
appropriate grain of salt. By "great web framework", I mean something specific:
a tool that takes ownership of the whole stack. Routing, rendering, data,
validation, sessions, deployment targets, runtime support. The pieces should be
designed to fit together.
I grew up building web apps with PHP frameworks: mostly Yii, some Laravel. What
I loved about them was not PHP itself, but the feeling that the framework owned
the stack. Databases, templates, routing, validation, patterns like MVC. You
could swap pieces inside one coherent system. You could go to the framework's
website and see what was officially supported. You knew what was familiar
territory, and what was outside the map.
As JavaScript moved to the server, that idea mostly disappeared. The JavaScript
ecosystem strongly prefers composition over cohesion. You choose a server
framework, then a database client, then validation, then sessions, then a
frontend, then a build tool, then glue it all together. Each piece may be
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
well. But web applications are not just pipelines of isolated tools. They are
full of shared assumptions: request shapes, validation boundaries, session
handling, rendering, routing, serialization, deployment targets. In practice,
things often fail in subtle ways exactly where those tools meet.
Meta-frameworks improved the situation, but only by straitjacketing the world.
Next gives you a coherent stack, but it is a React stack. If you discover Solid
and want to try it, you do not simply switch the view layer. You move to a
different meta-framework with different conventions, different routing details
and different backend assumptions.
The same pattern repeats across the ecosystem: filesystem routes plus one
blessed frontend, rebuilt again and again. React gets one meta-framework. Solid
gets another. Svelte gets another. Vue gets another. Each one solves mostly the
same backend problems, but in slightly different ways you have to relearn every
time.
Runtime fragmentation made this worse. Node, Deno, and Bun are all capable
JavaScript runtimes, but some frameworks are now effectively runtime-specific.
That means your app can be tied not only to a frontend, but also to a runtime.
And when a framework says it "supports" Deno or Bun, that often just means the
runtime is compatible enough to run Node code. It does not necessarily mean the
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.
Backends are ways to handle requests, data, and application logic. Runtimes are
execution targets. These things should be allowed to vary independently.
You should be able to write one route with React, another with Svelte, another
with Marko, and keep the same backend model.* You should be able to write backend
routes in TypeScript, Go, Ruby, or Python, and compose them inside one app. You
should be able to run the same app on Node, Deno, or Bun without changing a
single line of code or using runtime-specific packages to bridge the gap.
And the framework should still provide official support for validation, database
stores, sessions, i18n, routing, and rendering. That is what Primate is built
to be.
Primate is a web framework that does not belong to one frontend, one backend
language, or one runtime. It gives you one application model, then lets you
choose the pieces route by route. Use React where React makes sense. Use Svelte
where Svelte feels better. Use TypeScript for most routes, but Go, Ruby, or
Python where their ecosystems or runtime characteristics fit better. Run on
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.
You can still reach outside the official stack when you need to, but when you
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
frontends, no framework switch.
Create a project:
Configure the frontends:
Create a React route and collocated page:
Add another React route and page:
Add a Svelte route in the same app:
Run it:
Or, if you prefer Deno, use deno run -A npm:primate; for Bun, use bunx --bun primate.
This is the part that matters: the backend route model did not change. The app
did not become a React app or a Svelte app. It stayed a Primate app, and each
route chose the frontend that made sense for it.
- There are limits to this, even in Primate. Different frontend frameworks
cannot currently share layouts, because a layout is itself rendered by one
frontend. This may become possible later with web components.