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.

Task
Hours
Design
12h
Development
30h
Testing
8h

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.
  • Responsive layout: Uses grid under the hood.
  • Fully styleable: Custom class names for headers, cells, and container.
  • Formatter support: Format individual cell content dynamically.
  • Composable variants: Includes RowTable and ColumnTable wrappers.
  • Empty state handling: Customize fallback for empty data.
  • Sortable columns: Enable by passing sortable; click headers to toggle asc ↔ desc ↔ none.
  • Filtering: Provide a filterBy function 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

flex-table-basic.tsx
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 returns false 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

Name
Age
Alice
30
Bob
25

With formatter and horizontal orientation

name
John Doe
Jane Smith
Bob Johnson
email
john@example.com
jane@example.com
bob@example.com
age
21
27
33
status
Active
Inactive
Active

With complex data

User
Active
Registry
Jane
2/1/2023
John
5/15/2023

With complex generic type

id
name
price
stock
a1
T-Shirt
$4500
12
b2
Pants
$8200
4
c3
Shoes
$13200
Out of stock

Empty

No data

Sortable with excluded columns

name
price
category
stock
status
date
T-shirt
$19.99
Apparel
120
active
4/15/2025
Urban Backpack
$39.5
Accessories
80
active
3/1/2025
Thermos Bottle
$25
Home Goods
200
active
5/12/2025

Dynamic filters and Sorting

Name
Age
Occupation
John
25 y/o
Engineer
Jane
32 y/o
Designer
Bob
41 y/o
Chef
Charles
29 y/o
Doctor

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, and cellClassName.
  • Combine with @/lib/utils's cn 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 component
  • RowTable, ColumnTable - Aliases for vertical/horizontal layout
  • formatTableData() - Utility used internally, but exportable

FlexTable Props

PropTypeDefault
data*
T[]
orientation
enum"vertical"
variant
enum"default"
className
string
headerClassName
string
cellClassName
string
emptyMessage
React.ReactNode"No data"
formatter
function
sortable
booleanfalse
filterBy
function