Service.Util.tsx

Service.Util.tsx holds a small client control, such as a button, that runs one of a service module's endpoints. Not one of the eight service modules in this workspace has this file, and that is by design, not a gap waiting to be filled.
This page explains why the file is rare, where the control goes instead, and what it takes for yours to be the exception.
Words used on this page
A lib/_<name> folder with no model: a service, a signal, a dictionary and often a store.
A lib/<model> folder built around one stored model. Its Util acts on that model's records.
The file role for a small control, such as a button, that runs an endpoint.
A file that starts with "use client". It arrives as HTML, then again as JS the browser re-runs.
The app or lib folder for components that render JSX and are not bound to one model.
Why it is rare
Model Module: Verb And Noun
A Util is named for the endpoint verb minus the noun: Serve, Refund, Complete. The button runs the module's own endpoint on the module's own record, so it belongs there.
Service Module: Verb Only
It has endpoints but no model, so there is no record for the control to belong to. The control usually belongs to the screen that offers it, not to the capability behind it.
Where the control goes
Start from what the control is bound to. Most controls land in ui/ or page/, and only the last row earns a Util:
The control is
ui/
page/
.Util.tsx
Usually
Not bound to one model
✓
A disconnect button, a permission prompt, a map control. The service store only drives it.
A screen of its own
✓
The OAuth consent screen is a route in libs/shared/page/oauth, not a component.
Rarely
Meaningless outside this module
✓
It reads this store and calls this endpoint. In ui/ it would import the module back in.
✓Goes hereNot here
  • ui/ is the default. Rendering JSX without being bound to one model is the ui/ admission test, word for word. The map in libs/util/ui/MapView reads the _util store this way.
  • A screen of its own is a route. _oauth ships ten endpoints and zero components, because its one screen is the consent route.
  • A Util only when all three hold. It reads this store, calls this endpoint, and moving it to ui/ would mean importing the module back in.

The Shape, If You Write One

A Util is always a client component, so "use client" goes on line 1, above the imports. Its export is a role name, and in a service module that role is the endpoint's verb.
A receipt module's print button, which runs the printReceipt endpoint:
apps/koyo/lib/_receipt/Receipt.Util.tsx
The rules in the file
"use client"
Line 1, above the imports, in every .Util.tsx. A Util is always a client component.
Print
The endpoint printReceipt minus its noun. Callers write <Receipt.Util.Print>.
interface PrintProps
Sits right above the component with className first, and is not exported.
icecreamOrderId: string
An id, not the order. A cnst model prop arrives on the client as a plain object, methods stripped.
Publishes the button to the in-page agent, so a click and the agent run one handler.
The label comes from the module's dictionary, never from a string literal.

Two Component Roles

A service module folder has exactly two component roles: Service.Util.tsx and Service.Zone.tsx. There is no Template, no Unit and no View.
Role
model module
service module
Roles that need a model
.Template.tsx
✓
Binds to a model's form state.
.Unit.tsx
✓
Renders one light model, such as a list card.
.View.tsx
✓
Renders one full model, such as a detail screen.
Roles that need no model
.Util.tsx
✓
✓
One client control.
.Zone.tsx
✓
✓
One client section a page drops in whole.
✓AllowedNot allowed
  • The missing three all need a model. A service module has no form state to bind and no light or full model to render.
  • Both remaining roles are client components. What is left is one client control and one client section, each with "use client" on line 1.
Related pages

Released under the MIT License

Connect your AI to these docs

MCPhttps://akanjs.com/mcp
Copyright © 2026 Akan.js All rights reserved.System managed bybassman