Generic list screen with title, toolbar, rows that work desktop and mobile.
Each example is live, but its links go nowhere. HTML shows the markup to copy.
page-header
The screen's title bar: an h1, an optional subhead, and a primary action.
Title, subhead and a primary action
<headerrst-page-header>
<divrst-page-header-titles>
<h1>Posts</h1>
<prst-page-header-sub>Everything you have written, newest first.</p>
</div>
<arst-btn="primary"href="/posts/new">
<svgclass="icon"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"aria-hidden="true"><pathd="M5 12h14"/><pathd="M12 5v14"/></svg>
Write a post
</a>
</header>
{{template"page-header"dict"Title""Posts""Sub""Everything you have written, newest first.""ActionHref""/posts/new""ActionLabel""Write a post""ActionIcon""plus"}}
Nothing in Hidden says which pair is the page, so the default would carry page=4 into a result set that no longer has four pages. ClearHref is how an app resets it.
One list row: the record's name as the primary link, a meta line, and a secondary action.
Lead marker, status and an action
Put this inside <div rst-list>.
<divrst-row>
<spanrst-row-leaddata-lead="accent"aria-hidden="true">RN</span>
<spanrst-row-main><ahref="/posts/1">Release notes, August</a>
<smallrst-row-sub>Published 2 August · 4 min read</small>
</span>
<spanrst-statusrst-tone="positive">Published</span>
<arst-row-actionhref="/posts/1/edit"aria-label="Edit Release notes, August">Edit</a>
</div>
Put this inside <div rst-list>.
{{template"list-row-action"dict"Href""/posts/1""Main""Release notes, August""Sub""Published 2 August · 4 min read""ActionHref""/posts/1/edit""ActionLabel""Edit""ActionAria""Edit Release notes, August""StatusTone""positive""StatusLabel""Published""Lead""accent""LeadInitial""RN"}}
Name and meta line only
Put this inside <div rst-list>.
<divrst-row>
<spanrst-row-main><ahref="/posts/2">Why we moved off the old runner</a>
<smallrst-row-sub>Draft · saved 20 minutes ago</small>
</span>
</div>
Put this inside <div rst-list>.
{{template"list-row-action"dict"Href""/posts/2""Main""Why we moved off the old runner""Sub""Draft · saved 20 minutes ago"}}
row-menu
A row's other actions, behind a ⋮ button. A destructive one links to its confirm page.
Wrap the grid and a bulk-bar in one form. Each checkbox posts its tile's Select value under the name "id"; set SelectName to change it. Inside the form, menu items must be links.
pagination
A numbered strip of ordinary links, so paging survives JavaScript being off.
No Label, so the nav's accessible name comes from the catalog.
empty-state
The blank-state card. Empty state with affordances for starting steps.
With a POST call to action
<divrst-empty>
<h2rst-empty-title>Nothing here yet</h2>
<prst-empty-body>No posts yet. Your first one is a good place to start.</p>
<formrst-empty-ctamethod="post"action="/posts/seed">
<inputtype="hidden"name="csrf"value="tok-123">
<buttonrst-btn="primary"type="submit">Add sample posts</button>
</form>
</div>
{{template"empty-state"dict"Body""No posts yet. Your first one is a good place to start.""Title""Nothing here yet""ActionLabel""Add sample posts""PostAction""/posts/seed""Hidden" (list (list"csrf""tok-123"))}}
Sentence only
<divrst-empty>
<prst-empty-body>No comments on this post yet.</p>
</div>
{{template"empty-state"dict"Body""No comments on this post yet."}}