The official Templ renderer adapter lives in pkg/renderers/templ.
Templ is the primary reference renderer over the Platform BFF model. It is
first-class for current product apps, but not privileged: it consumes the same
bff.v1 contract as JSON adapters and must not bypass BFF for permissions,
validation, action paths, or data shape.
| Property | Templ reference renderer | JSON adapters (React/Vite, etc.) |
|---|---|---|
| Input | bff.PageModel in-process (Go structs) |
ScreenModel / PageModel over HTTP JSON |
| Delivery | SSR/MPA HTML via /go-admin/* |
SPA/SSG/islands via /bff/* |
| Contract | Same bff.v1 fields and semantics |
Same bff.v1 fields and semantics |
| Bypass BFF? | No | No |
Platform core (pkg/bff, pkg/render, pkg/panelschema) does not import Templ.
Templ stays under pkg/renderers/templ and product pkg/templ paths.
| Layer | Input | Role |
|---|---|---|
| Product shell | bff.PageModel |
Shell, navigation, session chrome, embedded screen |
| Platform adapter | render.ScreenModel |
Generic table/form/detail/dashboard/kanban/error rendering |
Product apps wire:
registry.Page(ctx, path, principal) -> bff.PageModel
views.Page(page) -> HTML (shell + adapter.Screen(page.Screen))
Templ templates import github.com/fastygo/platform/pkg/bff for PageModel
only. They must not import internal/application, internal/storage, or
internal/delivery. Architecture tests enforce this boundary.
Templ templates and the Platform Templ adapter must not:
- perform permission or capability checks (BFF filters nav and action visibility);
- define validation rules or mutate records directly (BFF hydrator + executors own this);
- construct alternate action paths outside
metadata.form_actionand row-action descriptors; - access storage, repositories, or product domain services from renderer code;
- import product app or module internals from
pkg/templ; - invent a different screen data shape than
ScreenModel/PageModel.
Native HTML forms still submit to BFF action paths declared in the model:
- form
action→metadata.form_action(for examplePOST /bff/actions/post.update?id=post-rest); - row actions →
action.Pathwith optional runtimemetadata.action_token; - action executors live in product apps; Templ only renders descriptors.
Navigation is not hardcoded in templates; it renders page.Navigation.Items
from BFF. Session and capability UI use the same BFF projection as JSON clients
(/bff/session, capability fields on nav items and actions).
github.com/fastygo/templowns primitive and composite component APIs.pkg/renderers/templowns the schema-to-component mapping forScreenModel.- Product apps (
app-gocms,app-crm) own:pkg/templshell composition (nav frompage.Navigation, chrome frompage.Shell);- theme tokens, Tailwind configuration, static files;
pkg/appauth and route wiring.
pkg/bff,pkg/render, andpkg/panelschemastay free of Templ-specific props.
The current adapter renders:
- table screens from
ScreenModel.Columns(and rows when hydrated); - form screens from
ScreenModel.Fields; - detail screens as read-only form projections;
- dashboard screens as composition containers;
- kanban screens with grouping metadata (fallback surface);
- error screens from
ScreenModel.Error.
Unsupported view types should use ScreenModel.Fallback where possible.
Before React/Vite parity proof expands, Templ reference behavior is proven on:
| Proof | Product route | Fixture | BFF JSON |
|---|---|---|---|
| AppCMS posts list | /go-admin/posts |
appcms-posts-table.json |
/bff/screens/go-admin/posts |
| AppCMS post form | /go-admin/posts/post-rest/edit |
appcms-post-edit-form.json |
/bff/screens/go-admin/posts/post-rest/edit |
| AppCRM leads list | /go-admin/crm/leads |
appcrm-leads-table.json |
/bff/screens/go-admin/crm/leads |
| Navigation projection | — | bff-nav-proof.json |
/bff/nav |
| Session projection | — | bff-session-proof.json |
/bff/session |
| BFF-described actions | product executors | action-result-*.json |
POST /bff/actions/{actionId} |
Regression: pkg/conformance/templ_parity_test.go plus AppCMS/AppCRM
cmd/server/bff_parity_test.go.
The same ScreenModel Templ receives in-process is available at
GET /bff/screens/{path...} for JSON BFF clients. Contract tests assert
equality for proof screens (AppCMS posts, AppCRM leads, post edit form).
Consumer apps must include Tailwind sources for:
- their own
.templand generated*_templ.gofiles; - Platform renderer templates if they use the official adapter;
- Templ registry
uiandcomponentstemplates and generated files.
The adapter does not import UI8Kit CSS and does not ship product tokens.
Templ is the reference adapter, not a required runtime dependency. JSON, React, Vue, Svelte, quicktemplate, and other adapters consume the same BFF contract. UI8Kit is not required for AppCMS, AppCRM, or JSON BFF proof renderers.
Product apps may choose Templ without blocking future React/Vite parity: both renderers prove against the same golden fixtures.