Skip to main content

Table

Present data in rows and columns with tables. Compare striped, compact, scrollable, and sticky layouts.

Examples

Default

Breakpoints

Mobile

320px

Mobile Lg

480px

Tablet

640px

Desktop

1024px
Historic documents
Document titleDescriptionYear
Declaration of IndependenceStatement declaring independence from the British Empire.1776
Bill of RightsThe first ten amendments of the U.S. Constitution.1791
Declaration of SentimentsA declaration of the rights of American women.1848
Emancipation ProclamationAn executive order granting freedom to enslaved people in designated states.1863
table-demo.tsx
import { Table } from '@uswds-tailwind/react'
const documents = [
{ title: 'Declaration of Independence', description: 'Statement declaring independence from the British Empire.', year: '1776' },
{ title: 'Bill of Rights', description: 'The first ten amendments of the U.S. Constitution.', year: '1791' },
{ title: 'Declaration of Sentiments', description: 'A declaration of the rights of American women.', year: '1848' },
{ title: 'Emancipation Proclamation', description: 'An executive order granting freedom to enslaved people in designated states.', year: '1863' },
]
export default function TableDemo() {
return (
<Table.Root>
<Table.Caption>Historic documents</Table.Caption>
<Table.Header>
<Table.Row>
<Table.ColumnHeader>Document title</Table.ColumnHeader>
<Table.ColumnHeader>Description</Table.ColumnHeader>
<Table.ColumnHeader>Year</Table.ColumnHeader>
</Table.Row>
</Table.Header>
<Table.Body>
{documents.map(document => (
<Table.Row key={document.title}>
<Table.ColumnHeader scope="row" className="font-normal">{document.title}</Table.ColumnHeader>
<Table.Cell>{document.description}</Table.Cell>
<Table.Cell>{document.year}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table.Root>
)
}

Striped

Breakpoints

Mobile

320px

Mobile Lg

480px

Tablet

640px

Desktop

1024px
Historic documents with stripes
Document titleDescriptionYear
Declaration of IndependenceStatement declaring independence from the British Empire.1776
Bill of RightsThe first ten amendments of the U.S. Constitution.1791
Declaration of SentimentsA declaration of the rights of American women.1848
Emancipation ProclamationAn executive order granting freedom to enslaved people in designated states.1863
table-striped-demo.tsx
import { Table } from '@uswds-tailwind/react'
const documents = [
{ title: 'Declaration of Independence', description: 'Statement declaring independence from the British Empire.', year: '1776' },
{ title: 'Bill of Rights', description: 'The first ten amendments of the U.S. Constitution.', year: '1791' },
{ title: 'Declaration of Sentiments', description: 'A declaration of the rights of American women.', year: '1848' },
{ title: 'Emancipation Proclamation', description: 'An executive order granting freedom to enslaved people in designated states.', year: '1863' },
]
export default function TableStripedDemo() {
return (
<Table.Root variant="striped">
<Table.Caption>Historic documents with stripes</Table.Caption>
<Table.Header>
<Table.Row>
<Table.ColumnHeader>Document title</Table.ColumnHeader>
<Table.ColumnHeader>Description</Table.ColumnHeader>
<Table.ColumnHeader>Year</Table.ColumnHeader>
</Table.Row>
</Table.Header>
<Table.Body>
{documents.map(document => (
<Table.Row key={document.title}>
<Table.ColumnHeader scope="row" className="font-normal">{document.title}</Table.ColumnHeader>
<Table.Cell>{document.description}</Table.Cell>
<Table.Cell>{document.year}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table.Root>
)
}

Borderless

Breakpoints

Mobile

320px

Mobile Lg

480px

Tablet

640px

Desktop

1024px
Borderless historic documents
Document titleDescriptionYear
Declaration of IndependenceStatement declaring independence from the British Empire.1776
Bill of RightsThe first ten amendments of the U.S. Constitution.1791
Declaration of SentimentsA declaration of the rights of American women.1848
Emancipation ProclamationAn executive order granting freedom to enslaved people in designated states.1863
table-borderless-demo.tsx
import { Table } from '@uswds-tailwind/react'
const documents = [
{ title: 'Declaration of Independence', description: 'Statement declaring independence from the British Empire.', year: '1776' },
{ title: 'Bill of Rights', description: 'The first ten amendments of the U.S. Constitution.', year: '1791' },
{ title: 'Declaration of Sentiments', description: 'A declaration of the rights of American women.', year: '1848' },
{ title: 'Emancipation Proclamation', description: 'An executive order granting freedom to enslaved people in designated states.', year: '1863' },
]
export default function TableBorderlessDemo() {
return (
<Table.Root variant="borderless">
<Table.Caption>Borderless historic documents</Table.Caption>
<Table.Header>
<Table.Row>
<Table.ColumnHeader>Document title</Table.ColumnHeader>
<Table.ColumnHeader>Description</Table.ColumnHeader>
<Table.ColumnHeader>Year</Table.ColumnHeader>
</Table.Row>
</Table.Header>
<Table.Body>
{documents.map(document => (
<Table.Row key={document.title}>
<Table.ColumnHeader scope="row" className="font-normal">{document.title}</Table.ColumnHeader>
<Table.Cell>{document.description}</Table.Cell>
<Table.Cell>{document.year}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table.Root>
)
}

Compact

Breakpoints

Mobile

320px

Mobile Lg

480px

Tablet

640px

Desktop

1024px
Compact historic documents
Document titleDescriptionYear
Declaration of IndependenceStatement declaring independence from the British Empire.1776
Bill of RightsThe first ten amendments of the U.S. Constitution.1791
Declaration of SentimentsA declaration of the rights of American women.1848
Emancipation ProclamationAn executive order granting freedom to enslaved people in designated states.1863
table-compact-demo.tsx
import { Table } from '@uswds-tailwind/react'
const documents = [
{ title: 'Declaration of Independence', description: 'Statement declaring independence from the British Empire.', year: '1776' },
{ title: 'Bill of Rights', description: 'The first ten amendments of the U.S. Constitution.', year: '1791' },
{ title: 'Declaration of Sentiments', description: 'A declaration of the rights of American women.', year: '1848' },
{ title: 'Emancipation Proclamation', description: 'An executive order granting freedom to enslaved people in designated states.', year: '1863' },
]
export default function TableCompactDemo() {
return (
<Table.Root compact>
<Table.Caption>Compact historic documents</Table.Caption>
<Table.Header>
<Table.Row>
<Table.ColumnHeader>Document title</Table.ColumnHeader>
<Table.ColumnHeader>Description</Table.ColumnHeader>
<Table.ColumnHeader>Year</Table.ColumnHeader>
</Table.Row>
</Table.Header>
<Table.Body>
{documents.map(document => (
<Table.Row key={document.title}>
<Table.ColumnHeader scope="row" className="font-normal">{document.title}</Table.ColumnHeader>
<Table.Cell>{document.description}</Table.Cell>
<Table.Cell>{document.year}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table.Root>
)
}

Scrollable

Breakpoints

Mobile

320px

Mobile Lg

480px

Tablet

640px

Desktop

1024px
Historic documents
Document titleDescriptionYear
Declaration of IndependenceStatement declaring independence from the British Empire.1776
Bill of RightsThe first ten amendments of the U.S. Constitution.1791
Declaration of SentimentsA declaration of the rights of American women.1848
Emancipation ProclamationAn executive order granting freedom to enslaved people in designated states.1863
table-scrollable-demo.tsx
import { Table } from '@uswds-tailwind/react'
const documents = [
{ title: 'Declaration of Independence', description: 'Statement declaring independence from the British Empire.', year: '1776' },
{ title: 'Bill of Rights', description: 'The first ten amendments of the U.S. Constitution.', year: '1791' },
{ title: 'Declaration of Sentiments', description: 'A declaration of the rights of American women.', year: '1848' },
{ title: 'Emancipation Proclamation', description: 'An executive order granting freedom to enslaved people in designated states.', year: '1863' },
]
export default function TableScrollableDemo() {
return (
<Table.ScrollArea>
<Table.Root>
<Table.Caption>Historic documents</Table.Caption>
<Table.Header>
<Table.Row>
<Table.ColumnHeader>Document title</Table.ColumnHeader>
<Table.ColumnHeader>Description</Table.ColumnHeader>
<Table.ColumnHeader>Year</Table.ColumnHeader>
</Table.Row>
</Table.Header>
<Table.Body>
{documents.map(document => (
<Table.Row key={document.title}>
<Table.ColumnHeader scope="row" className="font-normal">{document.title}</Table.ColumnHeader>
<Table.Cell className="whitespace-nowrap">{document.description}</Table.Cell>
<Table.Cell>{document.year}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table.Root>
</Table.ScrollArea>
)
}
Breakpoints

Mobile

320px

Mobile Lg

480px

Tablet

640px

Desktop

1024px
Historic documents with a sticky header
Document titleDescriptionYear
Declaration of IndependenceStatement declaring independence from the British Empire.1776
Bill of RightsThe first ten amendments of the U.S. Constitution.1791
Declaration of SentimentsA declaration of the rights of American women.1848
Emancipation ProclamationAn executive order granting freedom to enslaved people in designated states.1863
Declaration of IndependenceStatement declaring independence from the British Empire.1776
Bill of RightsThe first ten amendments of the U.S. Constitution.1791
Declaration of SentimentsA declaration of the rights of American women.1848
Emancipation ProclamationAn executive order granting freedom to enslaved people in designated states.1863
table-sticky-header-demo.tsx
import { Table } from '@uswds-tailwind/react'
const documents = [
{ title: 'Declaration of Independence', description: 'Statement declaring independence from the British Empire.', year: '1776' },
{ title: 'Bill of Rights', description: 'The first ten amendments of the U.S. Constitution.', year: '1791' },
{ title: 'Declaration of Sentiments', description: 'A declaration of the rights of American women.', year: '1848' },
{ title: 'Emancipation Proclamation', description: 'An executive order granting freedom to enslaved people in designated states.', year: '1863' },
]
export default function TableStickyHeaderDemo() {
return (
<Table.ScrollArea className="h-64 overflow-y-auto">
<Table.Root>
<Table.Caption>Historic documents with a sticky header</Table.Caption>
<Table.Header sticky>
<Table.Row>
<Table.ColumnHeader>Document title</Table.ColumnHeader>
<Table.ColumnHeader>Description</Table.ColumnHeader>
<Table.ColumnHeader>Year</Table.ColumnHeader>
</Table.Row>
</Table.Header>
<Table.Body>
{[...documents, ...documents].map((document, index) => (
<Table.Row key={index}>
<Table.ColumnHeader scope="row" className="font-normal">{document.title}</Table.ColumnHeader>
<Table.Cell>{document.description}</Table.Cell>
<Table.Cell>{document.year}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table.Root>
</Table.ScrollArea>
)
}

Sticky Column

Breakpoints

Mobile

320px

Mobile Lg

480px

Tablet

640px

Desktop

1024px
Historic documents with a sticky column
Document titleDescriptionYear
Declaration of IndependenceStatement declaring independence from the British Empire.1776
Bill of RightsThe first ten amendments of the U.S. Constitution.1791
Declaration of SentimentsA declaration of the rights of American women.1848
Emancipation ProclamationAn executive order granting freedom to enslaved people in designated states.1863
table-sticky-column-demo.tsx
import { Table } from '@uswds-tailwind/react'
const documents = [
{ title: 'Declaration of Independence', description: 'Statement declaring independence from the British Empire.', year: '1776' },
{ title: 'Bill of Rights', description: 'The first ten amendments of the U.S. Constitution.', year: '1791' },
{ title: 'Declaration of Sentiments', description: 'A declaration of the rights of American women.', year: '1848' },
{ title: 'Emancipation Proclamation', description: 'An executive order granting freedom to enslaved people in designated states.', year: '1863' },
]
export default function TableStickyColumnDemo() {
return (
<Table.ScrollArea>
<Table.Root>
<Table.Caption>Historic documents with a sticky column</Table.Caption>
<Table.Header>
<Table.Row>
<Table.ColumnHeader className="sticky left-0">Document title</Table.ColumnHeader>
<Table.ColumnHeader>Description</Table.ColumnHeader>
<Table.ColumnHeader>Year</Table.ColumnHeader>
</Table.Row>
</Table.Header>
<Table.Body>
{documents.map(document => (
<Table.Row key={document.title}>
<Table.ColumnHeader scope="row" className="font-normal sticky left-0">{document.title}</Table.ColumnHeader>
<Table.Cell className="whitespace-nowrap">{document.description}</Table.Cell>
<Table.Cell>{document.year}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table.Root>
</Table.ScrollArea>
)
}

Stacked

Breakpoints

Mobile

320px

Mobile Lg

480px

Tablet

640px

Desktop

1024px
Historic documents
Document titleDescriptionYear
Declaration of IndependenceStatement declaring independence from the British Empire.1776
Bill of RightsThe first ten amendments of the U.S. Constitution.1791
Declaration of SentimentsA declaration of the rights of American women.1848
Emancipation ProclamationAn executive order granting freedom to enslaved people in designated states.1863
table-stacked-demo.tsx
import { Table } from '@uswds-tailwind/react'
const documents = [
{ title: 'Declaration of Independence', description: 'Statement declaring independence from the British Empire.', year: '1776' },
{ title: 'Bill of Rights', description: 'The first ten amendments of the U.S. Constitution.', year: '1791' },
{ title: 'Declaration of Sentiments', description: 'A declaration of the rights of American women.', year: '1848' },
{ title: 'Emancipation Proclamation', description: 'An executive order granting freedom to enslaved people in designated states.', year: '1863' },
]
export default function TableStackedDemo() {
return (
<Table.Root variant="borderless" stacked>
<Table.Caption>Historic documents</Table.Caption>
<Table.Header>
<Table.Row>
<Table.ColumnHeader>Document title</Table.ColumnHeader>
<Table.ColumnHeader>Description</Table.ColumnHeader>
<Table.ColumnHeader>Year</Table.ColumnHeader>
</Table.Row>
</Table.Header>
<Table.Body>
{documents.map(document => (
<Table.Row key={document.title}>
<Table.ColumnHeader scope="row" data-label="Document title" className="font-normal">{document.title}</Table.ColumnHeader>
<Table.Cell data-label="Description">{document.description}</Table.Cell>
<Table.Cell data-label="Year">{document.year}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table.Root>
)
}

On narrow screens, stacked hides the visual header row and places each cell on its own line. Keep the native caption and column headers, and add a data-label that matches its column heading to every body cell.

Sortable

Breakpoints

Mobile

320px

Mobile Lg

480px

Tablet

640px

Desktop

1024px
Federal agency employees
National Park Service20,248Jun 12, 2025
Census Bureau10,632Mar 28, 2025
National Archives2,915Aug 4, 2025
Smithsonian Institution6,547Jan 17, 2025
table-sorted-demo.tsx
import type { ColumnDef } from '@tanstack/react-table'
import {
createSortedRowModel,
rowSortingFeature,
sortFn_basic,
sortFn_text,
tableFeatures,
useTable,
} from '@tanstack/react-table'
import { Table } from '@uswds-tailwind/react'
interface Agency {
name: string
employees: number
lastUpdated: string
}
const agencies: Agency[] = [
{ name: 'National Park Service', employees: 20248, lastUpdated: '2025-06-12' },
{ name: 'Census Bureau', employees: 10632, lastUpdated: '2025-03-28' },
{ name: 'National Archives', employees: 2915, lastUpdated: '2025-08-04' },
{ name: 'Smithsonian Institution', employees: 6547, lastUpdated: '2025-01-17' },
]
const dateFormatter = new Intl.DateTimeFormat('en-US', { dateStyle: 'medium', timeZone: 'UTC' })
const features = tableFeatures({
rowSortingFeature,
sortedRowModel: createSortedRowModel(),
sortFns: { text: sortFn_text, basic: sortFn_basic },
})
const columns: ColumnDef<typeof features, Agency>[] = [
{ accessorKey: 'name', header: 'Agency', sortFn: 'text' },
{ accessorKey: 'employees', header: 'Employees', sortFn: 'basic' },
{ accessorKey: 'lastUpdated', header: 'Last updated', sortFn: 'basic' },
]
export default function TableSortedDemo() {
const dataTable = useTable({ features, columns, data: agencies })
const sorting = dataTable.state.sorting[0]
const sortColumn = sorting
? ({ name: 0, employees: 1, lastUpdated: 2 } as Record<string, number>)[sorting.id] ?? null
: null
return (
<Table.Root
captionText="Federal agency employees"
columnNames={{ 0: 'Agency', 1: 'Employees', 2: 'Last updated' }}
sortDescriptor={sorting && sortColumn !== null
? { column: sortColumn, direction: sorting.desc ? 'descending' : 'ascending' }
: null}
onSortChange={({ sortDescriptor }) => {
dataTable.setSorting(sortDescriptor === null
? []
: [{ id: sortDescriptor.column === 0 ? 'name' : sortDescriptor.column === 1 ? 'employees' : 'lastUpdated', desc: sortDescriptor.direction === 'descending' }])
}}
>
<Table.Caption>Federal agency employees</Table.Caption>
<Table.Header>
<Table.Row>
<Table.ColumnHeader columnIndex={0} sortable>Agency</Table.ColumnHeader>
<Table.ColumnHeader columnIndex={1} sortable>Employees</Table.ColumnHeader>
<Table.ColumnHeader columnIndex={2} sortable>Last updated</Table.ColumnHeader>
</Table.Row>
</Table.Header>
<Table.Body>
{dataTable.getRowModel().rows.map(row => (
<Table.Row key={row.id}>
<Table.ColumnHeader scope="row">{row.original.name}</Table.ColumnHeader>
<Table.Cell columnIndex={1}>{row.original.employees.toLocaleString()}</Table.Cell>
<Table.Cell columnIndex={2}>{dateFormatter.format(new Date(`${row.original.lastUpdated}T00:00:00Z`))}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table.Root>
)
}

Install @tanstack/react-table to sort the data. Table.Root supplies sort buttons, aria-sort, and a status announcement; onSortChange forwards the requested column and direction to TanStack Table, whose row model supplies the reordered rows. Keep the Table sort state and row order in sync so the announcement describes the visible data.

Component API

Compose native table parts inside Root. Use column indices for machine-driven sorting and stacked cell labels.

Root<table>

Renders a div around the native table and an automatic sort status region. Native table props apply to the inner table.

PropTypeDefaultDescription
variantbordered | striped | borderlessborderedSelects border and row styling.
compactbooleanfalseReduces cell padding.
stackedbooleanfalseStacks cells on narrow screens.
captionTextstring—Caption text for table sorting status.
columnNamesRecord<number, string>—Accessible names for column indices. Every sortable ColumnHeader requires a nonempty matching entry.
defaultSortDescriptor{ column: number; direction: 'ascending' | 'descending' } | null—Initial uncontrolled sort state.
sortDescriptor{ column: number; direction: 'ascending' | 'descending' } | null—Controlled sort state. Use null for an unsorted table and reorder your rows to match.
onSortChange({ sortDescriptor: SortDescriptor | null }) => void—Receives sorting requests. Update sortDescriptor and the row order together.

Caption<caption>

Names the table.

Header<thead>

Contains column headings.

PropTypeDescription
stickybooleanKeeps the header visible while scrolling.

Body<tbody>

Contains data rows.

Contains footer rows.

Row<tr>

Contains cells.

ColumnHeader<th>

Labels a column.

PropTypeDescription
columnIndexnumberConnects this heading to the sorting machine.
sortablebooleanRenders a sort button when columnIndex is supplied.
AttributeValuePurpose
data-sortablePresent when sortableStyle sortable column headings.

Cell<td>

Displays one value. Supply data-label for a stacked cell heading.

PropTypeDescription
columnIndexnumberConnects this cell to the active sorted column.
AttributeValuePurpose
data-sort-activePresent for cells in the sorted columnStyles the active sorted column.

ScrollArea<div>

Wraps the table in a horizontally scrollable region.

SrStatus<div>

Announces sort changes. Root renders this part automatically; do not add a second status region.

Accessibility

Provide a caption and column headings. Handle onSortChange in the data source so sorted rows match the announced sort state.