Flex Table
A lightweight, responsive 'pseudo-table' component that can render content either vertically or horizontally.
Playground
Customize the Flex Table properties to see different variations.
Customize
Installation
pnpm dlx shadcn@latest add https://shadcn-ui-variants.vercel.app/r/flex-table.jsonOverview
A flexible, orientation-aware, accessible pseudo-table built with div's and modern layout primitives. Supports vertical (standard) and horizontal (rotated) orientations, customizable formatting, and graceful empty states.
Features
- Flexible orientation: Vertical or horizontal layout.
- Responsive layout: Uses
gridunder the hood. - Fully styleable: Custom class names for headers, cells, and container.
- Formatter support: Format individual cell content dynamically.
- Composable variants: Includes
RowTableandColumnTablewrappers. - Empty state handling: Customize fallback for empty data.
- Sortable columns: Enable by passing
sortable; click headers to toggle asc ↔ desc ↔ none. - Filtering: Provide a
filterByfunction to include/exclude rows before rendering.
Use Cases
- Rendering dynamic tabular data where layout needs to adapt.
- Building matrix-style inputs or dashboards.
- Displaying rotated column-based data (e.g. for mobile).
- Styling table-like data with Tailwind or design tokens, without using
<table>tags.
Basic Usage
1import { FlexTable } from "@/components/ui/flex-table"
2
3const data = [
4 { Name: "John", Age: 25 },
5 { Name: "Jane", Age: 32 },
6 { Name: "Charles", Age: 41 },
7];
8
9export default function FlexTableBasic() {
10 return <FlexTable data={data} />
11}Advanced
Sorting
Enable by passing sortable:
<FlexTable
data={data}
sortable
/>- Click on a header to sort ascending, click again for descending, and a third time to clear sort.
Filtering
Provide a filterBy function to prune rows before display:
<FlexTable
data={data}
filterBy={(row) => row.status === "active"}
/>- Rows where
filterByreturnsfalseare excluded from the table entirely.
Formatting
Use the formatter prop to transform cell values dynamically. This is useful also for applying custom logic to individual cells.
<FlexTable
data={data}
formatter={(val, rowIndex, key) =>
key === "date"
? new Date(val).toLocaleDateString()
: val
}
/>- The formatter function receives the cell value, the row index, and the column key (column name), allowing for context-aware formatting.
Examples
With custom styles
With formatter and horizontal orientation
With complex data
With complex generic type
Empty
Sortable with excluded columns
Dynamic filters and Sorting
Auxiliar Components
<RowTable />
A preconfigured wrapper for `FlexTable` with `orientation="vertical"`.
<RowTable data={data} />Use this when you want a traditional table layout.
<ColumnTable />
A preconfigured wrapper for `FlexTable` with `orientation="horizontal"`.
<ColumnTable data={data} />Use this when you want headers on the left and data flowing to the right.
How it Works
Internally, FlexTable transforms row-based data into column-oriented arrays using the formatTableData function. This enables consistent rendering in both orientations:
[
{ Name: "Alice", Age: 30 },
{ Name: "Bob", Age: 25 }
]becomes:
[
{ header: "Name", items: ["Alice", "Bob"] },
{ header: "Age", items: [30, 25] }
]Then:
- Vertical orientation renders using
gridwith rows and columns. - Horizontal orientation uses CSS
gridto transpose the layout.
Styling Tips
- You can use Tailwind classes via
className,headerClassName, andcellClassName. - Combine with
@/lib/utils'scnfor conditional styles. - Use
min-w-[300px],w-fit,border,rounded-md,bg-muted, etc., for layout control.
Utility Function
formatTableData(data: T[]): Column<T>[]
Converts an array of objects into a column-oriented structure.
Useful for custom renderers or logic beyond this component.
Related Files
FlexTable.tsx- Main componentRowTable,ColumnTable- Aliases for vertical/horizontal layoutformatTableData()- Utility used internally, but exportable
FlexTable Props
| Prop | Type | Default |
|---|---|---|
data* | T[] | — |
orientation | enum | "vertical" |
variant | enum | "default" |
className | string | — |
headerClassName | string | — |
cellClassName | string | — |
emptyMessage | React.ReactNode | "No data" |
formatter | function | — |
sortable | boolean | false |
filterBy | function | — |