Build a list
A list is a backend endpoint that returns PagedResult<T> and a frontend <DataGrid> that consumes it. Paging, sort, filter, search, URL sync, and the loading/empty/error states come for free.
Backend
Return a PagedResult<T> from a query spec that declares which columns are searchable, sortable, and filterable:
csharp
private static async Task<IResult> List(AppDbContext db, [AsParameters] PagedRequest req, CancellationToken ct)
{
var spec = db.Set<Project>().AsNoTracking()
.Searchable(p => p.Name) // free-text search hits these
.Sortable(p => p.Name, p => p.CreatedAt)
.Filterable(p => p.Name, p => p.CreatedAt);
return Results.Ok(await spec.ToPagedResultAsync(req, p => p.ToDto(), ct));
}The operator-suffix query syntax (?name=contains:acme&sort=createdAt:desc&page=2) is parsed for you — see the Query syntax reference.
Frontend
tsx
const columns: ColumnDef<Project>[] = [
{ accessorKey: 'name', header: 'Name' },
{ accessorKey: 'createdAt', header: 'Created', cell: ({ row }) => formatDate(row.original.createdAt) },
];
export default function ProjectsPage() {
const grid = useDataGrid<Project>({ endpoint: '/projects' });
return <DataGrid grid={grid} columns={columns} searchPlaceholder="Search projects" />;
}That's the whole list. Add a <FacetFilter> for multi-select column filters, pass renderCard={…} for a Table ⇄ Cards toggle, and the export menu wires to your /export endpoint. The grid persists page/sort/search/filters to the URL automatically, so the view is shareable.
See Data tables for the full feature set.