YouBothAgent▾
You — Business rules and flows you own. Read these yourself.
Both — Know the idea; your agent follows the details.
Agent — Conventions and references your agent follows. Look up as needed.
Workspace▾
App & Library▾
Domain▾
Scalar▾
Model.View.tsx
A View file draws one record in full: the body of a detail page or a detail section. It takes the full model as a prop and only draws it.
Takes the full model
ticket: cnst.TicketEvery field is there, including long text and nested data that a list leaves out.
Only draws
Ticket.Util.* · Ticket.Unit.*It may render Units, Utils, Zones and its own subcomponents. Saving and deciding happen elsewhere.
Exports General
Ticket.View.GeneralGeneral is the main export. A long screen adds named sections beside it.Drawn through a Zone
renderView={(ticket) => …}A detail Zone hands the model from the server to it through
Load.View.Words used on this page
TermDescription
full model
The complete model class, such as
cnst.Ticket, with every field the constant declares.light model
A slimmer class, such as
cnst.LightTicket, holding only the fields a list needs.view payload
What
fetch.viewTicket(id) returns as ticketView: one record as plain data.hydrate
Filling the client store with data the server already fetched, so no second request is sent.
View vs Unit
Both files only draw a model. They differ in how much of the model they get and in the role they play on the page.
View · one record in fullFor one detail page or detail section.
- Model
- cnst.Ticket
- Export
- Ticket.View.General
- Props
- GeneralProps
- Drawn by
- Load.View → renderView
Unit · one item of manyFor list rows, cards and compact summaries.
- Model
- cnst.LightTicket
- Export
- Ticket.Unit.Card
- Props
- ModelProps<"ticket", cnst.LightTicket>
- Drawn by
- Load.Units → renderItem
- One record in detail is a View. It needs fields such as a long body, so it takes the full model.
- The same shape repeated is a Unit. A list sends many records at once, so each row gets the light model.
Standard View Shape
Every View file starts from the same skeleton. Here is the whole file for a ticket:
apps/koyo/lib/ticket/Ticket.View.tsx
- The main export is
General. Pages and Zones reach it asTicket.View.General. - Props are the full model plus a class name.
GeneralPropssits right above the component,classNamefirst, thenticket: cnst.Ticket. - The caller's class goes last.
cn("…", className)lets the page or Zone adjust width and spacing. - Every label goes through the dictionary. A field name is
l("ticket.status"). An enum value is keyed by the enum's name, so"active"readsl("ticketStatus.active").
Full Model Detail Patterns
A View receives the full model, not the light summary, so it can draw any field the constant declares on it. Plain text fields go straight into the markup:
apps/blog/lib/article/Article.View.tsx
An enum goes through its dictionary label, and a number is formatted where it is drawn:
apps/koyo/lib/order/Order.View.tsx
- Shared display logic goes on the Light model. A one-off
toLocaleString()stays in the View. A format a Unit needs too becomes a method onLightOrder, which the full model inherits. - A long screen gets named sections.
User.Viewinlibs/sharedexportsGeneralandDiscordinstead of one giant component. - A button inside is a Util.
User.View.GeneralrendersUser.Util.ChangePassword; the View places it, the Util owns the click.
Using View In Pages
A detail page starts the request with
fetch.view<Model>(id) and gives the view payload to a Zone. Whether you await the call decides when the section arrives:Destructure — streamed
const { ticketView } = fetch.viewTicket(id)The page markup is sent while the query runs. The section fills in behind its own boundary.
await — part of the shell
await Promise.all([fetch.viewTicket(id)])For when the page itself reads the model: a title, an id for a link, or a redirect decision.
Streamed
The usual detail page does not await, and hands the promise across as it is:
apps/koyo/page/ticket/[ticketId]/_index.tsx
- No
async, noawait. The render callback isasynconly when its body awaits. - The Zone takes the promise.
ClientViewaccepts a payload or its promise, andLoad.Viewshows a skeleton until it lands.
Awaited
When the page needs the record itself, await the call. It resolves to an object holding
ticket and ticketView:apps/koyo/page/ticket/[ticketId]/_index.tsx
ticketViewstill goes to the Zone. Already resolved, it renders in the first HTML with no loading state.ticketstays in the page. It is the hydrated model, for the link, a title or a redirect.- The page does not call
Load.Viewitself.renderViewis a function, and a server page cannot pass a function to a client component. The Zone sits between them for that reason.



Pass
ticketView to a Zone, never ticket. ticket is a class instance, and React Flight refuses a class instance as a client prop.Load.View And Store Hydration
Load.View puts the record from the view payload into the client store, then calls your renderView with the full model. A detail Zone is little more than this one call:apps/koyo/lib/ticket/Ticket.Zone.tsx
- Use it wherever server-fetched view data meets the store. A detail Zone, a tab layout or a reusable section all wrap the View this way.
- Waiting and empty states are built in. A pending promise shows
loading, a skeleton by default; an empty payload showsempty, an<Empty />by default.
What it writes to the store
Before the View renders,
Load.View sets four keys for the model:Store keyDescription
<model>
The full model instance, built from the payload's
<model>Obj.<model>Loading
Set to
false, so the View draws right away with no loading state.<model>Modal
Set to
"view", so a modal wrapper opens the record to read, not its edit form.<model>ViewAt
The
Date the server stamped on the payload, used to compare it with the store.- Newer store data wins. If the store already holds this record with a later
<model>ViewAt,Load.Viewkeeps the store's copy instead of the older payload. - Going back after a save loads the record again. If the navigation cache replays a payload from before the save,
Load.Viewfetches the record again withst.do.view<Model>(id).
Practical Rules
What belongs in a View, and which file takes everything else:
The work
View
*.View.tsx
Util
*.Util.tsx
Zone
*.Zone.tsx
page
page/**
Drawing — the View's job
fields and markup
✓
Titles, body text, nested data and formatted numbers from the full model.
l() · l.trans()
✓
Field names, enum values and headings come from the dictionary.
General · Discord
✓
A large View splits into named sections, as
User.View does, not one giant General.<Model>.Unit · <Model>.Util
✓
A View may render Units, Utils and Zones; each keeps its own job.
Behaviour — another file
onClick · submit
✓
A button or action is a Util the View renders, such as
User.Util.ChangePassword.useState · useEffect
✓
✓
Hooks need the browser, so they live in a Util or a Zone.
st.use · st.do
✓
✓
Store reads and writes. The store, signal and service do the actual mutation.
Load.View
✓
Hydrates the store from the view payload and hands the model to the View.
fetch.view<Model>
✓
Called in the route, so the query starts before the first byte is sent.
✓Belongs hereNot here


A View is a server file, and lint checks it. In a
*.View.tsx, a "use client" line, a React hook import such as useState, or an st import each fail akan lint.Related pages