primate
Web framework focused on flexibility and developer freedom
Mix and match the best web tech, in one stack
backend
Write backend code in your language of choice, leveraging the power of Wasm. Mix routes of different backend languages, allowing your application to be written by different teams.
import view from "primate/handler/view";
const posts = [{
id: 1,
title: "First post",
}];
export default {
get() {
return view("PostIndex.jsx", { posts });
},
};
import type { Route } from "primate";
import view from "primate/handler/view";
const posts = [{
id: 1,
title: "First post",
}];
export default {
get() {
return view("PostIndex.jsx", { posts });
},
} satisfies Route;
import "github.com/primatejs/go/primate"
func Get(request Request) any {
posts := Array{Object{
"id": 1,
"title": "First post",
}};
return primate.View("PostIndex.jsx", Object{ "posts": posts });
}
def get(request):
posts = [{
"id": 1,
"title": "First post",
}]
return Primate.view("PostIndex.jsx", { "posts": posts })
def get(request)
posts = [{
id: 1,
title: "First post",
}]
Primate.view("PostIndex.jsx", { posts: posts })
end
frontend
Seamlessly switch between frontend frameworks, with support for SSR, hydration and layouts across the board. You can even combine more than one framework in your application.
export default ({ posts }) => {
return (<>
<h1>All posts</h1>
{posts.map(post => (
<h2 key={post.id}>
<a href={`/post/${post.id}`}>
{post.title}
</a>
</h2>
))}
</>);
};
<script>
export let posts;
</script>
<h1>All posts</h1>
{#each posts as post}
<h2>
<a href="/post/{post.id}">
{post.title}
</a>
</h2>
{/each}
<template>
<h1>All posts</h1>
<div v-for="post in posts">
<h2>
<a :href="`/post/${post.id}`">
{{post.title}}
</a>
</h2>
</div>
</template>
import { For } from "solid-js/web";
export default ({ posts }) => {
return <>
<h1>All posts</h1>
<For each={posts}>{post =>
<h2>
<a href={`/post/${post.id}`}>
{post.title}
</a>
</h2>
}</For>
</>;
};
import { Component, Input } from "@angular/core";
import { CommonModule } from "@angular/common";
@Component({
selector: "post-index",
imports: [CommonModule],
template: `
<h1>All posts</h1>
<div *ngFor="let post of posts">
<h2>
<a href="/post/{{post.id}}">
{{post.title}}
</a>
</h2>
</div>
`,
standalone: true,
})
export default class PostIndex {
@Input() posts = [];
}
<h1>All posts</h1>
${posts.map(post => `
<h2>
<a hx-get="/post/${post.id}" href="/post/${post.id}">
${post.title}
</a>
</h2>
`).join("")}
<script>
import Component from "@primate/webc/Component";
export default class PostIndex extends Component {
render() {
const { posts } = this.props;
return `<h1>All posts</h1>
${posts.map(post => `
<h2>
<a href="/post/${post.id}">
${post.title}
</a>
</h2>
`).join("")}
`;
},
};
</script>
runtime
Compare the performance of your application across different JavaScript runtimes. Use the comfort of one runtime during development and the speed gains of another in production.
npx -y primate@latest
deno run --allow-all npm:primate
bun --bun x primate
extensive, officially supported ecosystem
data handling
Validate input using Primate schemas. Persist information with stores, using any of the supported database drivers with a unified ORM interface, or write your own optimized, low-level store actions. Primate's ORM comes with automated transaction management and rollback on error, saving you writing boilerplate code in your application routes.
internationalization
Easily make your application international, using a unified API across different frontends with placeholder support and a built-in language switcher.
import t from "@primate/react/i18n";
import locale from "@primate/react/locale";
export default function ({ username }) {
return <>
<h1>{t("welcome", { username })}</h1>
<p>{t("message")}</p>
{t("bye")}~
<h3>{t("switch-language")}</h3>
<div>
<a onClick={() => locale.set("en-US")}>
{t("English")}
</a>
</div>
<div>
<a onClick={() => locale.set("de-DE")}>
{t("German")}
</a>
</div>
</>;
}
<script>
import t from "@primate/svelte/i18n";
import locale from "@primate/svelte/locale";
export let username;
</script>
<h1>{$t("welcome", { username })}</h1>
<p>{$t("message")}</p>
{$t("bye")}~
<h3>{$t("switch-language")}</h3>
<div>
<a on:click={() => locale.set("en-US")}>
{$t("English")}
</a>
</div>
<div>
<a on:click={() => locale.set("de-DE")}>
{$t("German")}
</a>
</div>
import t from "@primate/solid/i18n";
import locale from "@primate/solid/locale";
export default function ({ username }) {
return <>
<h1>{t("welcome", { username })}</h1>
<p>{t("message")}</p>
{t("bye")}~
<h3>{t("switch-language")}</h3>
<div>
<a onClick={() => locale.set("en-US")}>
{t("English")}
</a>
</div>
<div>
<a onClick={() => locale.set("de-DE")}>
{t("German")}
</a>
</div>
</>;
}
all around
Use esbuild for hot reload during development and bundling in production, add user sessions or write your own modules using the available hooks.
more than all the rest, combined
Feature | Next | Nuxt | SvelteKit | Analog | Primate |
---|---|---|---|---|---|
Backend | |||||
JavaScript | ✓ | ✓ | ✓ | ✗ | ✓ |
TypeScript | ✓ | ✓ | ✓ | ✓ | ✓ |
Go | ✗ | ✗ | ✗ | ✗ | ✓ |
Python | ✗ | ✗ | ✗ | ✗ | ✓ |
Ruby | ✗ | ✗ | ✗ | ✗ | ✓ |
Frontend | |||||
React | ✓ | ✗ | ✗ | ✗ | ✓ |
Vue | ✗ | ✓ | ✗ | ✗ | ✓ |
Svelte | ✗ | ✗ | ✓ | ✗ | ✓ |
Angular | ✗ | ✗ | ✗ | ✓ | ✓ |
Solid | ✗ | ✗ | ✗ | ✗ | ✓ |
Web Components | ✗ | ✗ | ✗ | ✗ | ✓ |
HTML | ✗ | ✗ | ✗ | ✗ | ✓ |
HTMX | ✗ | ✗ | ✗ | ✗ | ✓ |
Handlebars | ✗ | ✗ | ✗ | ✗ | ✓ |
Markdown | ✗ | ✗ | ✗ | ✗ | ✓ |
Marko | ✗ | ✗ | ✗ | ✗ | ✓ |
Eta | ✗ | ✗ | ✗ | ✗ | ✓ |
Voby | ✗ | ✗ | ✗ | ✗ | ✓ |
Native runtime | |||||
Node | ✓ | ✓ | ✓ | ✓ | ✓ |
Deno | ✗ | ✗ | ✗ | ✗ | ✓ |
Bun | ✗ | ✗ | ✗ | ✗ | ✓ |
Data stores / ORM | |||||
SQLite | ✗ | ✗ | ✗ | ✗ | ✓ |
MongoDB | ✗ | ✗ | ✗ | ✗ | ✓ |
PostgreSQL | ✗ | ✗ | ✗ | ✗ | ✓ |
MySQL | ✗ | ✗ | ✗ | ✗ | ✓ |
SurrealDB | ✗ | ✗ | ✗ | ✗ | ✓ |
Ecosystem | |||||
I18N | ✓ | ✓ | ✗ | ✗ | ✓ |
Head Component | ✓ | ✓ | ✗ | ✗ | ✓ |
Route guards | ✗ | ✗ | ✗ | ✗ | ✓ |
Recursive layouts | ✓ | ✓ | ✓ | ✗ | ✓ |
WebSockets | ✗ | ✗ | ✗ | ✗ | ✓ |
Server-sent events | ✗ | ✗ | ✗ | ✗ | ✓ |
User sessions | ✗ | ✓ | ✗ | ✗ | ✓ |