Search docs

Jump to any table example

Discord—

Deployments

A deployment history like the ones on hosting platforms: status with build time, environment, branch and commit, and a filter bar for environment, status, branch, and date, with live counts on every option.

$ npx shadcn add https://www.shad-table.dev/r/deployments-table.json

42 deployments

DeploymentStatusSourceCreated

dpl_fb86738b4

Production

Building

42s

main

484f3e3Fix redirect loop after SSO login

2m agoby ava

dpl_c1e89af9e

Production

Queued

Waiting for a build slot

main

01c567fRefactor auth middleware

12m agoby noah

dpl_2e53a50ae

ProductionCurrent

Ready

3m 18s

main

8aefd9fUpdate onboarding copy

42m agoby sofia

dpl_b8cf8d6ab

Production

Ready

3m 52s

main

fda099bUpdate onboarding copy

59m agoby liam

dpl_d2f89263d

Preview

Error

2m 10s

fix/login-redirect

bf739cdBump dependencies

1h agoby sofia

dpl_7720201ef

Preview

Ready

1m 33s

feat/billing-v2

55cbc8cTighten CSP headers

2h agoby sofia

dpl_4ddc65e45

Preview

Error

2m 2s

feat/billing-v2

6584e9fFix flaky e2e test

3h agoby sofia

dpl_a2b69d641

Preview

Canceled

2m 22s

fix/login-redirect

1becdf3Update onboarding copy

4h agoby liam

dpl_9b994d72b

Production

Ready

1m 30s

main

21486edHandle empty cart in checkout

5h agoby mia

dpl_c5efd02c1

Preview

Canceled

2m 53s

feat/billing-v2

cb07f11Tighten CSP headers

6h agoby noah

1–10 of 42

How it works

1.Every filter is a TanStack column filter

Environment, status, branch, and date each set a filter value on their own column, with a filterFn that knows its shape: a list of allowed values, or a number of days back. Clearing everything is one setColumnFilters([]) call, and the table never keeps a second copy of the filter state.

src/components/deployments/columns.tsx
const inList: FilterFn<Deployment> = (row, id, value: string[] | undefined) =>
!value?.length || value.includes(row.getValue<string>(id))
const withinDays: FilterFn<Deployment> = (row, id, days: number | undefined) =>
days === undefined || row.getValue<number>(id) >= NOW - days * 86_400_000

2.Option counts come from faceting, so they never lie

getFacetedUniqueValues() counts each status or branch after applying every other active filter, but not the column's own. With Production selected, the Status menu shows how many production deploys are Ready or Error, which is exactly what ticking that option would give you.

src/components/deployments/faceted-filter.tsx
const counts = column?.getFacetedUniqueValues()
<span className="ml-auto font-mono text-xs text-muted-foreground">
{counts?.get(option) ?? 0}
</span>

3.Search covers fields that are not visible columns

The commit SHA, message, and author are real columns hidden with columnVisibility, so the search box can match them without cluttering the table. A custom globalFilterFn decides exactly which fields are searched, instead of TanStack guessing from value types.

src/components/deployments/data-table.tsx
const searchDeployments: FilterFn<Deployment> = (row, _id, query: string) => {
const q = query.trim().toLowerCase()
if (!q) return true
const d = row.original
return [d.id, d.branch, d.sha, d.message, d.author].some((field) =>
field.toLowerCase().includes(q),
)
}

4.Filtering always starts from page 1

With autoResetPageIndex, any filter change sends the table back to the first page, so narrowing 42 deployments down to 3 never leaves you staring at an empty page 4. The footer and the count next to the filters both read from the filtered row model.

src/components/deployments/data-table.tsx
useReactTable({
// ...
autoResetPageIndex: true,
getFilteredRowModel: getFilteredRowModel(),
getFacetedRowModel: getFacetedRowModel(),
getFacetedUniqueValues: getFacetedUniqueValues(),
})