Skip to content

Views

The wippy/views module provides a virtual page and component system with template rendering, resource management, and environment variable mapping. Pages come in two distinct flavors:

  • Jet template pages (kind: template.jet) — server-side rendered HTML. The page’s data and resources are assembled and injected server-side, then the Jet engine renders the final HTML. This is the legacy, server-rendered model. See Template Pages.
  • Registry-entry frontends (kind: registry.entry) — two kinds: micro frontend apps (view.page, full SPAs) and reusable web components (view.component), served from a CDN or static mount. The registry entry holds only routing and deployment policy; proxy/CSS injection is authored in the frontend package’s package.json. See Component Pages and View Components.

Add the module to your project:

Terminal window
wippy add wippy/views
wippy install

Declare the dependency:

version: "1.0"
namespace: app
entries:
- name: dep.views
kind: ns.dependency
component: wippy/views
version: "*"
parameters:
- name: api_router
value: app:api.public
- name: env_storage
value: app:env.storage
ParameterRequiredDefaultDescription
api_routeryesHTTP router for view API endpoints
env_storageyesEnvironment storage backing the PUBLIC_API_URL variable

Server-rendered model. Template pages are the legacy, server-side rendering mechanism: wippy/views assembles the page data and resources on the server and renders the final HTML with the Jet template engine. There is no iframe proxy and no client-side micro-frontend — the response is plain HTML. For external SPAs and components, see Component Pages.

Template pages render server-side using Jet templates. Data is injected via data.set, data.data_func, and data.resources (server-side resource injection):

entries:
- name: contact_page
kind: template.jet
meta:
type: view.page
name: contact
title: Contact Us
icon: mail
order: 5
group: main
group_icon: layout-grid
group_order: 1
announced: true
secure: false
data:
set: app.templates:default
data_func: app:contact_data
resources:
- contact_styles
FieldTypeDefaultDescription
meta.typestringMust be view.page
meta.namestringentry namePage identifier
meta.titlestringDisplay title
meta.iconstringIcon identifier
meta.ordernumber9999Sort order within group
meta.groupstringGroup category
meta.group_iconstringGroup icon
meta.group_ordernumber9999Group sort order
meta.group_placementstring"default"Placement: "default", "sidebar"
meta.securebooleanfalseRequires authentication
meta.publicbooleanfalsePublicly accessible
meta.announcedboolean= publicShow in navigation
meta.inlinebooleanfalseHidden from UI
meta.content_typestringtext/htmlResponse MIME type
meta.parentstringParent page ID
FieldDescription
data.setTemplate set registry ID
data.data_funcFunction ID that returns page data
data.resourcesArray of resource registry IDs

The data_func receives { params, query } and returns a table that becomes the data context in the template.

  1. Load page from registry
  2. Check access (security)
  3. Call data_func if defined
  4. Collect resources: globals + template set resources + page-specific resources
  5. Load environment variables
  6. Render Jet template with context: { data, resources, query_params, route_params, env }

Component pages point to external single-page applications (SPAs, micro-frontends) loaded by the Web Host inside an iframe. The registry entry holds only registry-routing and deployment-policy fields — URL serving, access control, mount route, and per-page config overrides:

Required registry shape: component pages are kind: registry.entry with meta.type: view.page. view.page is never a kind value. Proxy deployment overrides live at meta.proxy, not data.proxy.

entries:
- name: dashboard
kind: registry.entry
meta:
type: view.page
name: dashboard
title: Dashboard
icon: chart-bar
url: /app
base_path: app/dashboard
entry_point: index.html
mountRoute: /dashboard/:part(.*)*
secure: true
announced: true
config_overrides:
customization:
cssVariables:
"--p-primary": "#7c9ed9"

The API returns a component descriptor with the resolved base URL. The Web Host renders the SPA in an iframe and applies the proxy injections the frontend package requested.

FieldTypeDefaultDescription
meta.urlstringBase URL prefix where the bundle is mounted (CDN origin or http.static path)
meta.base_pathstringSubdirectory within the static mount
meta.entry_pointstringindex.htmlHTML entry file; combined as <url>/<base_path>/<entry_point>
meta.mountRoutestringClaims a URL path in the host router; only the catch-all form /:part(.*)* (root) or /<literal-prefix>/:part(.*)* is allowed — arbitrary Vue Router patterns are rejected (HTTP 500). See view-page.md / dynamic-routing.md
meta.announcedbooleanShow in navigation and pages/list
meta.securebooleanfalseRequires authentication
meta.config_overridesobjectPer-page AppConfig overrides (camelCase), deep-merged over the bundled defaults

Proxy injection for SPA pages is configured in the FE package.json wippy.proxy.injections block (camelCase) and baked into wippy-meta.json at build time. It can also be overridden per deployment via a camelCase proxy: block nested under meta: in the registry entry (same shape and injections wrapper as the package.json wippy.proxy block); the host deep-merges it over the bundled wippy.proxy, and the YAML value wins per nested key. There is no snake_case form and no casing normalization. Note that config_overrides only deep-merges customization, axiosDefaults, routePrefix, and apiRoutes — it never affects proxy.injections. See Micro Frontend Apps (view.page) and CSS Injection.

Minimal correct deployment override shape:

entries:
- name: dashboard
kind: registry.entry
meta:
type: view.page
proxy:
enabled: true
injections:
css:
themeConfig: true
customCss: true
customVariables: true
tailwindConfig: false

View components are reusable custom elements (web components, micro-frontends) that the Web Host discovers and registers — they are not pages and have no navigation entry. Like component pages, the registry entry carries only routing and deployment policy:

entries:
- name: reaction-bar
kind: registry.entry
meta:
type: view.component
name: reaction-bar
tag_name: example-reaction-bar
announced: true
auto_register: true
secure: false
url: /app/wc/reaction-bar
entry_point: index.js

Components use meta.type: view.component instead of view.page, identify themselves by meta.tag_name, and default to index.js as the entry point. Proxy injection and theme CSS for components are likewise authored in the FE package.json (camelCase) and, for shadow-DOM CSS, declared via hostCssKeys — not in the registry YAML. See Web Components (view.component) and CSS Injection.

Resources are CSS, JS, and font files associated with pages:

entries:
- name: global_styles
kind: registry.entry
meta:
type: view.resource
name: Global Styles
resource_type: style
global: true
order: 1
url: https://cdn.example.com/global.css
- name: app_script
kind: registry.entry
meta:
type: view.resource
name: App Script
resource_type: script
template_set: app.templates:default
order: 10
url: https://cdn.example.com/app.js
defer: true
FieldTypeDescription
meta.typestringMust be view.resource
meta.resource_typestringFree-form (defaults to "other"); common values are "style", "script", "font"
meta.ordernumberSort order within type
meta.globalbooleanApplied to all pages
meta.template_setstringSpecific to a template set
meta.urlstringResource URL
meta.integritystringSRI hash
meta.crossoriginstring"anonymous" or "use-credentials"
meta.mediastringCSS media query
meta.deferbooleanDeferred script loading
meta.asyncbooleanAsync script loading

Resources are collected in three layers, merged in order:

  1. Global resourcesglobal: true, applied to all pages
  2. Template set resources — matched by template_set ID
  3. Page resources — listed in data.resources array

Within each layer, resources are grouped by resource_type and sorted by order.

The env loader maps environment variables to template context keys through a priority-based system.

entries:
- name: app_env
kind: registry.entry
meta:
type: view.env_mapping
priority: 20
data:
mappings:
api_endpoint: API_BASE_URL
app_title: APP_NAME
debug_mode: DEBUG_ENABLED

Each mapping entry associates context keys (used in templates as env.api_endpoint) with environment variable names.

RangeCategoryDescription
0–9Framework defaultsBuilt-in framework mappings
10–19System overridesSystem-level configuration
20–29Application mappingsApplication-specific mappings
30–100Environment overridesRuntime overrides

Higher priority wins when multiple mappings define the same context key.

Resolved environment values are available in the env context object:

<script>
window.API_URL = "{{ env.api_endpoint }}";
document.title = "{{ env.app_title }}";
</script>

The views module registers these endpoints on the configured router:

MethodPathDescription
GET/pages/listList accessible, announced pages
GET/components/listList accessible, announced view components
GET/pages/content/{id}Render page or return component descriptor
GET/pages/public/{id}Get component base URL
GET/components/by-tag/{tag}Resolve a custom-element tag name to its view.component descriptor (used by host loadByTagName)
GET/pages/routesReturn the mountRoutepageId map; HTTP 500 on invalid or duplicate mountRoute. Not filtered by announced (hidden pages still need URL resolution); access control applies to secure pages

For template pages, returns rendered HTML with the page’s content_type.

For component pages, returns a descriptor:

{
"name": "dashboard",
"version": "1.0.0",
"specification": "wippy-component-1.0",
"title": "Dashboard",
"baseUrl": "https://cdn.example.com/dashboard/",
"wippy": {
"type": "page",
"path": "index.html",
"proxy": {
"enabled": true,
"injections": {
"css": { "themeConfig": true, "iframe": true },
"tailwindConfig": false,
"resizeObserver": true,
"preventLinkClicks": true
}
}
}
}

The css injection flags are themeConfig, iframe, primevue, markdown, customCss, and customVariables. There is no fonts flag — Google Fonts are delivered via theming.global.customCSS (an @import rule), injected by customCss.

Pages with secure: true require authentication. The page registry checks security.can("view", "page:<page_id>") against the current actor and scope.

Non-secure pages are always accessible. The announced flag controls visibility in navigation listings without affecting access.

Relative IDs in page definitions are qualified with the entry’s namespace:

# In namespace "app"
data:
data_func: my_data_func # resolves to app:my_data_func
set: templates:default # stays as templates:default (already qualified)
resources:
- page_styles # resolves to app:page_styles