[data-reveal]{opacity:1!important;transform:none!important}
Best practices
AI & coding agents

Best practices

The rules every DMS page follows, and that the dms-best-practices skill makes agents follow — pages as backend blocks, custom only for the missing block, metadata on every custom block, and a custom block proposed rather than built silently.

Three rules keep a DMS page maintainable, permissionable and editable by the page builder and by agents. The dms-best-practices skill, shipped with @antelopejs/dms, makes coding agents such as Claude Code and Codex, and the dms-ai assistant, apply them on every page they build.

1. A page is always described as blocks

Describe every page on the backend as a tree of DMS blocks: a toolbar, a grid of cards, a table, a form. Never as one block that draws the whole screen.

Each block is a line in the roles editor, a target for .watch() reactivity, a widget a PeriodSelector can drive, and a node the page builder can move. One block for the whole screen gives up all of that at once.

src/pages/orders.ts
@RegisterPage()
export class OrdersPage extends PageController("orders", { displayName: "$shop.orders.title", category: shopCategory }) {
  static period = PeriodSelector({ id: "orders" });

  static kpis = Grid().child(
    "row",
    GridRow()
      .child("revenue", KpiCard({ title: "$shop.orders.revenue", fetchUrl: "/api/orders/revenue", periodScope: "orders" }))
      .child("count", KpiCard({ title: "$shop.orders.count", fetchUrl: "/api/orders/count", periodScope: "orders" })),
  );

  static table = TableView(OrdersDataAPI, { caption: "$shop.orders.list" });
}

2. Build only the missing block

When a part of the screen has no DMS equivalent — a flow canvas, a seat map — build that block only as a custom component, and keep everything around it as DMS blocks described on the backend. Permissions stay granular, and the rest of the page keeps the theme, the period, the live state and the editor for free.

3. Every custom block carries .meta()

Every custom block declares its name, description and icon with .meta(), as i18n keys, so the roles editor shows a readable line for it in every language. Without it, the permission is titled with the raw component name.

src/pages/routes.ts
static graph = CustomComponent("ApiRouteGraph")
  .options({ fetchUrl: "/api/routes/graph", periodScope: "api" })
  .meta({
    name: "$api.routes.graph.name",
    description: "$api.routes.graph.description",
    icon: "i-ph-graph",
  });

Add the keys to every locale file of your frontend layer. Use $ keys for every other text too: page names, titles, labels, button labels.

When to leave DMS blocks

A custom block is a deviation from the DMS, so an agent proposes it and lets you decide. It proposes one only in the cases on the left:

Leave DMS blocks whenStay on DMS blocks when
No block covers the need, even through its options or a composition of blocks.The difference is spacing, color, radius, font or a minor layout that the theme or the block's options handle.
The interaction model differs: a drag-and-drop canvas, real-time collaborative editing.The DMS block only looks slightly different from the mockup.
You asked for pixel perfect on this element.Another element of the page is custom: a custom block never pulls its neighbours along.
A measured constraint, such as performance or data volume, is beyond what the block can meet.The constraint is a guess rather than a measurement.

Before any custom block

An agent follows the same path before it writes any custom block, one element at a time:

Search the catalog

The agent looks for a block that fits: in dms-ai with @antelopejs/dms-builder loaded, through the Builder catalog; otherwise in the DMS block catalog, the components pages or the exports of @antelopejs/interface-dms/base. It tries the block's options first, then a composition of blocks, such as a Card holding a KeyValueList.

Compare

In the dms-ai chat, the agent previews the closest DMS option next to your mockup, with a sketch of the custom block it would build as another tab. Elsewhere, it writes a short comparison table.

Ask

The agent asks one closed question: keep the DMS block, adjust it through its options, or build a custom block.

Build

Only when you choose the custom block does the agent build it, alone and with .meta(); the rest of the page stays DMS blocks.

In safe mode, the dms-ai assistant cannot write custom code at all. It tells you so, offers the closest DMS composition, and asks whether to switch to Code mode for that one block.

When a design is involved

When you hand an agent a mockup or a screenshot, it follows the design-fidelity protocol: one framing question, then this path for each component that differs visibly.

Next steps

Design fidelity

From a mockup to blocks, one component at a time.

Block fundamentals

How blocks nest, read data and carry permissions.