Structured Card
Structured cards provide a pre-configured card layout with dedicated slots for a title, header actions, body content, and footer actions.
A pre-configured layout for a title, actions, body content, and footer buttons.
Overview
The structured card is a pre-configured card layout optimized for common patterns in Tyler Cloud applications. Its slot-based architecture provides designated areas for a title, header actions, body content, and footer buttons, so teams build consistent card UIs without hand-assembling the same header and footer structure every time. It’s ideal for table containers and self-contained content sections that need their own actions.
Usage
forge-structured-card lays out a card through named slots — before-title, title, header-actions, after-header-actions, body, footer-start, footer-secondary-action, and footer-primary-action — plus heading-level to set the title’s semantic level and body-spacing to remove body padding for edge-to-edge content. Every slot is optional, and the header and footer hide when nothing is slotted into them. Use it when a card needs a header, body, and footer; for a card that only groups content with no header or footer, use a plain forge-card instead.
Slot locations
Rules
heading-level so the title fits the page's heading hierarchy.after-header-actions.header-actions.body-spacing="none" for full-width body content, like a table.footer-primary-action and the alternative, like Cancel, in footer-secondary-action.heading3 to the title slot.With a form
Put a form in the body slot and its submit and cancel buttons in the footer action slots. Use header-actions for status, like a badge.
A structured card containing a form, with a status badge in the header and actions in the footer.
<div class="w-[600px]">
<forge-structured-card heading-level="2">
<div slot="title">
Project Details
</div>
<forge-badge theme="warning" slot="header-actions">
<span>
In progress
</span>
<forge-icon name="construction" slot="end">
</forge-icon>
</forge-badge>
<form slot="body">
<forge-stack>
<forge-text-field label-position="block-start">
<label>
Project name
</label>
<input type="text" />
</forge-text-field>
<forge-text-field label-position="block-start">
<label>
Description
</label>
<textarea>
</textarea>
</forge-text-field>
<forge-select label="Category" label-position="block-start">
<forge-option value="development">
Development
</forge-option>
<forge-option value="design">
Design
</forge-option>
<forge-option value="marketing">
Marketing
</forge-option>
<forge-option value="research">
Research
</forge-option>
</forge-select>
<forge-text-field label-position="block-start">
<label>
Owner
</label>
<input type="text" />
</forge-text-field>
<forge-file-picker accept=".jpg,.png,.pdf">
<forge-button variant="outlined">
Attach files
</forge-button>
</forge-file-picker>
</forge-stack>
</form>
<forge-button variant="text" slot="footer-secondary-action">
<forge-icon slot="start" name="delete">
</forge-icon>
Cancel
</forge-button>
<forge-button variant="tonal" slot="footer-primary-action">
<forge-icon slot="start" name="save">
</forge-icon>
Save
</forge-button>
</forge-structured-card>
</div>
<script type="module">
import { IconRegistry } from "@tylertech/forge/icon";
import { tylIconConstruction, tylIconDelete, tylIconSave } from "@tylertech/tyler-icons";
IconRegistry.define([tylIconConstruction, tylIconDelete, tylIconSave]);
</script>Header icon button
Use the after-header-actions slot for icon buttons that need to run up against the card’s edge, such as overflow menus or close buttons.
A structured card with an overflow menu icon button at the end of the header.
<div class="w-[600px]">
<forge-structured-card heading-level="2">
<div slot="title">
Project Details
</div>
<forge-menu slot="after-header-actions" id="structured-card-menu">
<forge-icon-button aria-label="More actions">
<forge-icon name="more_vert">
</forge-icon>
</forge-icon-button>
</forge-menu>
<div slot="body">
<p class="m-0">
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,
when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap
into electronic typesetting, remaining essentially unchanged.
</p>
</div>
<forge-button variant="text" slot="footer-secondary-action">
<forge-icon slot="start" name="delete">
</forge-icon>
Cancel
</forge-button>
<forge-button variant="filled" slot="footer-primary-action">
<forge-icon slot="start" name="save">
</forge-icon>
Save
</forge-button>
</forge-structured-card>
</div>
<script type="module">
import { IconRegistry } from "@tylertech/forge/icon";
import { tylIconDelete, tylIconDownload, tylIconEdit, tylIconMoreVert, tylIconSave, tylIconShare } from "@tylertech/tyler-icons";
IconRegistry.define([tylIconDelete, tylIconDownload, tylIconEdit, tylIconMoreVert, tylIconSave, tylIconShare]);
const menu = document.querySelector("#structured-card-menu");
if (menu) {
menu.options = [
{ label: "Edit", value: "edit", leadingIcon: "edit", leadingIconType: "component" },
{ label: "Share", value: "share", leadingIcon: "share", leadingIconType: "component" },
{ label: "Download", value: "download", leadingIcon: "download", leadingIconType: "component" },
{ label: "Delete", value: "delete", leadingIcon: "delete", leadingIconType: "component" }
];
}
</script>Back navigation
Use the before-title slot for a back button or navigation icon, such as in a detail panel.
A structured card with a back icon button before the title.
<div class="w-[600px]">
<forge-structured-card heading-level="2">
<forge-icon-button aria-label="Back" slot="before-title">
<forge-icon name="arrow_back">
</forge-icon>
</forge-icon-button>
<span slot="title">
Project Details
</span>
<div slot="body">
<p class="m-0">
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,
when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap
into electronic typesetting, remaining essentially unchanged.
</p>
</div>
<forge-button variant="text" slot="footer-secondary-action">
<forge-icon slot="start" name="delete">
</forge-icon>
Cancel
</forge-button>
<forge-button variant="filled" slot="footer-primary-action">
<forge-icon slot="start" name="save">
</forge-icon>
Save
</forge-button>
</forge-structured-card>
</div>
<script type="module">
import { IconRegistry } from "@tylertech/forge/icon";
import { tylIconArrowBack, tylIconDelete, tylIconSave } from "@tylertech/tyler-icons";
IconRegistry.define([tylIconArrowBack, tylIconDelete, tylIconSave]);
</script>Tables
Set body-spacing="none" (or the bodySpacing property) for full-width content like a table. It removes the body padding and the footer’s bottom padding so the content runs edge to edge. Put a paginator in footer-primary-action.
A structured card containing a table, with refresh and download icon buttons in the header and a paginator in the footer.
<forge-structured-card heading-level="2" body-spacing="none" class="w-[600px]">
<div slot="title">
User Management
</div>
<forge-icon-button aria-label="Refresh data" slot="after-header-actions">
<forge-icon name="refresh">
</forge-icon>
</forge-icon-button>
<forge-icon-button aria-label="Download report" slot="after-header-actions">
<forge-icon name="download">
</forge-icon>
</forge-icon-button>
<forge-table slot="body" id="structured-card-table">
</forge-table>
<forge-paginator slot="footer-primary-action" page-size="5" total="25" page-index="0">
</forge-paginator>
</forge-structured-card>
<script type="module">
import { IconRegistry } from "@tylertech/forge/icon";
import { tylIconDownload, tylIconRefresh } from "@tylertech/tyler-icons";
IconRegistry.define([tylIconDownload, tylIconRefresh]);
const table = document.querySelector("#structured-card-table");
if (table) {
table.data = [
{ id: 1, name: "John Doe", email: "john.doe@example.com", role: "Admin" },
{ id: 2, name: "Jane Smith", email: "jane.smith@example.com", role: "User" },
{ id: 3, name: "Bob Johnson", email: "bob.johnson@example.com", role: "User" },
{ id: 4, name: "Alice Williams", email: "alice.williams@example.com", role: "Manager" },
{ id: 5, name: "Charlie Brown", email: "charlie.brown@example.com", role: "User" }
];
table.columnConfigurations = [
{ property: "name", header: "Name" },
{ property: "email", header: "Email" },
{ property: "role", header: "Role" }
];
}
</script>Use footer-start for buttons or controls that belong at the start of the footer, opposite the paginator.
A structured card containing a table, with buttons at the start of the footer and a paginator at the end.
<forge-structured-card heading-level="2" body-spacing="none" class="w-[700px]">
<div slot="title">
Asset Inventory
</div>
<forge-icon-button aria-label="Refresh data" slot="after-header-actions">
<forge-icon name="refresh">
</forge-icon>
</forge-icon-button>
<forge-icon-button aria-label="Download report" slot="after-header-actions">
<forge-icon name="download">
</forge-icon>
</forge-icon-button>
<forge-table slot="body" id="asset-table">
</forge-table>
<forge-stack slot="footer-start" inline alignment="center" wrap gap="8">
<forge-button variant="outlined">
Update asset inventory
</forge-button>
<forge-button variant="outlined">
Asset inventory dataset
</forge-button>
</forge-stack>
<forge-paginator slot="footer-primary-action" page-size="5" total="25" page-index="0">
</forge-paginator>
</forge-structured-card>
<script type="module">
import { IconRegistry } from "@tylertech/forge/icon";
import { tylIconDownload, tylIconRefresh } from "@tylertech/tyler-icons";
IconRegistry.define([tylIconDownload, tylIconRefresh]);
const table = document.querySelector("#asset-table");
if (table) {
table.data = [
{ id: "AST-001", name: "Dell Latitude 5520", category: "Laptop", location: "Building A", status: "In Use" },
{ id: "AST-002", name: "HP LaserJet Pro", category: "Printer", location: "Building B", status: "Available" },
{ id: "AST-003", name: "Cisco IP Phone 8845", category: "Phone", location: "Building A", status: "In Use" },
{ id: "AST-004", name: 'Samsung 27" Monitor', category: "Monitor", location: "Building C", status: "In Repair" },
{ id: "AST-005", name: "Logitech MX Keys", category: "Keyboard", location: "Building A", status: "Available" }
];
table.columnConfigurations = [
{ property: "id", header: "Asset ID" },
{ property: "name", header: "Asset Name" },
{ property: "category", header: "Category" },
{ property: "location", header: "Location" },
{ property: "status", header: "Status" }
];
}
</script>Scrollable body
Set --forge-structured-card-body-height to a fixed value to make the body scroll while the header and footer stay pinned.
A structured card with a fixed body height and scrolling content.
<div class="w-[360px]">
<forge-structured-card style="--forge-structured-card-body-height: 200px">
<span slot="title">
Project Details
</span>
<forge-icon-button aria-label="More actions" slot="after-header-actions">
<forge-icon name="more_vert">
</forge-icon>
</forge-icon-button>
<div slot="body">
<p class="m-0">
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,
when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap
into electronic typesetting, remaining essentially unchanged.
</p>
<p class="m-0">
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,
when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap
into electronic typesetting, remaining essentially unchanged.
</p>
</div>
<forge-button variant="filled" slot="footer-primary-action">
<forge-icon slot="start" name="save">
</forge-icon>
Save
</forge-button>
</forge-structured-card>
</div>
<script type="module">
import { IconRegistry } from "@tylertech/forge/icon";
import { tylIconMoreVert, tylIconSave } from "@tylertech/tyler-icons";
IconRegistry.define([tylIconMoreVert, tylIconSave]);
</script>Multiple cards
Lay out related structured cards in a wrapping row so they share the same header, body, and footer structure and stay easy to scan.
Three structured cards in a wrapping row, each with an icon title, status badge, and footer actions.
<div class="flex flex-wrap gap-large">
<forge-structured-card heading-level="2" class="grow basis-[300px] max-w-[400px]">
<div slot="title" class="flex items-center gap-medium">
<forge-icon name="person">
</forge-icon>
<span>
User Profile
</span>
</div>
<forge-badge theme="success" slot="header-actions">
<forge-icon name="check_circle" slot="start">
</forge-icon>
<span>
Active
</span>
</forge-badge>
<div slot="body" class="flex flex-col gap-small">
<div class="bg-surface-dim rounded-sm h-[48px]">
</div>
<div class="bg-surface-dim rounded-sm h-[24px]">
</div>
<div class="bg-surface-dim rounded-sm h-[24px] w-[60%]">
</div>
</div>
<forge-button variant="text" slot="footer-secondary-action">
Cancel
</forge-button>
<forge-button variant="tonal" slot="footer-primary-action">
Save
</forge-button>
</forge-structured-card>
<forge-structured-card heading-level="2" class="grow basis-[300px] max-w-[400px]">
<div slot="title" class="flex items-center gap-medium">
<forge-icon name="settings">
</forge-icon>
<span>
Preferences
</span>
</div>
<forge-badge theme="warning" slot="header-actions">
<forge-icon name="construction" slot="start">
</forge-icon>
<span>
Modified
</span>
</forge-badge>
<div slot="body" class="flex flex-col gap-small">
<div class="bg-surface-dim rounded-sm h-[24px]">
</div>
<div class="bg-surface-dim rounded-sm h-[24px]">
</div>
<div class="bg-surface-dim rounded-sm h-[48px]">
</div>
</div>
<forge-button variant="text" slot="footer-secondary-action">
Reset
</forge-button>
<forge-button variant="tonal" slot="footer-primary-action">
Apply
</forge-button>
</forge-structured-card>
<forge-structured-card heading-level="2" class="grow basis-[300px] max-w-[400px]">
<div slot="title" class="flex items-center gap-medium">
<forge-icon name="edit">
</forge-icon>
<span>
Notes
</span>
</div>
<forge-badge theme="info-secondary" slot="header-actions">
<forge-icon name="description" slot="start">
</forge-icon>
<span>
Draft
</span>
</forge-badge>
<div slot="body" class="flex flex-col gap-small">
<div class="bg-surface-dim rounded-sm h-[24px] w-[60%]">
</div>
<div class="bg-surface-dim rounded-sm h-[48px]">
</div>
<div class="bg-surface-dim rounded-sm h-[24px]">
</div>
<div class="bg-surface-dim rounded-sm h-[24px] w-[60%]">
</div>
</div>
<forge-button variant="text" slot="footer-secondary-action">
Discard
</forge-button>
<forge-button variant="tonal" slot="footer-primary-action">
Save
</forge-button>
</forge-structured-card>
</div>
<script type="module">
import { IconRegistry } from "@tylertech/forge/icon";
import { tylIconCheckCircle, tylIconConstruction, tylIconDescription, tylIconEdit, tylIconPerson, tylIconSettings } from "@tylertech/tyler-icons";
IconRegistry.define([tylIconCheckCircle, tylIconConstruction, tylIconDescription, tylIconEdit, tylIconPerson, tylIconSettings]);
</script>A pre-configured card layout with designated areas for titles, header actions, body content, and footer buttons.
import "@tylertech/forge/structured-card";| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
headingLevel | — | HeadingLevel | 2 | Controls the semantic heading level for the title content. Defaults to `2` (h2). Use this to maintain proper heading hierarchy in your document structure. |
bodySpacing | — | 'none' | 'default' | 'default' | Controls the spacing applied to the body section. Defaults to `default`. - `default`: Applies standard padding to the body section. - `none`: Removes padding from the body section for full-width content like tables. |
| Name | Description |
|---|---|
before-title | Content displayed before the title in the header |
title | The title content displayed in the header |
header-actions | Action buttons or controls displayed at the end of the header |
after-header-actions | Content displayed after the header actions, designed specifically for icon buttons that need to run up against the card edge (ie: menu icon button) |
body | The main body content of the card |
footer-start | Content displayed at the start of the footer (e.g., pagination controls) |
footer-secondary-action | Secondary action button displayed at the end of the footer |
footer-primary-action | Primary action button displayed at the end of the footer |
| Name | Description |
|---|---|
--forge-structured-card-body-height | Controls the height of the body content. Defaults to `auto`. |
Heading level
The title is exposed as a heading, at level 2 by default. Set heading-level so it fits the page’s heading hierarchy — for example, 3 for a card under a section’s h2.
Labeling icon buttons
Give every icon button in the header an aria-label, such as “More actions” or “Back”, since it has no visible text.
Tab order
A structured card is a container, not an interactive control. Its slotted controls follow the natural tab order: header, then body, then footer.