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.json
Overview
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.
grid
under the hood. Responsive layout: Uses - Fully styleable: Custom class names for headers, cells, and container.
- Formatter support: Format individual cell content dynamically.
RowTable
andColumnTable
wrappers. Composable variants: Includes - Empty state handling: Customize fallback for empty data.
sortable
; click headers to toggle asc ↔ desc ↔ none. Sortable columns: Enable by passing filterBy
function to include/exclude rows before rendering. Filtering: Provide a
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
filterBy
returnsfalse
are 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
grid
with rows and columns. - Horizontal orientation uses CSS
grid
to transpose the layout.
Styling Tips
- You can use Tailwind classes via
className
,headerClassName
, andcellClassName
. - Combine with
@/lib/utils
'scn
for 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 | — |