- Status
- Active
- Plan
- Enterprise
- Seats
- 105
- MRR
- $15,800
- Renewal
- Nov 19
beUI Pro for mobile is live — 50 React Native blocks and screens
Data Table
Three reusable data table components with virtualization, pagination, responsive mobile cards, filtering, tabs, sorting, selection, and customizable columns.
Responsive data table
responsive-data-table.tsxA responsive data table with animated tabs, search, multi-select filters, column visibility controls, sortable headers, and a purpose-built mobile card view.
- Status
- Active
- Plan
- Enterprise
- Seats
- 84
- MRR
- $12,400
- Renewal
- Sep 24
- Status
- Trial
- Plan
- Enterprise
- Seats
- 61
- MRR
- $9,100
- Renewal
- Nov 08
- Status
- Active
- Plan
- Scale
- Seats
- 46
- MRR
- $7,200
- Renewal
- Oct 11
- Status
- Active
- Plan
- Scale
- Seats
- 38
- MRR
- $5,900
- Renewal
- Dec 12
- Status
- Trial
- Plan
- Scale
- Seats
- 32
- MRR
- $4,800
- Renewal
- Oct 02
- Status
- Active
- Plan
- Scale
- Seats
- 29
- MRR
- $3,650
- Renewal
- Nov 01
- Status
- Paused
- Plan
- Starter
- Seats
- 12
- MRR
- $1,200
- Renewal
- Dec 03
"use client";
import { useMemo } from "react";
import {
AnimatedBadge,
type AnimatedBadgeStatus,
} from "@/components/motion/animated-badge";
import {
ResponsiveDataTable,
type ResponsiveDataTableColumn,
type ResponsiveDataTableTab,
} from "@/components/premium/responsive-data-table";
type Account = {
id: string;
company: string;
contact: string;
channel: "Direct" | "Partner" | "Product-led";
status: "Active" | "Trial" | "Paused";
plan: "Starter" | "Scale" | "Enterprise";
seats: number;
mrr: number;
renewal: string;
};
const accounts: Account[] = [
{
id: "arc-01",
company: "Arc Studio",
contact: "maya@arc.studio",
channel: "Direct",
status: "Active",
plan: "Enterprise",
seats: 84,
mrr: 12400,
renewal: "Sep 24",
},
{
id: "north-02",
company: "Northstar Labs",
contact: "jon@northstar.dev",
channel: "Partner",
status: "Trial",
plan: "Scale",
seats: 32,
mrr: 4800,
renewal: "Oct 02",
},
{
id: "kin-03",
company: "Kin Systems",
contact: "nora@kin.systems",
channel: "Product-led",
status: "Active",
plan: "Scale",
seats: 46,
mrr: 7200,
renewal: "Oct 11",
},
{
id: "mono-04",
company: "Mono Works",
contact: "eli@mono.works",
channel: "Direct",
status: "Paused",
plan: "Starter",
seats: 8,
mrr: 890,
renewal: "Oct 18",
},
{
id: "field-05",
company: "Field Notes",
contact: "ava@fieldnotes.co",
channel: "Product-led",
status: "Active",
plan: "Scale",
seats: 29,
mrr: 3650,
renewal: "Nov 01",
},
{
id: "orbit-06",
company: "Orbit Finance",
contact: "noah@orbit.finance",
channel: "Partner",
status: "Trial",
plan: "Enterprise",
seats: 61,
mrr: 9100,
renewal: "Nov 08",
},
{
id: "plume-07",
company: "Plume AI",
contact: "lena@plume.ai",
channel: "Direct",
status: "Active",
plan: "Enterprise",
seats: 105,
mrr: 15800,
renewal: "Nov 19",
},
{
id: "form-08",
company: "Form & Co.",
contact: "milo@form.co",
channel: "Product-led",
status: "Paused",
plan: "Starter",
seats: 12,
mrr: 1200,
renewal: "Dec 03",
},
{
id: "signal-09",
company: "Signal House",
contact: "sara@signal.house",
channel: "Partner",
status: "Active",
plan: "Scale",
seats: 38,
mrr: 5900,
renewal: "Dec 12",
},
];
const statusTone: Record<Account["status"], AnimatedBadgeStatus> = {
Active: "success",
Trial: "info",
Paused: "warning",
};
function AccountIdentity({ account }: { account: Account }) {
return (
<div className="flex min-w-0 items-center gap-3">
<span className="grid size-9 shrink-0 place-items-center rounded-full bg-primary font-medium text-primary-foreground text-xs">
{account.company
.split(" ")
.map((word) => word[0])
.join("")
.slice(0, 2)}
</span>
<span className="min-w-0">
<span className="block truncate font-medium text-sm">
{account.company}
</span>
<span className="block truncate text-muted-foreground text-xs">
{account.contact}
</span>
</span>
</div>
);
}
export function ResponsiveDataTablePreview() {
const columns = useMemo<ResponsiveDataTableColumn<Account>[]>(
() => [
{
key: "company",
header: "Account",
label: "Account",
sortable: true,
hideable: false,
width: "30%",
cell: (account) => <AccountIdentity account={account} />,
},
{
key: "status",
header: "Status",
sortable: true,
width: "110px",
cell: (account) => (
<AnimatedBadge
status={statusTone[account.status]}
size="sm"
contentKey={account.status}
>
{account.status}
</AnimatedBadge>
),
},
{
key: "plan",
header: "Plan",
sortable: true,
width: "120px",
},
{
key: "seats",
header: "Seats",
sortable: true,
width: "90px",
cell: (account) => (
<span className="tabular-nums">{account.seats}</span>
),
},
{
key: "mrr",
header: "MRR",
sortable: true,
align: "right",
width: "120px",
cell: (account) => (
<span className="font-medium tabular-nums">
${account.mrr.toLocaleString()}
</span>
),
},
{
key: "renewal",
header: "Renewal",
width: "110px",
align: "right",
},
],
[],
);
const tabs = useMemo<ResponsiveDataTableTab<Account>[]>(
() => [
{ value: "all", label: "All accounts" },
{
value: "direct",
label: "Direct",
filter: (account) => account.channel === "Direct",
},
{
value: "partner",
label: "Partner",
filter: (account) => account.channel === "Partner",
},
{
value: "product-led",
label: "Product-led",
filter: (account) => account.channel === "Product-led",
},
],
[],
);
return (
<div className="w-full p-2 sm:p-5">
<ResponsiveDataTable
data={accounts}
columns={columns}
getRowId={(account) => account.id}
tabs={tabs}
filter={{
label: "Status",
options: [
{ value: "Active", label: "Active" },
{ value: "Trial", label: "Trial" },
{ value: "Paused", label: "Paused" },
],
getValue: (account) => account.status,
}}
search={{
placeholder: "Search accounts",
getSearchText: (account) =>
`${account.company} ${account.contact} ${account.plan}`,
}}
defaultSort={{ key: "mrr", direction: "desc" }}
defaultVisibleColumns={[
"company",
"status",
"plan",
"seats",
"mrr",
"renewal",
]}
/>
</div>
);
}
Source code is locked
Get access to install premium components and view the full source.
Open dashboardInstall
Data table
data-table.tsxA standalone table surface with 10,000 virtualized rows, sorting, selection, and custom cell rendering.
"use client";
import { useMemo, useState } from "react";
import {
DataTable,
type DataTableColumn,
} from "@/components/premium/data-table";
import { cn } from "@/lib/utils";
type Customer = {
id: string;
name: string;
email: string;
plan: "Starter" | "Pro" | "Business";
status: "Active" | "Trial" | "Paused";
seats: number;
spend: number;
};
const FIRST_NAMES = [
"Maya",
"Jon",
"Lena",
"Milo",
"Ava",
"Noah",
"Nora",
"Eli",
];
const LAST_NAMES = [
"Bennett",
"Bell",
"Ortiz",
"Carter",
"Robinson",
"Williams",
"Kim",
"Thompson",
];
const PLANS: Customer["plan"][] = ["Starter", "Pro", "Business"];
const STATUSES: Customer["status"][] = ["Active", "Trial", "Paused"];
function createCustomers(count: number): Customer[] {
return Array.from({ length: count }, (_, index) => {
const firstName = FIRST_NAMES[index % FIRST_NAMES.length];
const lastName = LAST_NAMES[(index * 5) % LAST_NAMES.length];
return {
id: `customer-${index + 1}`,
name: `${firstName} ${lastName}`,
email: `${firstName}.${lastName}${index + 1}@example.com`.toLowerCase(),
plan: PLANS[index % PLANS.length],
status: STATUSES[(index * 2) % STATUSES.length],
seats: 1 + ((index * 3) % 24),
spend: 29 + ((index * 47) % 1970),
};
});
}
const STATUS_STYLES: Record<Customer["status"], string> = {
Active: "bg-primary text-primary-foreground",
Trial: "bg-muted text-foreground",
Paused: "border border-border bg-background text-muted-foreground",
};
export function DataTablePreview() {
const data = useMemo(() => createCustomers(10_000), []);
const [selected, setSelected] = useState<string[]>([]);
const columns = useMemo<DataTableColumn<Customer>[]>(
() => [
{
key: "name",
header: "Customer",
sortable: true,
width: "38%",
cell: (customer) => (
<div className="min-w-0">
<p className="truncate font-medium">{customer.name}</p>
<p className="truncate text-muted-foreground text-xs">
{customer.email}
</p>
</div>
),
},
{
key: "plan",
header: "Plan",
sortable: true,
align: "center",
width: "130px",
},
{
key: "status",
header: "Status",
sortable: true,
align: "center",
width: "130px",
cell: (customer) => (
<span
className={cn(
"inline-flex rounded-full px-2.5 py-1 font-medium text-xs",
STATUS_STYLES[customer.status],
)}
>
{customer.status}
</span>
),
},
{
key: "seats",
header: "Seats",
sortable: true,
align: "center",
width: "100px",
cell: (customer) => (
<span className="tabular-nums">{customer.seats}</span>
),
},
{
key: "spend",
header: "Spend",
sortable: true,
align: "right",
width: "120px",
cell: (customer) => (
<span className="font-medium tabular-nums">
${customer.spend.toLocaleString()}
</span>
),
},
],
[],
);
return (
<DataTable
data={data}
columns={columns}
getRowId={(customer) => customer.id}
selectable
selectedRowIds={selected}
onSelectionChange={setSelected}
defaultSort={{ key: "spend", direction: "desc" }}
height={448}
rowHeight={60}
className="rounded-xl"
/>
);
}
Source code is locked
Get access to install premium components and view the full source.
Open dashboardInstall
Paginated data table
data-table-pagination.tsxA composable paginated table with global sorting, controlled or uncontrolled page state, page-size controls, accessible navigation, and stable row sizing.
"use client";
import { useMemo } from "react";
import {
type DataTableColumn,
PaginatedDataTable,
} from "@/components/premium/data-table-pagination";
import { cn } from "@/lib/utils";
type Subscription = {
id: string;
customer: string;
email: string;
plan: "Starter" | "Pro" | "Business";
status: "Active" | "Trial" | "Paused";
seats: number;
revenue: number;
};
const FIRST_NAMES = [
"Maya",
"Noah",
"Lena",
"Eli",
"Ava",
"Jon",
"Nora",
"Milo",
] as const;
const LAST_NAMES = [
"Bennett",
"Williams",
"Ortiz",
"Thompson",
"Robinson",
"Bell",
"Kim",
"Carter",
] as const;
const PLANS: Subscription["plan"][] = ["Starter", "Pro", "Business"];
const STATUSES: Subscription["status"][] = ["Active", "Trial", "Paused"];
function buildSubscriptions(count = 237): Subscription[] {
return Array.from({ length: count }, (_, index) => {
const first = FIRST_NAMES[index % FIRST_NAMES.length];
const last = LAST_NAMES[(index * 5 + Math.floor(index / 8)) % 8];
return {
id: `subscription-${index + 1}`,
customer: `${first} ${last}`,
email: `${first}.${last}${index + 1}@example.com`.toLowerCase(),
plan: PLANS[(index * 2) % PLANS.length],
status: STATUSES[(index * 5) % STATUSES.length],
seats: 1 + ((index * 7) % 48),
revenue: 29 + ((index * 47) % 1970),
};
});
}
const STATUS_STYLES: Record<Subscription["status"], string> = {
Active: "bg-primary/10 text-foreground",
Trial: "bg-muted text-foreground",
Paused: "border border-border bg-background text-muted-foreground",
};
export function DataTablePaginationPreview() {
const data = useMemo(() => buildSubscriptions(), []);
const columns = useMemo<DataTableColumn<Subscription>[]>(
() => [
{
key: "customer",
header: "Customer",
sortable: true,
width: "34%",
cell: (row) => (
<div className="min-w-0">
<p className="truncate font-medium">{row.customer}</p>
<p className="truncate text-muted-foreground text-xs">
{row.email}
</p>
</div>
),
},
{
key: "plan",
header: "Plan",
sortable: true,
align: "center",
width: "17%",
},
{
key: "status",
header: "Status",
sortable: true,
align: "center",
width: "18%",
cell: (row) => (
<span
className={cn(
"inline-flex rounded-full px-2.5 py-1 font-medium text-xs",
STATUS_STYLES[row.status],
)}
>
{row.status}
</span>
),
},
{
key: "seats",
header: "Seats",
sortable: true,
align: "center",
width: "13%",
cell: (row) => <span className="tabular-nums">{row.seats}</span>,
},
{
key: "revenue",
header: "MRR",
sortable: true,
align: "right",
width: "18%",
cell: (row) => (
<span className="font-medium tabular-nums">
${row.revenue.toLocaleString()}
</span>
),
},
],
[],
);
return (
<PaginatedDataTable
data={data}
columns={columns}
getRowId={(row) => row.id}
defaultSort={{ key: "revenue", direction: "desc" }}
defaultPageSize={10}
pageSizeOptions={[10, 25, 50]}
rowHeight={56}
className="rounded-xl"
tableClassName="[&_thead]:bg-muted/25"
/>
);
}
Source code is locked
Get access to install premium components and view the full source.
Open dashboard