Tern

module
v1.11.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 11, 2026 License: MIT

README

Tern

Svelte on the client. Go on the server.
A full-stack web framework with file-based routing, typed data loading and single-binary deploys.

CI Go Reference Docs

[!NOTE] Tern 1.0 is stable: minor releases do not break the public API. See the stability promise.


Why Tern?

SvelteKit has one of the best developer experiences on the web, but its server code runs on Node. Tern keeps the parts that make SvelteKit pleasant, such as file-based routes, load functions, form actions and layouts, and runs everything server-side in Go.

  • Real Svelte 5. Not a lookalike. Your components, the Svelte ecosystem and your editor tooling all keep working.
  • Go on the server. load functions and form actions are plain Go, built on net/http.
  • End-to-end types. The structs your Go Load returns become TypeScript types for your Svelte pages, generated automatically.
  • Server-side rendering. Pages render in Go through an embedded JavaScript engine and hydrate in the browser. No Node in production.
  • One binary. tern build embeds the client and the renderer into a single static Go executable.
  • Progressive enhancement. Forms work without JavaScript, and get better with it.

Getting started

You need Go 1.26+ and Node.js 22+.

npm create tern@latest

It asks where to put the project and whether to start bare or with a full app. A bare app is the minimal, blog or api template, optionally styled with Tern UI. The full app (saas) has accounts with passkeys and two-step sign-in, teams with invitations and roles, a dashboard, settings and per-team data in SQLite. Then it offers to create a git repository and install packages. Without npm, go run github.com/Unfield/Tern/cmd/tern@latest new does the same. Then:

cd my-app
npm run dev

Open http://localhost:5173. npm run build produces build/my-app, a single binary that serves the whole app.

A taste

src/routes/
├── +layout.svelte
├── +page.svelte
└── blog/slug.param/        # → /blog/[slug]
    ├── page.server.go
    └── +page.svelte
// src/routes/blog/slug.param/page.server.go
package page

import "github.com/Unfield/Tern/kit"

type Data struct {
	Title string `json:"title"`
	Body  string `json:"body"`
}

func Load(ctx *kit.Ctx) (Data, error) {
	post, err := posts.Find(ctx.Param("slug"))
	if err != nil {
		return Data{}, kit.NotFound()
	}
	return Data{Title: post.Title, Body: post.Body}, nil
}
<!-- src/routes/blog/slug.param/+page.svelte -->
<script lang="ts">
  import type { PageProps } from './$types';
  let { data }: PageProps = $props(); // fully typed from the Go struct
</script>

<h1>{data.title}</h1>
<article>{data.body}</article>

Dynamic directories use Go-safe names, because Go import paths cannot contain brackets: slug.param for [slug], lang.optional for [[lang]], path.rest for [...path], and app.group for (app). See examples/todos for a complete app with sessions, form actions, a protected route group and an API endpoint, examples/dashboard for a shadcn-svelte dashboard, examples/status for a self-hosted status page with live updates, background jobs and notifications, and examples/accounts for user accounts with passkeys.

Status

Milestone Scope State
v0.1 Router, loads, actions, endpoints, typegen, dev server, single-binary build Done
v0.2 Server-side rendering and hydration Done
v0.3 Prerendering, streaming, deployment guides Done
v0.4 Typed links, navigation hooks, selective reloading, CSP, a shadcn-svelte dashboard example, docs site Done
v1.0 Plugins, base paths, public environment variables, the stability promise Done
v1.1 Translations, responsive images, service workers, sessions, metrics, rate limiting and CORS plugins Done
v1.2 Markdown and content, realtime, jobs and migrations, OAuth/OIDC, email, storage, typed endpoints with OpenAPI, starter templates, experimental Go components Done
v1.3 Live components (Go component logic on the server, typed markup with tern check, several processes through a broker), the MCP server, Tern UI Done
v1.4 Form validation from Go struct tags, checked in the browser too; the complete Tern UI with charts, data tables and auth screens; tern ui add; npm create tern; an analytics example app Done
v1.5 Native rendering: tern build compiles the server renderer to Go, 12× faster than QuickJS on a 100-row table and faster than SvelteKit on a Node cluster Done
v1.6 Fully native rendering; user accounts with passkeys, email verification, password resets and signed-in devices; Tern's own texts translatable Done
v1.7 Two-step sign-in, sign-in with GitHub, Google or OIDC into accounts, email changes and rate limits; a faster JSON encoder and smaller binaries; render benchmarks in CI Done
v1.8 A full app to start from (tern new asks bare or full); native rendering phase 2: page data as Go values and Svelte's renderer and helpers in Go, 272 µs for the 100-row table Done
v1.9 Structured logging with sinks and a log view in tern dev; HTTPS from the app itself; security signals with TLS and HTTP fingerprints; tern deploy to servers, Fly.io and registries with zero-downtime restarts; tern.land Done
v1.10 Tracing with OTLP export and a waterfall in tern dev; error reports grouped by cause, from server and browser, to Sentry; testing loads, actions and whole apps from Go, with tests in every template Done
v1.11 Teams in accounts with invitations, roles and an audit log; billing with Stripe: plans, trials, seats and limits; team and pricing screens in Tern UI; the saas template with both Done

Documentation

Contributing

Contributions are welcome. Please read CONTRIBUTING.md and our Code of Conduct first. To report a security issue, follow SECURITY.md.

License

MIT © The Tern Authors

Tern is an independent project and is not affiliated with or endorsed by the Svelte team.

Directories

Path Synopsis
Package accounts signs users up and in, with passwords or passkeys, and keeps their accounts: email verification, password resets, and the devices they're signed in on, any of which they can sign out.
Package accounts signs users up and in, with passwords or passkeys, and keeps their accounts: email verification, password resets, and the devices they're signed in on, any of which they can sign out.
Package billing charges for an app with Stripe: plans defined in Go, Stripe Checkout to subscribe, the customer portal to change or cancel, trials and seats, and limits per plan checked in loads and actions.
Package billing charges for an app with Stripe: plans defined in Go, Stripe Checkout to subscribe, the customer portal to change or cancel, trials and seats, and limits per plan checked in loads and actions.
cmd
tern command
Command tern is the command-line interface for the Tern framework.
Command tern is the command-line interface for the Tern framework.
tern/templates/api/internal/store
Package store keeps todos in memory.
Package store keeps todos in memory.
tern/templates/blog/internal/blog
Package blog reads the posts in src/content/posts.
Package blog reads the posts in src/content/posts.
tern/templates/saas/internal/app
Package app holds what the routes share: the database, user accounts and their teams, billing, email and background jobs.
Package app holds what the routes share: the database, user accounts and their teams, billing, email and background jobs.
tern/templates/saas/migrations
Package migrations holds the app's database migrations, applied in order when the app starts (see the migrate package).
Package migrations holds the app's database migrations, applied in order when the app starts (see the migrate package).
Package content loads Markdown collections from an app's src/content directory, with typed YAML front matter:
Package content loads Markdown collections from an app's src/content directory, with typed YAML front matter:
Package docs embeds Tern's documentation, so tools such as tern mcp can serve the docs for the version in use.
Package docs embeds Tern's documentation, so tools such as tern mcp can serve the docs for the version in use.
Package goscript is the Go side of Tern's <script lang="go">: component logic written in Go with Svelte-style runes, while the markup stays Svelte.
Package goscript is the Go side of Tern's <script lang="go">: component logic written in Go with Svelte-style runes, while the markup stays Svelte.
livetest
Package livetest runs live components in Go tests, without a browser:
Package livetest runs live components in Go tests, without a browser:
internal
batch
Package batch sends items to an exporter in batches, from a goroutine, with a bounded queue and retries: what logging's sinks and tracing's exporters share.
Package batch sends items to an exporter in batches, from a goroutine, with a bounded queue and retries: what logging's sinks and tracing's exporters share.
codegen
Package codegen generates the code that connects a Tern app's routes to the runtime: tern.gen.go for the Go server, the client route manifest for Vite, and TypeScript types for every route.
Package codegen generates the code that connects a Tern app's routes to the runtime: tern.gen.go for the Go server, the client route manifest for Vite, and TypeScript types for every route.
dbtest
Package dbtest opens the databases that Tern's database packages are tested against: a fresh SQLite file always, and PostgreSQL when TERN_TEST_POSTGRES holds a connection string (CI sets it).
Package dbtest opens the databases that Tern's database packages are tested against: a fresh SQLite file always, and PostgreSQL when TERN_TEST_POSTGRES holds a connection string (CI sets it).
formrules
Package formrules parses validation tags and checks submitted form values against them.
Package formrules parses validation tags and checks submitted form values against them.
htmltext
Package htmltext turns HTML into readable plain text, for the text part of emails.
Package htmltext turns HTML into readable plain text, for the text part of emails.
i18n
Package i18n formats translated messages.
Package i18n formats translated messages.
jsondiff
Package jsondiff computes small patches between two JSON values, so live components send what changed in a list instead of the whole list.
Package jsondiff computes small patches between two JSON values, so live components send what changed in a list instead of the whole list.
jsonenc
Package jsonenc encodes values as encoding/json does, byte for byte, except that a nil slice is written as [] instead of null: Tern sends [] for pages, typed endpoints and live components, so their TypeScript types are T[] rather than T[] | null.
Package jsonenc encodes values as encoding/json does, byte for byte, except that a nil slice is written as [] instead of null: Tern sends [] for pages, typed endpoints and live components, so their TypeScript types are T[] rather than T[] | null.
jsvalue
Package jsvalue makes native rendering's JavaScript values straight from Go values: what JSON.parse would make of the JSON internal/jsonenc writes for them, value for value and in the same key order, without writing or parsing the JSON.
Package jsvalue makes native rendering's JavaScript values straight from Go values: what JSON.parse would make of the JSON internal/jsonenc writes for them, value for value and in the same key order, without writing or parsing the JSON.
markdown
Package markdown renders Markdown with YAML front matter.
Package markdown renders Markdown with YAML front matter.
mcp
Package mcp is a small Model Context Protocol server over stdio: tools and resources, as JSON-RPC 2.0 messages, one per line.
Package mcp is a small Model Context Protocol server over stdio: tools and resources, as JSON-RPC 2.0 messages, one per line.
native
Package native compiles the modules of an app's server build to Go, so pages render without the JavaScript engine (ADR 0011).
Package native compiles the modules of an app's server build to Go, so pages render without the JavaScript engine (ADR 0011).
nilslices
Package nilslices makes nil slices empty before encoding/json sees them.
Package nilslices makes nil slices empty before encoding/json sees them.
routes
Package routes discovers Tern routes on the filesystem and matches URLs against them.
Package routes discovers Tern routes on the filesystem and matches URLs against them.
seal
Package seal encrypts and authenticates small values, such as cookie contents, with AES-256-GCM.
Package seal encrypts and authenticates small values, such as cookie contents, with AES-256-GCM.
ssr
Package ssr renders Svelte pages on the server by running the app's server bundle in embedded QuickJS VMs.
Package ssr renders Svelte pages on the server by running the app's server bundle in embedded QuickJS VMs.
telemetry
Package telemetry is what Tern's logging and tracing exporters share: sending a batch over HTTP with gzip, status codes mapped to retries and Retry-After, and OTLP's JSON encoding.
Package telemetry is what Tern's logging and tracing exporters share: sending a batch over HTTP with gzip, status codes mapped to retries and Retry-After, and OTLP's JSON encoding.
ui
Package ui formats the Tern CLI's terminal output: colors, symbols, spinners and tables.
Package ui formats the Tern CLI's terminal output: colors, symbols, spinners and tables.
version
Package version reports the version of the Tern build.
Package version reports the version of the Tern build.
Package jobs runs background work: durable jobs with retries, stored in SQLite or PostgreSQL, and recurring schedules.
Package jobs runs background work: durable jobs with retries, stored in SQLite or PostgreSQL, and recurring schedules.
kit
Package kit is the server runtime for Tern applications.
Package kit is the server runtime for Tern applications.
kittest
Package kittest tests Tern apps from Go: call a load, an action or an endpoint with a request made up for the test, and check what it returns; or run the whole app and query the HTML its pages render.
Package kittest tests Tern apps from Go: call a load, an action or an endpoint with a request made up for the test, and check what it returns; or run the whole app and query the HTML its pages render.
Package logging is structured logging for Tern apps, on log/slog.
Package logging is structured logging for Tern apps, on log/slog.
datadog
Package datadog is a logging sink that sends records to Datadog's log intake (HTTP API v2):
Package datadog is a logging sink that sends records to Datadog's log intake (HTTP API v2):
file
Package file is a logging sink that writes records to a file as JSON lines, rotating it by size:
Package file is a logging sink that writes records to a file as JSON lines, rotating it by size:
internal/sinktest
Package sinktest has helpers for the sinks' tests.
Package sinktest has helpers for the sinks' tests.
loki
Package loki is a logging sink that pushes records to Grafana Loki:
Package loki is a logging sink that pushes records to Grafana Loki:
otlp
Package otlp is a logging sink that sends records to an OpenTelemetry collector, or any service that takes OTLP over HTTP (Grafana Cloud, Honeycomb, New Relic, SigNoz, Axiom…), as OTLP/HTTP JSON:
Package otlp is a logging sink that sends records to an OpenTelemetry collector, or any service that takes OTLP over HTTP (Grafana Cloud, Honeycomb, New Relic, SigNoz, Axiom…), as OTLP/HTTP JSON:
Package mail sends email, written as Svelte components in src/emails:
Package mail sends email, written as Svelte components in src/emails:
Package migrate applies SQL migrations to SQLite or PostgreSQL.
Package migrate applies SQL migrations to SQLite or PostgreSQL.
native
js
Package js is a Go model of JavaScript values with the language's exact semantics, for components that tern build compiles to Go (ADR 0011).
Package js is a Go model of JavaScript values with the language's exact semantics, for components that tern build compiles to Go (ADR 0011).
svelte
Package svelte holds what compiled components need from Go beyond the JavaScript they were compiled from: Go versions of Svelte's hottest server helpers (intrinsics).
Package svelte holds what compiled components need from Go beyond the JavaScript they were compiled from: Go versions of Svelte's hottest server helpers (intrinsics).
plugins
compress
Package compress gzips a Tern app's dynamic responses (pages, data, endpoint JSON) for clients that accept it:
Package compress gzips a Tern app's dynamic responses (pages, data, endpoint JSON) for clients that accept it:
cors
Package cors lets other origins call a Tern app's endpoints:
Package cors lets other origins call a Tern app's endpoints:
health
Package health adds liveness and readiness endpoints to a Tern app:
Package health adds liveness and readiness endpoints to a Tern app:
images
Package images resizes remote images for <Image src="https://…">:
Package images resizes remote images for <Image src="https://…">:
metrics
Package metrics exposes Prometheus metrics for a Tern app, with no dependencies:
Package metrics exposes Prometheus metrics for a Tern app, with no dependencies:
oauth
Package oauth adds "Sign in with GitHub, Google or any OpenID Connect provider" to a Tern app:
Package oauth adds "Sign in with GitHub, Google or any OpenID Connect provider" to a Tern app:
oidc
Package oidc makes a Tern app an OAuth 2.0 authorization server and OpenID Connect provider: other apps (or your own frontends, mobile apps and services) sign users in with it, and your APIs accept its tokens.
Package oidc makes a Tern app an OAuth 2.0 authorization server and OpenID Connect provider: other apps (or your own frontends, mobile apps and services) sign users in with it, and your APIs accept its tokens.
ratelimit
Package ratelimit limits how often each client can call a Tern app:
Package ratelimit limits how often each client can call a Tern app:
requestlog
Package requestlog logs every request a Tern app serves, with slog:
Package requestlog logs every request a Tern app serves, with slog:
secure
Package secure sets security headers on every response of a Tern app:
Package secure sets security headers on every response of a Tern app:
sessions
Package sessions stores per-user data in an encrypted cookie:
Package sessions stores per-user data in an encrypted cookie:
signals
Package signals tells people from bots and recognizes devices, from what the server sees of each request: its TLS ClientHello (JA4), its HTTP/2 settings and the order of its headers, how its headers fit the browser its User-Agent claims, and, optionally, a client check in the browser with a proof of work.
Package signals tells people from bots and recognizes devices, from what the server sees of each request: its TLS ClientHello (JA4), its HTTP/2 settings and the order of its headers, how its headers fit the browser its User-Agent claims, and, optionally, a client check in the browser with a proof of work.
Package rbac checks permissions by role:
Package rbac checks permissions by role:
Package realtime is a publish/subscribe hub for pushing updates to browsers over server-sent events or WebSockets:
Package realtime is a publish/subscribe hub for pushing updates to browsers over server-sent events or WebSockets:
pgbroker
Package pgbroker is a [realtime.Broker] on PostgreSQL's LISTEN and NOTIFY, so an app running several processes delivers realtime messages and live component updates to all of them without another service:
Package pgbroker is a [realtime.Broker] on PostgreSQL's LISTEN and NOTIFY, so an app running several processes delivers realtime messages and live component updates to all of them without another service:
Package report turns an app's unexpected errors and panics, on the server and in the browser, into reports grouped by cause, and sends them to an error tracker such as Sentry:
Package report turns an app's unexpected errors and panics, on the server and in the browser, into reports grouped by cause, and sends them to an error tracker such as Sentry:
sentry
Package sentry is a report sink for Sentry, and for the services that take its protocol, such as GlitchTip and Bugsink:
Package sentry is a report sink for Sentry, and for the services that take its protocol, such as GlitchTip and Bugsink:
Package sqldb holds the small amount of SQL dialect knowledge shared by Tern's database packages (migrate and jobs).
Package sqldb holds the small amount of SQL dialect knowledge shared by Tern's database packages (migrate and jobs).
Package storage keeps files: uploads, generated exports, anything that doesn't belong in the database.
Package storage keeps files: uploads, generated exports, anything that doesn't belong in the database.
Package tracing records what a request does as a tree of timed spans, and exports them to OpenTelemetry collectors and the services that take OTLP, such as Grafana Tempo, Honeycomb, Jaeger or Datadog:
Package tracing records what a request does as a tree of timed spans, and exports them to OpenTelemetry collectors and the services that take OTLP, such as Grafana Tempo, Honeycomb, Jaeger or Datadog:
otlp
Package otlp exports spans to an OpenTelemetry collector, or any service that takes OTLP over HTTP (Grafana Tempo, Honeycomb, Jaeger, SigNoz, Datadog, New Relic…), as OTLP/HTTP JSON:
Package otlp exports spans to an OpenTelemetry collector, or any service that takes OTLP over HTTP (Grafana Tempo, Honeycomb, Jaeger, SigNoz, Datadog, New Relic…), as OTLP/HTTP JSON:

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL