@samveys/view

Version
1.0.2Nov 5, 2026
Install
pnpm add @samveys/view

From packages/view/README.md in the framework repository.

The view contract: one ViewEngine interface, an engine registry and ctx.view. No runtime dependencies.

EnginePackage
.tpl.html templates@samveys/template
Vue server rendering@samveys/vue
Inertia root view@samveys/inertia (renders through the template engine)

Set up

Install view() first, then an engine plugin.

ts
import { http } from '@samveys/http'
import { template } from '@samveys/template'
import { view } from '@samveys/view'

export const plugins = [http(), view(), template()]

view({ engine: 'vue' }) picks the active engine when several are installed. Without it, the first engine registered is used.

Render from a handler

ts
import type { Router } from '@samveys/http'

export function routes(router: Router): void {
  router.get('/users', async (ctx) => {
    ctx.view.share('appName', 'Samveys')
    return ctx.view.render('users.index', { title: 'Users' }, { status: 200 })
  })
}
  • ctx.view.render(name, data, init?) returns a Response with content-type: text/html; charset=utf-8.
  • ctx.view.share(key, value) adds data for this request only. Data given to render wins over shared data.
  • Every render is published on the samveys:view.render tracing channel.
  • Rendering without an installed engine, or with an unknown one, is SVS_E109.

Write an engine

ts
import type { ViewEngine } from '@samveys/view'

export const upper: ViewEngine = {
  async render(name, data) {
    return `${name}: ${JSON.stringify(data)}`.toUpperCase()
  },
}

Register it from a plugin with container.make('view.registry').register('upper', upper). The render third argument carries the request (ctx.http) so an engine can read the CSRF token.

ts
import { definePlugin } from '@samveys/core'
import { upper } from './upper.ts'

export const upperPlugin = definePlugin({
  name: 'upper-engine',
  requires: { plugins: ['@samveys/view'] },
  boot: ({ container }) => container.make('view.registry').register('upper', upper),
})