Summary
UI blocks describe pages, layouts, navigation, forms, and view fragments in Point source. Default apps (point.json runtime: "owned") render through owned runtime SSR — HTML strings from packages/point/runtime/ssr/ with no React or Vite in the author tree.
Legacy emit apps (--template full-stack-app, saas-app, vercel-app) still lower views to React-style components consumed by a generated TypeScript host — see Build and emit.
Owned runtime SSR (default)
point dev and point serve interpret view, page, layout, and navigation blocks directly. Authors stay in .point; the runtime owns HTTP, form POST handling, and link navigation.
Scaffold and run:
point create my-app
cd my-app
point dev src/app.pointview
See examples/view.point for syntax. Callable expressions in render and when ... render clauses produce dynamic content.
For legacy React hosts, use point build-ts when a generated TypeScript bundle consumes the output — not required for owned-runtime apps.
### Semantic styling
View nodes accept semantic style modifiers after render. The compiler maps them to shipped point-ui.css classes — authors stay in Point syntax with no separate stylesheet.
module Counter
view counter
input count: Int
when count > 0 render emphasized large "Counter ready"
render muted "Counter empty"Pages can style the main content region:
module Demo
view dashboard view
render "Dashboard body"
page demo page
title "Dashboard"
main render padded dashboardView()Forms accept layout modifiers:
module Demo
record Settings
name: Text
view settings form
input settings: Settings
input on settings change: Handler Settings
on change call on settings change
form compact
bind field "Name" to settings.nameModifiers: emphasized, muted, danger, success, large, small, compact, padded, centered, card, stack, badge, panel, spaced.
### Theme presets
Declare one theme block per module to apply accent, density, and radius tokens on layout roots and router mount wrappers:
module Demo
record Placeholder
id: Text
theme app theme
accent indigo
density comfortable
radius mediumAccents: indigo, emerald, rose, slate. Density: compact, comfortable. Radius: soft, medium, sharp.
Import styles in your web entry (full-stack apps):
@import "@hatchingpoint/point/ui/point-ui.css";Unknown modifiers produce a unknown-view-style diagnostic with a repair hint listing valid modifiers.
### Raw class escape hatch
View nodes still accept class "..." after render for custom CSS classes when semantic modifiers are not enough:
module Demo
view counter
input count: Int
when count > 0 render class "my-custom-class" "Counter ready"See examples/view.point for a minimal styled view.
### Data loading on mount
Views and pages can load data from an action on mount. The compiler generates the hook wiring — authors declare the action and binding only.
action fetch items
output result: Text
touches none
return "alpha, beta, gamma"
view items list
load data from action fetch items
when loading render "Loading items..."
when error render "Could not load items"
when empty render "No items yet"
render "Items: " + dataload data from action <name>oron mount call <name>binds the action result todatafor render expressions.load data from fetch GET "/api/items" field items type List<Item>fetches JSON over HTTP in the client (Path B full-stack apps).when loading render,when error render, andwhen empty renderare semantic state modifiers (optional).- Calling the load action directly in
renderwithoutawaitis amissing-awaitdiagnostic — use thedatabinding instead.
### Periodic refresh (polling)
After load data from action …, on mount call …, or load data from fetch …, add `refresh every <N> seconds` or `refresh every <N> minutes`.
On runtime-owned apps, the runtime wraps the view in a live region and polls the current page for updated HTML (no React). On legacy emit apps, the compiler emits setInterval refetches in the host bundle.
Multiple refresh every lines on one view are a duplicate-refresh-interval error; refresh every without a data-load binding is refresh-without-load.
Example: `examples/app/live-dashboard/live-dashboard.point`.
### Theme toggle
Declare `theme app theme` (or another name) with optional accent, density, and radius. Views can include `toggle theme`. Runtime-owned apps serve /point-ui.css and persist light/dark mode in localStorage.
### Live subscriptions (runtime-owned)
On default apps, `subscribe to sse`, `subscribe to stream`, and `terminal subscribe to stream` render owned-runtime client shells (EventSource / WebSocket) — no React emit. See Realtime.
See examples/app/dashboard/dashboard.point for a list view that loads from action fetch items.
### Rich components and lists
See examples/app/dashboard/dashboard.point and examples/full-stack-template/src/app.point for full apps. Syntax reference:
#### Links
view dashboard nav
link "Settings" to "/settings"
link "Items" to "/items"
render "Dashboard"#### Lists (each)
Requires a List input or the data binding from load data from action:
module DashboardApp
record Item
id: Text
title: Text
action fetch items
output items: List<Item>
touches none
return [{ id: "alpha", title: "Alpha" }]
view items list
load data from action fetch items
when loading render "Loading items..."
each item in data render link item.title to "/items/" + item.idOptional Tailwind on list rows: each item in data render class "text-sm" link item.title to "/path".
#### Forms
A form block groups controlled fields. Use bind field for text inputs and bind checkbox for booleans:
module DashboardApp
record WorkspaceSettings
workspace name: Text
notifications enabled: Bool
view settings form
input settings: WorkspaceSettings
input on settings change: Handler<WorkspaceSettings>
on change call on settings change
form
bind field "Workspace name" to settings.workspace name
bind checkbox "Email notifications" to settings.notifications enabled
render "Settings"#### Tabs
At least two tab lines inside a tabs block:
module DashboardApp
record WorkspaceSettings
workspace name: Text
theme: Text
view settings tabs
input settings: WorkspaceSettings
tabs
tab "General" render muted "Theme: " + settings.theme
tab "Advanced" render emphasized "Workspace-wide notification settings"Optional classes: tab "General" render class "font-bold" "General settings". Tabs also accept semantic style modifiers before the render expression, using the same modifier list as view render lines.
#### Modals
module DashboardApp
record WorkspaceSettings
notifications enabled: Bool
view settings modal hint
input settings: WorkspaceSettings
modal "Notifications enabled" when settings.notifications enabled render "Email alerts are active"Forms:
modal "Title" render expressionmodal "Title" when condition render expressionmodal "Title" render class "..." expression(optional Tailwind on the dialog shell)
#### Controlled inputs and Handler T
Views can declare callback props with Handler T or Handler<T> and bind controlled checkboxes to record fields:
record Item Flags
featured: Bool
view item editor
input flags: Item Flags
input on flags change: Handler<Item Flags>
on change call on flags change
bind checkbox "Featured" to flags.featured
render "Edit item"Handler Item Flags/Handler<Item Flags>— callback input for parent-controlled statebind checkbox "Label" to record.field— controlled checkbox bound to a record fieldon change call on flags changewires checkbox updates to the named callback input (optional when there is exactly oneHandlerinput)
### Embed in a host app
1. Run point build (or typed build — see Build and emit). 2. Import the generated module into your host app. 3. Ensure the app has React types (JSX.Element); add "jsx": "react-jsx" in tsconfig.json if needed. 4. Pass props from parent state for views with input bindings.
### Adopter example (interactive checklist)
examples/adopters/hatchingpoint/readiness-widget.point is a dogfood demo (App Store listing checklist) using the same controlled-input patterns. See the live demo or build locally:
point build examples/adopters/hatchingpoint/readiness-widget.point generated/readiness-widget.jslayout and navigation
App shells use named slots and a route registry:
module App
layout app shell
slot sidebar render "Nav"
slot main render "Select a page"
page settings page
layout app shell
title "Settings"
main render "Settings"
page members list page
layout app shell
title "Members"
main render "Members"
navigation dashboard app
path "/settings" page settings page
path "/members" page members list page
bootstrap routerPages bind to layouts with layout app shell, title, and main render. See examples/app/dashboard/dashboard.point.
page
Full-page shells for Next.js app routes. A page block wraps a title, optional description, and a main render slot in semantic HTML (<main>, <header>, <section>).
See examples/app/dashboard/dashboard.point for settings and list pages inside a layout shell.
Use view for embeddable fragments; use page when you want a document shell with title and main content regions. Pages forward Handler callback props to embedded views in main render call expressions.
Database in views
Load persisted data through actions — not vendor-specific hooks:
module Notes
action list notes
output rows: Text
touches database
return "[]"
view notes list
load data from action list notes
when loading render "Loading..."
render "Notes loaded"Wire real queries with std.sql or an external driver — see Database interop and examples/app/notes/notes.point.
Common mistakes
- Building SQL from concatenated user input — use parameterized actions only
- Calling load actions directly in
renderwithout thedatabinding
