Skip to content

Define Screens

The defineScreen function creates a screen definition with metadata about your page/route.

import { defineScreen } from "screenbook"
export const screen = defineScreen({
id: "billing.invoice.detail",
title: "Invoice Detail",
route: "/billing/invoices/:id",
})
FieldTypeDescription
idstringUnique identifier for the screen
titlestringHuman-readable title
routestringRoute path pattern
FieldTypeDescription
ownerstring[]Team(s) that own this screen
tagsstring[]Tags for categorization
dependsOnstring[]APIs/services this screen depends on
entryPointsstring[]Screen IDs that can navigate here
nextstring[]Screen IDs this screen can navigate to
descriptionstringOptional description
linksLink[]External resource links
mockScreenMockWireframe-level UI mock definition
import { defineScreen } from "screenbook"
export const screen = defineScreen({
// Required fields
id: "billing.invoice.detail",
title: "Invoice Detail",
route: "/billing/invoices/:id",
// Ownership
owner: ["billing-team", "payments-team"],
// Categorization
tags: ["billing", "invoice", "detail"],
description: "Displays detailed information about a specific invoice",
// Dependencies
dependsOn: [
"InvoiceAPI.getDetail",
"PaymentAPI.getStatus",
"CustomerAPI.get",
],
// Navigation
entryPoints: ["billing.invoice.list", "dashboard"],
next: ["billing.invoice.edit", "billing.payment.start"],
// External links
links: [
{ label: "Figma Design", url: "https://figma.com/..." },
{ label: "Storybook", url: "https://storybook.example.com/..." },
],
})

Use dot-separated hierarchical IDs:

domain.feature.action

Examples:

  • billing.invoice.list
  • billing.invoice.detail
  • billing.invoice.create
  • settings.profile
  • auth.login

This convention helps with:

  • Alphabetical sorting groups related screens
  • Easy filtering by domain
  • Clear ownership boundaries

Routes can include dynamic segments:

// Static route
route: "/dashboard"
// Single dynamic segment
route: "/users/:id"
// Multiple dynamic segments
route: "/projects/:projectId/tasks/:taskId"
// Optional segments (depends on your router)
route: "/products/:category?/:subcategory?"

The dependsOn field tracks which APIs or services this screen relies on:

dependsOn: [
"InvoiceAPI.getDetail", // Specific endpoint
"PaymentService", // Entire service
"UserStore.currentUser", // State dependency
]

This enables Impact Analysis to identify affected screens when APIs change.

Screens that link TO this screen:

// On invoice detail page
entryPoints: ["billing.invoice.list"] // User comes from list page

Screens this page links TO:

// On invoice detail page
next: ["billing.invoice.edit", "billing.payment.start"]

These relationships create the Navigation Graph.

Screen references in entryPoints and next are validated during build:

Terminal window
# Shows warnings for invalid references
npx screenbook build
# Fails on invalid references
npx screenbook build --strict

The mock field allows you to define wireframe-level UI mockups for screen flow documentation. Navigation targets defined in mocks are automatically extracted and merged into the next array.

import { defineScreen } from "screenbook"
export const screen = defineScreen({
id: "billing.invoice.detail",
title: "Invoice Detail",
route: "/billing/invoices/:id",
mock: {
sections: [
{
title: "Header",
layout: "horizontal",
elements: [
{ type: "text", label: "Invoice #123", variant: "heading" },
{ type: "button", label: "Edit", navigateTo: "billing.invoice.edit" },
],
},
{
title: "Line Items",
elements: [
{ type: "list", label: "Items", itemCount: 5, itemNavigateTo: "billing.lineitem.detail" },
],
},
],
},
})
TypePropertiesDescription
buttonlabel, variant?, navigateTo?Clickable button
inputlabel, placeholder?, inputType?Form input field
linklabel, navigateTo?Text link
textlabel, variant?Static text
imagelabel, aspectRatio?Image placeholder
listlabel, itemCount?, itemNavigateTo?List of items
tablelabel, columns?, rowCount?, rowNavigateTo?Data table
{ type: "button", label: "Submit", variant: "primary" }
{ type: "button", label: "Cancel", variant: "secondary" }
{ type: "button", label: "Delete", variant: "danger" }
{ type: "text", label: "Page Title", variant: "heading" }
{ type: "text", label: "Section Title", variant: "subheading" }
{ type: "text", label: "Normal text", variant: "body" }
{ type: "text", label: "Small text", variant: "caption" }
{ type: "input", label: "Email", inputType: "email" }
{ type: "input", label: "Password", inputType: "password" }
{ type: "input", label: "Search", inputType: "search" }
{ type: "input", label: "Description", inputType: "textarea" }
// Vertical layout (default)
{ title: "Form", layout: "vertical", elements: [...] }
// Horizontal layout
{ title: "Actions", layout: "horizontal", elements: [...] }

Sections can contain child sections for complex layouts:

{
title: "Parent Section",
elements: [{ type: "text", label: "Parent content" }],
children: [
{
title: "Child Section",
elements: [{ type: "button", label: "Child action" }],
},
],
}

Navigation targets from mock elements are automatically merged into next:

// These navigateTo values...
{ type: "button", label: "Edit", navigateTo: "billing.invoice.edit" }
{ type: "list", label: "Items", itemNavigateTo: "billing.lineitem.detail" }
{ type: "table", label: "Invoices", rowNavigateTo: "billing.invoice.detail" }
// ...are automatically added to the screen's `next` array

This means you don’t need to manually maintain next when using mocks - navigation is derived from the UI definition.

  1. Keep metadata close to routes - Place screen.meta.ts in the same directory as your page component

  2. Use consistent ID patterns - Establish a naming convention for your team

  3. Track real dependencies - Only list APIs actually called by the screen

  4. Update on changes - Keep navigation relationships in sync with actual links

  5. Use mocks for documentation - Define wireframes to visualize screen structure and auto-generate navigation