Overview
Column helpers reduce boilerplate in table column definitions by providing common rendering patterns. They are shared between AdminListTableBlock and ListTableBlock for consistent column behavior.
Key Features
- Pre-built renderers for text, badges, dates, numbers, currency, percentages
- Automatic sorting configuration with sensible defaults
- User column with avatar and clickable profile
- Badge column with variant mapping and auto-generated options
- Currency/percentage with tooltip for full precision
- Custom column for full render control
Examples
Text Column
Simple text display with optional sorting and alignment.
| Name | Description |
|---|---|
| Alpha Project | Initial project setup |
| Beta Testing | User acceptance testing |
| Legacy Migration | Database migration completed |
columnHelpers.text('name', 'Name', { sortable: true })Primary Column
The row's main identifier with bold styling and optional description.
| Project Name | Status |
|---|---|
| Alpha Project | active |
| Beta Testing | pending |
| Legacy Migration | archived |
columnHelpers.primary('name', 'Project Name', {
descriptionKey: 'description',
sortable: true,
})Badge Column
Status indicators with color-coded variants. Supports editable mode with auto-generated options.
| Name | Status |
|---|---|
| Alpha Project | active |
| Beta Testing | pending |
| Legacy Migration | archived |
columnHelpers.badge('status', 'Status', {
active: 'success',
pending: 'warning',
archived: 'secondary',
})Date Column
Automatic relative formatting with tooltip for absolute date. Uses FormattedDate component.
| Name | Created | Created (Absolute) |
|---|---|---|
| Alpha Project | 2 days ago | Dec 11, 2025 @ 01:28 |
| Beta Testing | 5 days ago | Dec 8, 2025 @ 01:28 |
| Legacy Migration | last month | Nov 13, 2025 @ 01:28 |
// Relative (default)
columnHelpers.date('createdAt', 'Created', { sortable: true })
// Absolute with time
columnHelpers.date('createdAt', 'Created', { style: 'absolute', includeTime: true })Number Column
Formatted numbers with optional decimal precision and aggregation.
| Name | Count | Full | Score |
|---|---|---|---|
| Alpha Project | 1.23K | 1,234 | 1234.0 |
| Beta Testing | 45.7K | 45,678 | 45678.0 |
| Legacy Migration | 892K | 892,456 | 892456.0 |
columnHelpers.number('count', 'Count') // 1.2K, 45.7K (compact by default)
columnHelpers.number('id', 'ID', { compact: false }) // 1,234 (full number)
columnHelpers.number('score', 'Score', { decimals: 1 }) // 1234.0 (decimals disables compact)Currency Column
| Name | Budget | Budget (EUR) |
|---|---|---|
| Alpha Project | $1,251 | €1,251 |
| Beta Testing | $3,400 | €3,400 |
| Legacy Migration | $8,750 | €8,750 |
Currency display with integer format and full value tooltip. Footer aggregations formatted as currency.
columnHelpers.currency('amount', 'Budget')
columnHelpers.currency('amount', 'Budget (EUR)', { currency: 'EUR' })Percentage Column
| Name | Progress | Completion |
|---|---|---|
| Alpha Project | 86% | 86% |
| Beta Testing | 62% | 62% |
| Legacy Migration | 100% | 100% |
Percentage display with integer format and full value tooltip.
columnHelpers.percentage('progress', 'Progress')
columnHelpers.percentage('progress', 'Completion', { decimals: 1 })Boolean Column
| Name | Active | Status |
|---|---|---|
| Alpha Project | ✓ | active |
| Beta Testing | ✓ | pending |
| Legacy Migration | — | archived |
Checkmark/dash indicators for true/false values. Supports editable mode with toggle switch.
columnHelpers.boolean('isActive', 'Active')User Column
| Project | Created By |
|---|---|
| Alpha Project | |
| Beta Testing | |
| Legacy Migration |
User profile with avatar and name. Supports click handler for opening UserSheet.
columnHelpers.user('creator', 'Created By', {
onClick: (user) => openUserSheet(user.id),
})Sparkline Column
Inline trend visualization from array data
| Project | Responses | 7-Day Trend |
|---|---|---|
| Alpha Project | 1.23K | |
| Beta Testing | 45.7K | |
| Legacy Migration | 892K |
Displays a mini trend chart within the table cell. Perfect for showing historical trends alongside other data. Uses SparklineChart under the hood.
columnHelpers.sparkline('trend', '7-Day Trend', {
data: (item) => item.dailyResponses,
width: 80,
height: 24,
showArea: true,
showEndDot: false,
color: 'var(--chart-2)',
hideOnMobile: true,
})Star Rating Column
Star rating display with optional response count
| Feature | Satisfaction | Rating Only |
|---|---|---|
| Alpha Project | 71 | |
| Beta Testing | 65 | |
| Legacy Migration | 19 |
Displays star ratings (0-5) with support for half-stars. Optionally shows response count after the stars. Can be made interactive with the editable option for inline rating changes.
// With response count
columnHelpers.starRating('satisfaction', 'Satisfaction', {
rating: (item) => item.averageRating,
count: (item) => item.totalResponses,
})
// Rating only (no count)
columnHelpers.starRating('satisfaction', 'Rating', {
rating: (item) => item.averageRating,
})
// Interactive (editable)
columnHelpers.starRating('userRating', 'Your Rating', {
rating: (item) => item.userRating,
editable: { canEdit: (item) => !item.locked },
})KPI Summary Column
Multiple colored KPI badges in a single column
| Feature | Metrics |
|---|---|
| Alpha Project | A68-7010% |
| Beta Testing | B+48-5414% |
| Legacy Migration | C-15-4354% |
Displays 1-3 compact KPI values as colored badges. Each badge can have its own color variant (success, warning, destructive, info, muted) and optional tooltip. Perfect for showing multiple metrics in a single column.
columnHelpers.kpiSummary('metrics', 'Metrics', {
badgeWidth: 'lg', // 'sm' (40px) | 'md' (48px) | 'lg' (56px) - uniform width
values: (item) => [
{ value: item.healthGrade, variant: 'success', tooltip: 'Health' },
{ value: `${item.min}-${item.max}`, variant: 'info', tooltip: 'Range' },
{ value: `${item.impact}%`, variant: item.impact > 20 ? 'success' : 'muted' },
],
})Dot Rating Column
Dot-based rating display (0-X filled dots)
| Feature | Priority | Urgency |
|---|---|---|
| Alpha Project | ||
| Beta Testing | ||
| Legacy Migration |
Displays a row of dots where filled dots indicate the rating level. Supports custom max dots (up to 10) and color options. Can be made interactive for quick inline value changes.
// Basic dot rating
columnHelpers.dotRating('priority', 'Priority', {
value: (item) => item.priorityLevel,
maxDots: 5,
})
// Custom color
columnHelpers.dotRating('urgency', 'Urgency', {
value: (item) => item.urgencyLevel,
maxDots: 3,
color: 'destructive', // 'primary' | 'success' | 'warning' | 'destructive'
})
// Interactive (editable)
columnHelpers.dotRating('priority', 'Priority', {
value: (item) => item.priorityLevel,
maxDots: 5,
editable: { canEdit: (item) => !item.locked },
})Rating Bar + Score Badge Columns
Build survey question matrices with dynamic columns
| Question | 1 | 2 | 3 | 4 | 5 | Avg | n |
|---|---|---|---|---|---|---|---|
| Overall product satisfaction | 3 | 8 | 22 | 45 | 34 | 3.88 | 112 |
| Ease of use | 2 | 5 | 18 | 52 | 35 | 4.01 | 112 |
| Value for money | 12 | 22 | 35 | 28 | 15 | 3.11 | 112 |
Combine ratingBar and scoreBadge to build survey question matrices. The ratingBar shows response distribution with color-coded bars (red=low, green=high), while scoreBadge displays the average score with color coding. Generate columns dynamically for any rating scale (1-5, 0-10, etc.).
// Build survey matrix with dynamic rating columns
const getMaxCount = (item) => Math.max(...item.responses.map(r => r.count));
const columns = [
columnHelpers.text('question', 'Question'),
// Generate columns for each rating value
...[1, 2, 3, 4, 5].map((rating) =>
columnHelpers.ratingBar(`rating${rating}`, String(rating), {
count: (item) => item.responses.find(r => r.rating === rating)?.count ?? 0,
maxCount: getMaxCount,
rating, // For color coding
maxRating: 5, // Scale max
})
),
columnHelpers.scoreBadge('avgScore', 'Avg', {
score: (item) => item.averageScore,
maxScore: 5,
decimals: 2,
colorScale: 'diverging', // or 'sequential'
}),
columnHelpers.number('totalResponses', 'n'),
];Audience Access Column
| Name | Access |
|---|---|
| Alpha Project | Part. |
| Beta Testing | Build. |
| Legacy Migration | Pub. |
Displays audience access level with icon and label. Supports editable dropdown for inline level changes.
columnHelpers.audienceAccess('access', 'Access', {
editable: true,
onChange: (item, newLevel) => handleAccessChange(item.id, newLevel),
})Link Column
Clickable URLs with external icon and truncation
| Project | Website |
|---|---|
| Alpha Project | example.com |
| Beta Testing | beta.example.org |
| Legacy Migration | legacy.example.com |
Displays clickable links with automatic hostname extraction, external link icons, and truncation. Full URL shown in tooltip on hover.
columnHelpers.link('website', 'Website', {
maxLength: 30,
showExternalIcon: true,
})Email Column
Email addresses with mailto links and copy button
| Project | Contact |
|---|---|
| Alpha Project | |
| Beta Testing | |
| Legacy Migration |
Displays email addresses as mailto links with optional copy button on hover. Truncates long emails with tooltip showing the full address.
columnHelpers.email('contactEmail', 'Contact', {
showCopy: true,
maxLength: 25,
})Phone Column
Phone numbers with tel links and formatting
| Project | Phone |
|---|---|
| Alpha Project | |
| Beta Testing | |
| Legacy Migration |
Displays phone numbers as tel: links with automatic formatting. Copy button appears on hover for easy clipboard access.
columnHelpers.phone('phoneNumber', 'Phone', {
format: true,
showCopy: true,
})Thumbnail Column
Small image previews with hover expansion
| Cover | Project | Status |
|---|---|---|
| Alpha Project | active | |
| Beta Testing | pending | |
| Legacy Migration | archived |
Displays image thumbnails with customizable size (sm/md/lg). Supports fallback images and optional hover-to-expand for larger preview.
columnHelpers.thumbnail('coverImage', 'Cover', {
size: 'md',
fallback: '/placeholder.png',
expandOnHover: true,
})Tags Column
Multiple tags/pills with overflow handling
| Project | Labels |
|---|---|
| Alpha Project | featured enterprise +1 |
| Beta Testing | beta testing |
| Legacy Migration | archived |
Displays multiple tags as badges with configurable max visible count. Overflow shows "+N more" badge with tooltip listing remaining tags.
columnHelpers.tags('labels', 'Labels', {
tags: (item) => item.tags, // string[] or TagItem[]
maxVisible: 2,
defaultVariant: 'secondary',
})Code Column
Monospace styled IDs, SKUs, and version strings
| Project | SKU |
|---|---|
| Alpha Project | #ALPHA-001 |
| Beta Testing | #BETA-002 |
| Legacy Migration | #LEGACY-003 |
Displays code, IDs, or SKUs with monospace styling. Supports prefix (like # or v), copy button, and truncation for long values.
columnHelpers.code('sku', 'SKU', {
prefix: '#',
showCopy: true,
uppercase: false,
})File Size Column
Human-readable file sizes (KB, MB, GB)
| Project | Size |
|---|---|
| Alpha Project | 1.5 MB |
| Beta Testing | 512 KB |
| Legacy Migration | 1 GB |
Converts bytes to human-readable format (KB, MB, GB, etc.). Tooltip shows exact byte count. Supports different input units.
columnHelpers.fileSize('sizeBytes', 'Size', {
inputUnit: 'bytes', // 'bytes' | 'kb' | 'mb'
sortable: true,
})Duration Column
Human-readable time durations
| Project | Duration | Duration (Long) |
|---|---|---|
| Alpha Project | 1h 1m | 1 hour 1 minute |
| Beta Testing | 1m 30s | 1 minute 30 seconds |
| Legacy Migration | 1d | 1 day |
Formats milliseconds (or other units) to human-readable duration. Supports short (1h 30m), long (1 hour 30 minutes), and compact (1:30:00) styles.
columnHelpers.duration('elapsedMs', 'Duration', {
inputUnit: 'ms', // 'ms' | 'seconds' | 'minutes'
style: 'short', // 'short' | 'long' | 'compact'
maxUnits: 2,
})Status Dot Column
Colored status indicators with optional labels
| Project | Status |
|---|---|
| Alpha Project | Online |
| Beta Testing | Online |
| Legacy Migration | Offline |
Displays a colored dot indicator with optional text label. Supports pulse animation for "live" status indicators. Colors: success, warning, destructive, info, muted.
columnHelpers.statusDot('status', 'Status', {
color: (item) => item.isOnline ? 'success' : 'muted',
label: (item) => item.isOnline ? 'Online' : 'Offline',
pulse: true,
})Trend Column
Up/down arrows with delta values
| Project | Growth |
|---|---|
| Alpha Project | +12.5% |
| Beta Testing | -3.2% |
| Legacy Migration | 0.0% |
Displays trend indicators with directional arrows. Green for positive, red for negative, muted for flat. Use invertColors for metrics where up is bad (costs).
columnHelpers.trend('growth', 'Growth', {
value: (item) => item.growthPercent,
isPercentage: true,
invertColors: false, // true for costs
})Health Score Column
Letter grades with color coding
| Project | Health |
|---|---|
| Alpha Project | A |
| Beta Testing | B+ |
| Legacy Migration | C- |
Displays health scores as colored grade badges. Supports letter grades (A+, B, C-) or numeric scores that map to grades automatically.
// Letter grade
columnHelpers.healthScore('grade', 'Health', {
score: (item) => item.healthGrade,
type: 'grade',
})
// Numeric (auto-converts to grade)
columnHelpers.healthScore('score', 'Health', {
score: (item) => item.score,
type: 'numeric',
maxScore: 100,
})Avatar Stack Column
Overlapping user avatars with overflow
| Project | Team |
|---|---|
| Alpha Project | +1 |
| Beta Testing | |
| Legacy Migration |
Displays multiple user avatars in an overlapping stack. Shows "+N" overflow indicator when exceeding maxVisible. Tooltip shows all names.
columnHelpers.avatarStack('team', 'Team', {
users: (item) => item.assignees,
maxVisible: 3,
size: 'sm', // 'sm' | 'md'
onClick: (user) => openUserSheet(user.id),
})Mini Bar Column
Horizontal progress bars with value labels
| Project | Progress |
|---|---|
| Alpha Project | 17/20 |
| Beta Testing | 8/15 |
| Legacy Migration | 50/50 |
Displays a mini horizontal progress bar with optional value label. Supports percentage, fraction, or number formats. Color can be static or dynamic.
columnHelpers.miniBar('progress', 'Progress', {
value: (item) => item.completedTasks,
maxValue: (item) => item.totalTasks,
valueFormat: 'fraction', // 'percentage' | 'fraction' | 'number'
color: 'primary', // or (value, max) => 'success' | 'warning' | etc.
})Custom Column
| Name | Actions |
|---|---|
| Alpha Project | |
| Beta Testing | |
| Legacy Migration |
Full control over rendering with custom render function.
columnHelpers.custom('actions', 'Actions', {
render: (item) => <Button size="sm">View</Button>,
align: 'right',
})Available Helpers
| Prop | Type | Default | Description |
|---|---|---|---|
text(key, label, options?) | function | - | Simple text with optional sorting, alignment, and editing |
primary(key, label, options?) | function | - | Row identifier with bold styling and optional description |
badge(key, label, variants, options?) | function | - | Status badge with variant mapping (success, warning, destructive, etc.) |
date(key, label, options?) | function | - | Formatted date with relative/absolute display and FormattedDate |
number(key, label, options?) | function | - | Formatted number with locale-aware display and aggregation |
currency(key, label, options?) | function | - | Currency with integer display and full value tooltip |
percentage(key, label, options?) | function | - | Percentage with integer display and full value tooltip |
boolean(key, label, options?) | function | - | Checkmark/dash indicator with optional toggle editing |
user(key, label, options?) | function | - | User avatar and name with optional click handler |
sparkline(key, label, options) | function | - | Inline trend chart from array data (requires data function) |
starRating(key, label, options) | function | - | Star rating display with optional count (supports half-stars, interactive mode) |
kpiSummary(key, label, options) | function | - | Multiple colored KPI badges in a single column (1-3 values with tooltips) |
dotRating(key, label, options) | function | - | Dot-based rating display (0-X filled dots, customizable color, interactive mode) |
ratingBar(key, label, options) | function | - | Survey response distribution bar with count (for question matrices) |
scoreBadge(key, label, options) | function | - | Color-coded score badge (diverging or sequential scale) |
audienceAccess(key, label, options?) | function | - | Audience access level with icon and editable dropdown |
avatarStack(key, label, options) | function | - | Stacked avatars for multiple users with +N overflow indicator and tooltip |
link(key, label, options?) | function | - | Clickable URLs with external icon, truncation, and hostname extraction |
email(key, label, options?) | function | - | Email addresses with mailto: links and optional copy button |
phone(key, label, options?) | function | - | Phone numbers with tel: links, formatting, and optional copy button |
thumbnail(key, label, options?) | function | - | Image previews with size options, fallback, and optional hover expansion |
tags(key, label, options) | function | - | Multiple badges/tags with overflow handling (+N more with tooltip) |
code(key, label, options?) | function | - | Monospace styled IDs/SKUs/codes with optional prefix and copy button |
fileSize(key, label, options?) | function | - | Human-readable byte formatting (KB, MB, GB) with SI/binary options |
duration(key, label, options?) | function | - | Time duration formatting in short, long, or compact formats |
statusDot(key, label, options) | function | - | Colored status dot with optional label and pulse animation |
trend(key, label, options) | function | - | Up/down trend arrows with delta values and color coding |
healthScore(key, label, options) | function | - | Letter grades (A+, B, C-) with color-coded backgrounds |
miniBar(key, label, options) | function | - | Horizontal progress bar with value label (percentage, fraction, or number) |
custom(key, label, options) | function | - | Full control with custom render function and all options |
Usage Patterns
import { columnHelpers, ListTableBlock } from '@/ui/blocks';
const columns = [
columnHelpers.primary('name', 'Project Name', {
descriptionKey: 'description',
sortable: true,
}),
columnHelpers.badge('status', 'Status', {
active: 'success',
pending: 'warning',
archived: 'secondary',
}),
columnHelpers.date('createdAt', 'Created'),
columnHelpers.currency('amount', 'Budget'),
columnHelpers.percentage('progress', 'Progress'),
columnHelpers.boolean('isActive', 'Active'),
columnHelpers.user('creator', 'Created By', {
onClick: (user) => openUserSheet(user.id),
}),
columnHelpers.sparkline('trend', 'Trend', {
data: (item) => item.weeklyData,
}),
columnHelpers.custom('actions', 'Actions', {
render: (item) => <Button size="sm">View</Button>,
align: 'right',
}),
];
<ListTableBlock items={projects} columns={columns} />