Primate LogoPrimate

Frontends

Primate supports multiple frontend frameworks and template engines. Use one or mix them in the same app.

Overview

Frontend SSR Hydration CSR Layouts Head Forms i18n
Reactive frontends
Angular
React
Solid
Svelte
Vue
Marko
Component-based frontends
HTML
HTMX
Voby
Web Components
Template engines
Eta
Handlebars
Markdown

For programmatic browser navigation and current-route refresh, see Client navigation. For mutation patterns, dynamic paths, validation errors, and troubleshooting, see Client forms.

Features

Development rendering

For frontends with client rendering, Primate renders in the browser during development by default to keep live reload builds small and fast. Production uses server-side rendering by default. Pass ssr: true to the frontend module to also use server-side rendering during development, or ssr: false to disable it in every mode.

If csr: false is set and ssr is omitted, Primate enables SSR in development so the page is not blank. Setting both csr: false and ssr: false explicitly disables both rendering paths and therefore produces an empty app container.

Development keeps packages external for fast rebuilds. Dependencies used by server-rendered views must therefore expose JavaScript that the runtime can load directly.

Reactive frontends

Angular, React, Solid, Svelte, Vue

State-driven UI with SSR, hydration, routing, layouts, validation, and i18n in one model. Suited for complex state and frequent interactions.

Component-based frontends

HTML, HTMX, Marko, Voby, Web Components

Components without CSR. Works well for server-driven pages and simple dashboards with minimal client JS.

Template engines

Handlebars, Eta, Markdown

Static rendering for content-focused pages and templates. Good for docs, blogs, emails, and CMS output.

Quickstart

Install a frontend module:

npm install @primate/react react react-dom

Configure it:

// config/app.ts
import config from "primate/config";
import react from "@primate/react";

export default config({
  modules: [react()],
});

Create a route page next to your route:

// routes/index.tsx
export default function Welcome({ name }: { name: string }) {
  return <h1>Hello, {name}!</h1>;
}

Render it from the route:

// routes/index.ts
import response from "primate/response";
import route from "primate/route";

export default route({
  get() {
    return response.page({ name: "World" });
  },
});

Mixing frontends

Multiple frontends can coexist; each handles its own files.

import config from "primate/config";
import react from "@primate/react";
import markdown from "@primate/markdown";
import htmx from "@primate/htmx";

export default config({
  modules: [react(), markdown(), htmx()],
});

Hydrated links can navigate between reactive frontend modules without replacing the document. Primate disposes the outgoing adapter before mounting the destination adapter. For independently built client partitions, the destination assets and inactive styles are loaded before this handoff; see Client partitions.

Forms with multiple actions

Prefer a separate form for each mutation. If controls must share a form, store the selected operation in an explicit hidden field:

<form method="post">
  <input name="title" />
  <input type="hidden" name="intent" value="draft" />
  <button>Save draft</button>
</form>

Update and validate the intent deliberately rather than relying only on the browser's selected submitter. See Client forms for the full decision guide.

Previous
Modules
Next
Navigation