Standards Docs
Concepts

Views

How to declare list and detail views for objects and mount them in React.

A view declares how an object is presented — a list/grid for collections, or a detail page for a single record. The schema declares views; the UI mounts them.

// @noverify
import { detailView, group, listView, viewRegistry } from "@stndrds/schema";

export const CONTACT_LIST = listView("contacts-list", "Contacts")
  .for("contacts")
  .default()
  .tab("all", "All contacts")
  .columns("firstName", "lastName", "email", "status")
  .sort("lastName", "asc")
  .default()
  .build();

export const CONTACT_DETAIL = detailView("contacts-detail", "Contact")
  .for("contacts")
  .default()
  .tab("general", "General")
  .form(group("identity", "Identity").fields("firstName", "lastName", "email"))
  .build();

viewRegistry.register(CONTACT_LIST);
viewRegistry.register(CONTACT_DETAIL);

Call viewRegistry.register() once per view, typically in a schema/index.ts entry point loaded at startup.

Detail vs list

listView targets a collection. Each tab carries its own layout (table or kanban), column set, filters, and sorts. The tab is where you pick what columns are visible and in what order.

detailView targets a single record. It organises attributes into tabs, with each tab containing one or more groups of fields. A detail view can also include tabs that show related records via tableFrom(), rich-text notes (.richtext()), documents (.documents()), and activity (.activity()).

Tabs and groups

// @noverify
detailView("contacts-detail", "Contact")
  .for("contacts")
  .default()
  .tab("general", "General")
  .form(
    group("identity", "Identity")
      .field("firstName", { span: 6 })
      .field("lastName", { span: 6 })
      .field("email", { span: 12 }),
    group("meta", "Details").fields("status", "company")
  )
  .tab("activity", "Activity")
  .activity()
  .build();

group(id, label) organises fields inside a form tab. Each field accepts a span (1–12 grid columns) and an optional readOnly flag. Tabs are added in declaration order; the first tab is shown by default unless you call .default() on another.

Mounting in React

// @noverify
import { ViewAwareRecordsView } from "@stndrds/ui";

export function ContactsPage() {
  return <ViewAwareRecordsView objectId="contacts" />;
}

ViewAwareRecordsView resolves the default list view for the object and renders it. The active tab is controlled by URL state — switching tabs updates the URL so the state is bookmarkable and shareable.

ViewAwareRecordsView requires SchemaClientProvider in the tree. See the React frontend guide for provider setup.

On this page