Data Table

A component for displaying data in a table format with sorting and pagination

Multi-Select with Page Size Selector

Standard table with checkbox multi-selection and page size options


Code Svelte
1
2<script lang="ts">
3	import { DataTable, DataTablePrimitives } from "@kareyes/aether";
4
5	const { DataTableCheckbox, DataTableColumnHeader, renderComponent } = DataTablePrimitives;
6
7	type Payment = {
8		id: string;
9		amount: number;
10		status: "Pending" | "Processing" | "Success" | "Failed";
11		email: string;
12	};
13
14	const data: Payment[] = [
15		{ id: "m5gr84i9", amount: 316, status: "Success", email: "ken99@yahoo.com" },
16		{ id: "3u1reuv4", amount: 242, status: "Success", email: "Abe45@gmail.com" },
17		{ id: "derv1ws0", amount: 837, status: "Processing", email: "Monserrat44@gmail.com" },
18		{ id: "5kma53ae", amount: 874, status: "Success", email: "Silas22@gmail.com" },
19		{ id: "bhqecj4p", amount: 721, status: "Failed", email: "carmella@hotmail.com" },
20		{ id: "a1b2c3d4", amount: 450, status: "Pending", email: "john.doe@example.com" },
21		{ id: "e5f6g7h8", amount: 920, status: "Success", email: "jane.smith@gmail.com" },
22		{ id: "i9j0k1l2", amount: 350, status: "Processing", email: "bob.wilson@yahoo.com" },
23		{ id: "m3n4o5p6", amount: 680, status: "Failed", email: "alice.brown@hotmail.com" },
24		{ id: "q7r8s9t0", amount: 530, status: "Success", email: "charlie.davis@gmail.com" },
25		{ id: "u1v2w3x4", amount: 290, status: "Pending", email: "diana.miller@example.com" },
26		{ id: "y5z6a7b8", amount: 775, status: "Processing", email: "edward.jones@yahoo.com" },
27	];
28
29	const columns: DataTablePrimitives.ColumnDef<Payment>[] = [
30		{
31			id: "select",
32			header: ({ table }: DataTablePrimitives.HeaderContext<Payment, unknown>) =>
33				renderComponent(DataTableCheckbox, {
34					checked: table.getIsAllPageRowsSelected(),
35					indeterminate: table.getIsSomePageRowsSelected() && !table.getIsAllPageRowsSelected(),
36					onCheckedChange: (value) => table.toggleAllPageRowsSelected(!!value),
37					"aria-label": "Select all",
38				}),
39			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) =>
40				renderComponent(DataTableCheckbox, {
41					checked: row.getIsSelected(),
42					onCheckedChange: (value) => row.toggleSelected(!!value),
43					"aria-label": "Select row",
44				}),
45			enableSorting: false,
46			enableHiding: false,
47		},
48		{
49			accessorKey: "status",
50			header: "Status",
51			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) => {
52				const statusSnippet = createRawSnippet<[{ status: string }]>((getStatus) => {
53					const { status } = getStatus();
54					return { render: () => `<div class="capitalize">${status}</div>` };
55				});
56				return renderSnippet(statusSnippet, { status: row.original.status });
57			},
58		},
59		{
60			accessorKey: "email",
61			header: ({ column }: DataTablePrimitives.HeaderContext<Payment, unknown>) =>
62				renderComponent(DataTableColumnHeader, {
63					title: "Email",
64					onclick: column.getToggleSortingHandler(),
65				}),
66			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) => {
67				const emailSnippet = createRawSnippet<[{ email: string }]>((getEmail) => {
68					const { email } = getEmail();
69					return { render: () => `<div class="lowercase">${email}</div>` };
70				});
71				return renderSnippet(emailSnippet, { email: row.original.email });
72			},
73		},
74		{
75			accessorKey: "amount",
76			header: () => {
77				const amountHeaderSnippet = createRawSnippet(() => {
78					return { render: () => `<div class="text-end">Amount</div>` };
79				});
80				return renderSnippet(amountHeaderSnippet);
81			},
82			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) => {
83				const formatter = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" });
84				const amountCellSnippet = createRawSnippet<[{ amount: number }]>((getAmount) => {
85					const { amount } = getAmount();
86					const formatted = formatter.format(amount);
87					return { render: () => `<div class="text-end font-medium">${formatted}</div>` };
88				});
89				return renderSnippet(amountCellSnippet, { amount: row.original.amount });
90			},
91		},
92		{
93			id: "actions",
94			enableHiding: false,
95			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) =>
96				renderComponent(DataTableActions, {
97					id: row.original.id,
98					copyLabel: "Copy payment ID",
99					actions: [
100						{ label: "View details", onclick: () => console.log("View details for", row.original.id) },
101						{ label: "Edit payment", onclick: () => console.log("Edit payment", row.original.id) },
102						{ label: "Delete", onclick: () => console.log("Delete payment", row.original.id) },
103					],
104				}),
105		},
106	];
107</script>
108
109<DataTable
110	{data}
111	{columns}
112	filterColumn="email"
113	filterPlaceholder="Filter emails..."
114	pageSize={5}
115	pageSizeOptions={[5, 10, 20, 50]}
116	selectionMode="multi"
117	onRowSelectionChange={(selected) => console.log("Selected rows:", selected)}
118/>

Single Select

Table with single row selection (radio button behavior)


Code Svelte
1
2<script lang="ts">
3	import { DataTable } from "@kareyes/aether";
4	
5	const { DataTableCheckbox, DataTableColumnHeader, renderComponent } = DataTablePrimitives;
6
7	type Payment = {
8		id: string;
9		amount: number;
10		status: "Pending" | "Processing" | "Success" | "Failed";
11		email: string;
12	};
13
14	const data: Payment[] = [
15		{ id: "m5gr84i9", amount: 316, status: "Success", email: "ken99@yahoo.com" },
16		{ id: "3u1reuv4", amount: 242, status: "Success", email: "Abe45@gmail.com" },
17		{ id: "derv1ws0", amount: 837, status: "Processing", email: "Monserrat44@gmail.com" },
18		{ id: "5kma53ae", amount: 874, status: "Success", email: "Silas22@gmail.com" },
19		{ id: "bhqecj4p", amount: 721, status: "Failed", email: "carmella@hotmail.com" },
20		{ id: "a1b2c3d4", amount: 450, status: "Pending", email: "john.doe@example.com" },
21		{ id: "e5f6g7h8", amount: 920, status: "Success", email: "jane.smith@gmail.com" },
22		{ id: "i9j0k1l2", amount: 350, status: "Processing", email: "bob.wilson@yahoo.com" },
23		{ id: "m3n4o5p6", amount: 680, status: "Failed", email: "alice.brown@hotmail.com" },
24		{ id: "q7r8s9t0", amount: 530, status: "Success", email: "charlie.davis@gmail.com" },
25		{ id: "u1v2w3x4", amount: 290, status: "Pending", email: "diana.miller@example.com" },
26		{ id: "y5z6a7b8", amount: 775, status: "Processing", email: "edward.jones@yahoo.com" },
27	];
28
29	const columns: DataTablePrimitives.ColumnDef<Payment>[] = [
30		{
31			id: "select",
32			header: ({ table }: DataTablePrimitives.HeaderContext<Payment, unknown>) =>
33				renderComponent(DataTableCheckbox, {
34					checked: table.getIsAllPageRowsSelected(),
35					indeterminate: table.getIsSomePageRowsSelected() && !table.getIsAllPageRowsSelected(),
36					onCheckedChange: (value) => table.toggleAllPageRowsSelected(!!value),
37					"aria-label": "Select all",
38				}),
39			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) =>
40				renderComponent(DataTableCheckbox, {
41					checked: row.getIsSelected(),
42					onCheckedChange: (value) => row.toggleSelected(!!value),
43					"aria-label": "Select row",
44				}),
45			enableSorting: false,
46			enableHiding: false,
47		},
48		{
49			accessorKey: "status",
50			header: "Status",
51			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) => {
52				const statusSnippet = createRawSnippet<[{ status: string }]>((getStatus) => {
53					const { status } = getStatus();
54					return { render: () => `<div class="capitalize">${status}</div>` };
55				});
56				return renderSnippet(statusSnippet, { status: row.original.status });
57			},
58		},
59		{
60			accessorKey: "email",
61			header: ({ column }: DataTablePrimitives.HeaderContext<Payment, unknown>) =>
62				renderComponent(DataTableColumnHeader, {
63					title: "Email",
64					onclick: column.getToggleSortingHandler(),
65				}),
66			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) => {
67				const emailSnippet = createRawSnippet<[{ email: string }]>((getEmail) => {
68					const { email } = getEmail();
69					return { render: () => `<div class="lowercase">${email}</div>` };
70				});
71				return renderSnippet(emailSnippet, { email: row.original.email });
72			},
73		},
74		{
75			accessorKey: "amount",
76			header: () => {
77				const amountHeaderSnippet = createRawSnippet(() => {
78					return { render: () => `<div class="text-end">Amount</div>` };
79				});
80				return renderSnippet(amountHeaderSnippet);
81			},
82			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) => {
83				const formatter = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" });
84				const amountCellSnippet = createRawSnippet<[{ amount: number }]>((getAmount) => {
85					const { amount } = getAmount();
86					const formatted = formatter.format(amount);
87					return { render: () => `<div class="text-end font-medium">${formatted}</div>` };
88				});
89				return renderSnippet(amountCellSnippet, { amount: row.original.amount });
90			},
91		},
92		{
93			id: "actions",
94			enableHiding: false,
95			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) =>
96				renderComponent(DataTableActions, {
97					id: row.original.id,
98					copyLabel: "Copy payment ID",
99					actions: [
100						{ label: "View details", onclick: () => console.log("View details for", row.original.id) },
101						{ label: "Edit payment", onclick: () => console.log("Edit payment", row.original.id) },
102						{ label: "Delete", onclick: () => console.log("Delete payment", row.original.id) },
103					],
104				}),
105		},
106	];
107</script>
108
109<DataTable
110	{data}
111	{columns}
112	filterColumn="email"
113	filterPlaceholder="Filter emails..."
114	pageSize={5}
115	selectionMode="single"
116	onRowSelectionChange={(selected) => console.log("Selected row:", selected)}
117/>

No Selection

Table without row selection capability


Code Svelte
1
2<script lang="ts">
3	import { DataTable } from "@kareyes/aether";
4	
5	const { DataTableCheckbox, DataTableColumnHeader, renderComponent } = DataTablePrimitives;
6
7	type Payment = {
8		id: string;
9		amount: number;
10		status: "Pending" | "Processing" | "Success" | "Failed";
11		email: string;
12	};
13
14	const data: Payment[] = [
15		{ id: "m5gr84i9", amount: 316, status: "Success", email: "ken99@yahoo.com" },
16		{ id: "3u1reuv4", amount: 242, status: "Success", email: "Abe45@gmail.com" },
17		{ id: "derv1ws0", amount: 837, status: "Processing", email: "Monserrat44@gmail.com" },
18		{ id: "5kma53ae", amount: 874, status: "Success", email: "Silas22@gmail.com" },
19		{ id: "bhqecj4p", amount: 721, status: "Failed", email: "carmella@hotmail.com" },
20		{ id: "a1b2c3d4", amount: 450, status: "Pending", email: "john.doe@example.com" },
21		{ id: "e5f6g7h8", amount: 920, status: "Success", email: "jane.smith@gmail.com" },
22		{ id: "i9j0k1l2", amount: 350, status: "Processing", email: "bob.wilson@yahoo.com" },
23		{ id: "m3n4o5p6", amount: 680, status: "Failed", email: "alice.brown@hotmail.com" },
24		{ id: "q7r8s9t0", amount: 530, status: "Success", email: "charlie.davis@gmail.com" },
25		{ id: "u1v2w3x4", amount: 290, status: "Pending", email: "diana.miller@example.com" },
26		{ id: "y5z6a7b8", amount: 775, status: "Processing", email: "edward.jones@yahoo.com" },
27	];
28
29	const columns: DataTablePrimitives.ColumnDef<Payment>[] = [
30		{
31			accessorKey: "status",
32			header: "Status",
33			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) => {
34				const statusSnippet = createRawSnippet<[{ status: string }]>((getStatus) => {
35					const { status } = getStatus();
36					return { render: () => `<div class="capitalize">${status}</div>` };
37				});
38				return renderSnippet(statusSnippet, { status: row.original.status });
39			},
40		},
41		{
42			accessorKey: "email",
43			header: ({ column }: DataTablePrimitives.HeaderContext<Payment, unknown>) =>
44				renderComponent(DataTableColumnHeader, {
45					title: "Email",
46					onclick: column.getToggleSortingHandler(),
47				}),
48			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) => {
49				const emailSnippet = createRawSnippet<[{ email: string }]>((getEmail) => {
50					const { email } = getEmail();
51					return { render: () => `<div class="lowercase">${email}</div>` };
52				});
53				return renderSnippet(emailSnippet, { email: row.original.email });
54			},
55		},
56		{
57			accessorKey: "amount",
58			header: () => {
59				const amountHeaderSnippet = createRawSnippet(() => {
60					return { render: () => `<div class="text-end">Amount</div>` };
61				});
62				return renderSnippet(amountHeaderSnippet);
63			},
64			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) => {
65				const formatter = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" });
66				const amountCellSnippet = createRawSnippet<[{ amount: number }]>((getAmount) => {
67					const { amount } = getAmount();
68					const formatted = formatter.format(amount);
69					return { render: () => `<div class="text-end font-medium">${formatted}</div>` };
70				});
71				return renderSnippet(amountCellSnippet, { amount: row.original.amount });
72			},
73		},
74		{
75			id: "actions",
76			enableHiding: false,
77			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) =>
78				renderComponent(DataTableActions, {
79					id: row.original.id,
80					copyLabel: "Copy payment ID",
81					actions: [
82						{ label: "View details", onclick: () => console.log("View details for", row.original.id) },
83						{ label: "Edit payment", onclick: () => console.log("Edit payment", row.original.id) },
84						{ label: "Delete", onclick: () => console.log("Delete payment", row.original.id) },
85					],
86				}),
87		},
88	];
89</script>
90
91<DataTable
92	{data}
93	{columns}
94	filterColumn="email"
95	filterPlaceholder="Filter emails..."
96	pageSize={5}
97	selectionMode="none"
98/>

Striped Variant

Alternating row background colors for better readability


Code Svelte
1
2<script lang="ts">
3	import { DataTable } from "@kareyes/aether";
4	
5	const { DataTableCheckbox, DataTableColumnHeader, renderComponent } = DataTablePrimitives;
6
7	type Payment = {
8		id: string;
9		amount: number;
10		status: "Pending" | "Processing" | "Success" | "Failed";
11		email: string;
12	};
13
14	const data: Payment[] = [
15		{ id: "m5gr84i9", amount: 316, status: "Success", email: "ken99@yahoo.com" },
16		{ id: "3u1reuv4", amount: 242, status: "Success", email: "Abe45@gmail.com" },
17		{ id: "derv1ws0", amount: 837, status: "Processing", email: "Monserrat44@gmail.com" },
18		{ id: "5kma53ae", amount: 874, status: "Success", email: "Silas22@gmail.com" },
19		{ id: "bhqecj4p", amount: 721, status: "Failed", email: "carmella@hotmail.com" },
20		{ id: "a1b2c3d4", amount: 450, status: "Pending", email: "john.doe@example.com" },
21		{ id: "e5f6g7h8", amount: 920, status: "Success", email: "jane.smith@gmail.com" },
22		{ id: "i9j0k1l2", amount: 350, status: "Processing", email: "bob.wilson@yahoo.com" },
23		{ id: "m3n4o5p6", amount: 680, status: "Failed", email: "alice.brown@hotmail.com" },
24		{ id: "q7r8s9t0", amount: 530, status: "Success", email: "charlie.davis@gmail.com" },
25		{ id: "u1v2w3x4", amount: 290, status: "Pending", email: "diana.miller@example.com" },
26		{ id: "y5z6a7b8", amount: 775, status: "Processing", email: "edward.jones@yahoo.com" },
27	];
28
29	const columns: DataTablePrimitives.ColumnDef<Payment>[] = [
30		{
31			id: "select",
32			header: ({ table }: DataTablePrimitives.HeaderContext<Payment, unknown>) =>
33				renderComponent(DataTableCheckbox, {
34					checked: table.getIsAllPageRowsSelected(),
35					indeterminate: table.getIsSomePageRowsSelected() && !table.getIsAllPageRowsSelected(),
36					onCheckedChange: (value) => table.toggleAllPageRowsSelected(!!value),
37					"aria-label": "Select all",
38				}),
39			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) =>
40				renderComponent(DataTableCheckbox, {
41					checked: row.getIsSelected(),
42					onCheckedChange: (value) => row.toggleSelected(!!value),
43					"aria-label": "Select row",
44				}),
45			enableSorting: false,
46			enableHiding: false,
47		},
48		{
49			accessorKey: "status",
50			header: "Status",
51			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) => {
52				const statusSnippet = createRawSnippet<[{ status: string }]>((getStatus) => {
53					const { status } = getStatus();
54					return { render: () => `<div class="capitalize">${status}</div>` };
55				});
56				return renderSnippet(statusSnippet, { status: row.original.status });
57			},
58		},
59		{
60			accessorKey: "email",
61			header: ({ column }: DataTablePrimitives.HeaderContext<Payment, unknown>) =>
62				renderComponent(DataTableColumnHeader, {
63					title: "Email",
64					onclick: column.getToggleSortingHandler(),
65				}),
66			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) => {
67				const emailSnippet = createRawSnippet<[{ email: string }]>((getEmail) => {
68					const { email } = getEmail();
69					return { render: () => `<div class="lowercase">${email}</div>` };
70				});
71				return renderSnippet(emailSnippet, { email: row.original.email });
72			},
73		},
74		{
75			accessorKey: "amount",
76			header: () => {
77				const amountHeaderSnippet = createRawSnippet(() => {
78					return { render: () => `<div class="text-end">Amount</div>` };
79				});
80				return renderSnippet(amountHeaderSnippet);
81			},
82			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) => {
83				const formatter = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" });
84				const amountCellSnippet = createRawSnippet<[{ amount: number }]>((getAmount) => {
85					const { amount } = getAmount();
86					const formatted = formatter.format(amount);
87					return { render: () => `<div class="text-end font-medium">${formatted}</div>` };
88				});
89				return renderSnippet(amountCellSnippet, { amount: row.original.amount });
90			},
91		},
92		{
93			id: "actions",
94			enableHiding: false,
95			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) =>
96				renderComponent(DataTableActions, {
97					id: row.original.id,
98					copyLabel: "Copy payment ID",
99					actions: [
100						{ label: "View details", onclick: () => console.log("View details for", row.original.id) },
101						{ label: "Edit payment", onclick: () => console.log("Edit payment", row.original.id) },
102						{ label: "Delete", onclick: () => console.log("Delete payment", row.original.id) },
103					],
104				}),
105		},
106	];
107</script>
108
109<DataTable
110	{data}
111	{columns}
112	variant="striped"
113	filterColumn="email"
114	filterPlaceholder="Filter emails..."
115	pageSize={5}
116/>

Bordered Variant

Enhanced borders between columns and around the table


Code Svelte
1
2<script lang="ts">
3	import { DataTable } from "@kareyes/aether";
4	
5	const { DataTableCheckbox, DataTableColumnHeader, renderComponent } = DataTablePrimitives;
6
7	type Payment = {
8		id: string;
9		amount: number;
10		status: "Pending" | "Processing" | "Success" | "Failed";
11		email: string;
12	};
13
14	const data: Payment[] = [
15		{ id: "m5gr84i9", amount: 316, status: "Success", email: "ken99@yahoo.com" },
16		{ id: "3u1reuv4", amount: 242, status: "Success", email: "Abe45@gmail.com" },
17		{ id: "derv1ws0", amount: 837, status: "Processing", email: "Monserrat44@gmail.com" },
18		{ id: "5kma53ae", amount: 874, status: "Success", email: "Silas22@gmail.com" },
19		{ id: "bhqecj4p", amount: 721, status: "Failed", email: "carmella@hotmail.com" },
20		{ id: "a1b2c3d4", amount: 450, status: "Pending", email: "john.doe@example.com" },
21		{ id: "e5f6g7h8", amount: 920, status: "Success", email: "jane.smith@gmail.com" },
22		{ id: "i9j0k1l2", amount: 350, status: "Processing", email: "bob.wilson@yahoo.com" },
23		{ id: "m3n4o5p6", amount: 680, status: "Failed", email: "alice.brown@hotmail.com" },
24		{ id: "q7r8s9t0", amount: 530, status: "Success", email: "charlie.davis@gmail.com" },
25		{ id: "u1v2w3x4", amount: 290, status: "Pending", email: "diana.miller@example.com" },
26		{ id: "y5z6a7b8", amount: 775, status: "Processing", email: "edward.jones@yahoo.com" },
27	];
28
29	const columns: DataTablePrimitives.ColumnDef<Payment>[] = [
30		{
31			id: "select",
32			header: ({ table }: DataTablePrimitives.HeaderContext<Payment, unknown>) =>
33				renderComponent(DataTableCheckbox, {
34					checked: table.getIsAllPageRowsSelected(),
35					indeterminate: table.getIsSomePageRowsSelected() && !table.getIsAllPageRowsSelected(),
36					onCheckedChange: (value) => table.toggleAllPageRowsSelected(!!value),
37					"aria-label": "Select all",
38				}),
39			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) =>
40				renderComponent(DataTableCheckbox, {
41					checked: row.getIsSelected(),
42					onCheckedChange: (value) => row.toggleSelected(!!value),
43					"aria-label": "Select row",
44				}),
45			enableSorting: false,
46			enableHiding: false,
47		},
48		{
49			accessorKey: "status",
50			header: "Status",
51			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) => {
52				const statusSnippet = createRawSnippet<[{ status: string }]>((getStatus) => {
53					const { status } = getStatus();
54					return { render: () => `<div class="capitalize">${status}</div>` };
55				});
56				return renderSnippet(statusSnippet, { status: row.original.status });
57			},
58		},
59		{
60			accessorKey: "email",
61			header: ({ column }: DataTablePrimitives.HeaderContext<Payment, unknown>) =>
62				renderComponent(DataTableColumnHeader, {
63					title: "Email",
64					onclick: column.getToggleSortingHandler(),
65				}),
66			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) => {
67				const emailSnippet = createRawSnippet<[{ email: string }]>((getEmail) => {
68					const { email } = getEmail();
69					return { render: () => `<div class="lowercase">${email}</div>` };
70				});
71				return renderSnippet(emailSnippet, { email: row.original.email });
72			},
73		},
74		{
75			accessorKey: "amount",
76			header: () => {
77				const amountHeaderSnippet = createRawSnippet(() => {
78					return { render: () => `<div class="text-end">Amount</div>` };
79				});
80				return renderSnippet(amountHeaderSnippet);
81			},
82			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) => {
83				const formatter = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" });
84				const amountCellSnippet = createRawSnippet<[{ amount: number }]>((getAmount) => {
85					const { amount } = getAmount();
86					const formatted = formatter.format(amount);
87					return { render: () => `<div class="text-end font-medium">${formatted}</div>` };
88				});
89				return renderSnippet(amountCellSnippet, { amount: row.original.amount });
90			},
91		},
92		{
93			id: "actions",
94			enableHiding: false,
95			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) =>
96				renderComponent(DataTableActions, {
97					id: row.original.id,
98					copyLabel: "Copy payment ID",
99					actions: [
100						{ label: "View details", onclick: () => console.log("View details for", row.original.id) },
101						{ label: "Edit payment", onclick: () => console.log("Edit payment", row.original.id) },
102						{ label: "Delete", onclick: () => console.log("Delete payment", row.original.id) },
103					],
104				}),
105		},
106	];
107</script>
108
109<DataTable
110	{data}
111	{columns}
112	variant="bordered"
113	filterColumn="email"
114	filterPlaceholder="Filter emails..."
115	pageSize={5}
116/>

Compact Variant

Reduced padding and smaller text for dense data display


Code Svelte
1
2<script lang="ts">
3	import { DataTable } from "@kareyes/aether";
4</script>
5
6<DataTable
7	{data}
8	{columns}
9	variant="compact"
10	filterColumn="email"
11	filterPlaceholder="Filter emails..."
12	pageSize={8}
13/>

Minimal Configuration

No filter, no column toggle, larger page size


Code Svelte
1
2<script lang="ts">
3	import { DataTable } from "@kareyes/aether";
4	
5	const { DataTableCheckbox, DataTableColumnHeader, renderComponent } = DataTablePrimitives;
6
7	type Payment = {
8		id: string;
9		amount: number;
10		status: "Pending" | "Processing" | "Success" | "Failed";
11		email: string;
12	};
13
14	const data: Payment[] = [
15		{ id: "m5gr84i9", amount: 316, status: "Success", email: "ken99@yahoo.com" },
16		{ id: "3u1reuv4", amount: 242, status: "Success", email: "Abe45@gmail.com" },
17		{ id: "derv1ws0", amount: 837, status: "Processing", email: "Monserrat44@gmail.com" },
18		{ id: "5kma53ae", amount: 874, status: "Success", email: "Silas22@gmail.com" },
19		{ id: "bhqecj4p", amount: 721, status: "Failed", email: "carmella@hotmail.com" },
20		{ id: "a1b2c3d4", amount: 450, status: "Pending", email: "john.doe@example.com" },
21		{ id: "e5f6g7h8", amount: 920, status: "Success", email: "jane.smith@gmail.com" },
22		{ id: "i9j0k1l2", amount: 350, status: "Processing", email: "bob.wilson@yahoo.com" },
23		{ id: "m3n4o5p6", amount: 680, status: "Failed", email: "alice.brown@hotmail.com" },
24		{ id: "q7r8s9t0", amount: 530, status: "Success", email: "charlie.davis@gmail.com" },
25		{ id: "u1v2w3x4", amount: 290, status: "Pending", email: "diana.miller@example.com" },
26		{ id: "y5z6a7b8", amount: 775, status: "Processing", email: "edward.jones@yahoo.com" },
27	];
28
29	const columns: DataTablePrimitives.ColumnDef<Payment>[] = [
30		{
31			id: "select",
32			header: ({ table }: DataTablePrimitives.HeaderContext<Payment, unknown>) =>
33				renderComponent(DataTableCheckbox, {
34					checked: table.getIsAllPageRowsSelected(),
35					indeterminate: table.getIsSomePageRowsSelected() && !table.getIsAllPageRowsSelected(),
36					onCheckedChange: (value) => table.toggleAllPageRowsSelected(!!value),
37					"aria-label": "Select all",
38				}),
39			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) =>
40				renderComponent(DataTableCheckbox, {
41					checked: row.getIsSelected(),
42					onCheckedChange: (value) => row.toggleSelected(!!value),
43					"aria-label": "Select row",
44				}),
45			enableSorting: false,
46			enableHiding: false,
47		},
48		{
49			accessorKey: "status",
50			header: "Status",
51			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) => {
52				const statusSnippet = createRawSnippet<[{ status: string }]>((getStatus) => {
53					const { status } = getStatus();
54					return { render: () => `<div class="capitalize">${status}</div>` };
55				});
56				return renderSnippet(statusSnippet, { status: row.original.status });
57			},
58		},
59		{
60			accessorKey: "email",
61			header: ({ column }: DataTablePrimitives.HeaderContext<Payment, unknown>) =>
62				renderComponent(DataTableColumnHeader, {
63					title: "Email",
64					onclick: column.getToggleSortingHandler(),
65				}),
66			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) => {
67				const emailSnippet = createRawSnippet<[{ email: string }]>((getEmail) => {
68					const { email } = getEmail();
69					return { render: () => `<div class="lowercase">${email}</div>` };
70				});
71				return renderSnippet(emailSnippet, { email: row.original.email });
72			},
73		},
74		{
75			accessorKey: "amount",
76			header: () => {
77				const amountHeaderSnippet = createRawSnippet(() => {
78					return { render: () => `<div class="text-end">Amount</div>` };
79				});
80				return renderSnippet(amountHeaderSnippet);
81			},
82			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) => {
83				const formatter = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" });
84				const amountCellSnippet = createRawSnippet<[{ amount: number }]>((getAmount) => {
85					const { amount } = getAmount();
86					const formatted = formatter.format(amount);
87					return { render: () => `<div class="text-end font-medium">${formatted}</div>` };
88				});
89				return renderSnippet(amountCellSnippet, { amount: row.original.amount });
90			},
91		},
92		{
93			id: "actions",
94			enableHiding: false,
95			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) =>
96				renderComponent(DataTableActions, {
97					id: row.original.id,
98					copyLabel: "Copy payment ID",
99					actions: [
100						{ label: "View details", onclick: () => console.log("View details for", row.original.id) },
101						{ label: "Edit payment", onclick: () => console.log("Edit payment", row.original.id) },
102						{ label: "Delete", onclick: () => console.log("Delete payment", row.original.id) },
103					],
104				}),
105		},
106	];
107</script>
108
109<DataTable
110	{data}
111	{columns}
112	showFilter={false}
113	showColumnToggle={false}
114	pageSize={10}
115/>

No Pagination

Shows all rows without pagination controls


Code Svelte
1
2<script lang="ts">
3	import { DataTable } from "@kareyes/aether";
4</script>
5
6<DataTable
7	{data}
8	{columns}
9	showPagination={false}
10	pageSize={100}
11/>

Expandable Rows

Click the chevron to expand rows and view additional details


Code Svelte
1
2<script lang="ts">
3	import { DataTable } from "@kareyes/aether";
4	
5	const { DataTableCheckbox, DataTableColumnHeader, renderComponent } = DataTablePrimitives;
6
7	type Payment = {
8		id: string;
9		amount: number;
10		status: "Pending" | "Processing" | "Success" | "Failed";
11		email: string;
12	};
13
14	const data: Payment[] = [
15		{ id: "m5gr84i9", amount: 316, status: "Success", email: "ken99@yahoo.com" },
16		{ id: "3u1reuv4", amount: 242, status: "Success", email: "Abe45@gmail.com" },
17		{ id: "derv1ws0", amount: 837, status: "Processing", email: "Monserrat44@gmail.com" },
18		{ id: "5kma53ae", amount: 874, status: "Success", email: "Silas22@gmail.com" },
19		{ id: "bhqecj4p", amount: 721, status: "Failed", email: "carmella@hotmail.com" },
20		{ id: "a1b2c3d4", amount: 450, status: "Pending", email: "john.doe@example.com" },
21		{ id: "e5f6g7h8", amount: 920, status: "Success", email: "jane.smith@gmail.com" },
22		{ id: "i9j0k1l2", amount: 350, status: "Processing", email: "bob.wilson@yahoo.com" },
23		{ id: "m3n4o5p6", amount: 680, status: "Failed", email: "alice.brown@hotmail.com" },
24		{ id: "q7r8s9t0", amount: 530, status: "Success", email: "charlie.davis@gmail.com" },
25		{ id: "u1v2w3x4", amount: 290, status: "Pending", email: "diana.miller@example.com" },
26		{ id: "y5z6a7b8", amount: 775, status: "Processing", email: "edward.jones@yahoo.com" },
27	];
28
29	const columns: DataTablePrimitives.ColumnDef<Payment>[] = [
30		{
31			id: "select",
32			header: ({ table }: DataTablePrimitives.HeaderContext<Payment, unknown>) =>
33				renderComponent(DataTableCheckbox, {
34					checked: table.getIsAllPageRowsSelected(),
35					indeterminate: table.getIsSomePageRowsSelected() && !table.getIsAllPageRowsSelected(),
36					onCheckedChange: (value) => table.toggleAllPageRowsSelected(!!value),
37					"aria-label": "Select all",
38				}),
39			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) =>
40				renderComponent(DataTableCheckbox, {
41					checked: row.getIsSelected(),
42					onCheckedChange: (value) => row.toggleSelected(!!value),
43					"aria-label": "Select row",
44				}),
45			enableSorting: false,
46			enableHiding: false,
47		},
48		{
49			accessorKey: "status",
50			header: "Status",
51			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) => {
52				const statusSnippet = createRawSnippet<[{ status: string }]>((getStatus) => {
53					const { status } = getStatus();
54					return { render: () => `<div class="capitalize">${status}</div>` };
55				});
56				return renderSnippet(statusSnippet, { status: row.original.status });
57			},
58		},
59		{
60			accessorKey: "email",
61			header: ({ column }: DataTablePrimitives.HeaderContext<Payment, unknown>) =>
62				renderComponent(DataTableColumnHeader, {
63					title: "Email",
64					onclick: column.getToggleSortingHandler(),
65				}),
66			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) => {
67				const emailSnippet = createRawSnippet<[{ email: string }]>((getEmail) => {
68					const { email } = getEmail();
69					return { render: () => `<div class="lowercase">${email}</div>` };
70				});
71				return renderSnippet(emailSnippet, { email: row.original.email });
72			},
73		},
74		{
75			accessorKey: "amount",
76			header: () => {
77				const amountHeaderSnippet = createRawSnippet(() => {
78					return { render: () => `<div class="text-end">Amount</div>` };
79				});
80				return renderSnippet(amountHeaderSnippet);
81			},
82			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) => {
83				const formatter = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" });
84				const amountCellSnippet = createRawSnippet<[{ amount: number }]>((getAmount) => {
85					const { amount } = getAmount();
86					const formatted = formatter.format(amount);
87					return { render: () => `<div class="text-end font-medium">${formatted}</div>` };
88				});
89				return renderSnippet(amountCellSnippet, { amount: row.original.amount });
90			},
91		},
92		{
93			id: "actions",
94			enableHiding: false,
95			cell: ({ row }: DataTablePrimitives.CellContext<Payment, unknown>) =>
96				renderComponent(DataTableActions, {
97					id: row.original.id,
98					copyLabel: "Copy payment ID",
99					actions: [
100						{ label: "View details", onclick: () => console.log("View details for", row.original.id) },
101						{ label: "Edit payment", onclick: () => console.log("Edit payment", row.original.id) },
102						{ label: "Delete", onclick: () => console.log("Delete payment", row.original.id) },
103					],
104				}),
105		},
106	];
107</script>
108
109<DataTable {data} {columns} expandable={true} pageSize={5}>
110	{#snippet renderSubComponent({ row })}
111		{@const payment = row.original}
112		<div class="space-y-2 text-sm">
113			<div class="grid grid-cols-2 gap-4">
114				<div>
115					<span class="font-semibold">Payment ID:</span>
116					{payment.id}
117				</div>
118				<div>
119					<span class="font-semibold">Status:</span>
120					<span class="capitalize">{payment.status}</span>
121				</div>
122				<div>
123					<span class="font-semibold">Amount:</span>
124					${payment.amount.toFixed(2)}
125				</div>
126				<div>
127					<span class="font-semibold">Email:</span>
128					{payment.email}
129				</div>
130			</div>
131		</div>
132	{/snippet}
133</DataTable>

Responsive Design

The DataTable automatically adapts to different screen sizes. Use the buttons to force different display modes.


Code Svelte
1
2<script lang="ts">
3	import { DataTable } from "@kareyes/aether";
4	
5	const { DataTableCheckbox, DataTableColumnHeader, renderComponent } = DataTablePrimitives;
6let selectedMode: DataTablePrimitives.ResponsiveMode = $state("scroll");
7	type Payment = {
8		id: string;
9		amount: number;
10		status: "Pending" | "Processing" | "Success" | "Failed";
11		email: string;
12	};
13		const paymentData2: Payment2[] = [
14		{
15			id: "PAY-001",
16			amount: 316,
17			status: "Success",
18			email: "ken99@yahoo.com",
19			date: "2025-01-15",
20			method: "Credit Card",
21		},
22		{
23			id: "PAY-002",
24			amount: 242,
25			status: "Success",
26			email: "abe45@gmail.com",
27			date: "2025-01-14",
28			method: "PayPal",
29		},
30		{
31			id: "PAY-003",
32			amount: 837,
33			status: "Processing",
34			email: "monserrat44@gmail.com",
35			date: "2025-01-14",
36			method: "Bank Transfer",
37		}
38	];
39		const responsiveColumns: DataTablePrimitives.ColumnDef<Payment2>[] = [
40		{
41			id: "select",
42			header: ({ table }: DataTablePrimitives.HeaderContext<Payment2, unknown>) =>
43				renderComponent(DataTableCheckbox, {
44					checked: table.getIsAllPageRowsSelected(),
45					indeterminate:
46						table.getIsSomePageRowsSelected() &&
47						!table.getIsAllPageRowsSelected(),
48					onCheckedChange: (value) =>
49						table.toggleAllPageRowsSelected(!!value),
50					"aria-label": "Select all",
51				}),
52			cell: ({ row }:DataTablePrimitives.CellContext<Payment2, unknown>) =>
53				renderComponent(DataTableCheckbox, {
54					checked: row.getIsSelected(),
55					onCheckedChange: (value) => row.toggleSelected(!!value),
56					"aria-label": "Select row",
57				}),
58			enableSorting: false,
59			enableHiding: false,
60		},
61		{
62			accessorKey: "status",
63			header: "Status",
64			meta: {
65				mobileLabel: "Payment Status",
66				priority: 1,
67				alwaysVisible: true,
68			} as DataTablePrimitives.DataTableColumnMeta,
69			cell: ({ row }) => {
70				const status = row.original.status;
71				const snippet = createRawSnippet<[{ status: string }]>(
72					(getStatus) => {
73						const { status } = getStatus();
74						const colorClass =
75							status === "Success"
76								? "text-green-600"
77								: status === "Failed"
78									? "text-red-600"
79									: status === "Processing"
80										? "text-blue-600"
81										: "text-yellow-600";
82						return {
83							render: () =>
84								`<span class="capitalize font-medium ${colorClass}">${status}</span>`,
85						};
86					},
87				);
88				return renderSnippet(snippet, { status });
89			},
90		},
91		{
92			accessorKey: "email",
93			header: ({ column }: DataTablePrimitives.HeaderContext<Payment2, unknown>) =>
94				renderComponent(DataTableColumnHeader, {
95					title: "Email",
96					onclick: column.getToggleSortingHandler(),
97				}),
98			meta: {
99				mobileLabel: "Customer Email",
100				priority: 2,
101			} as DataTablePrimitives.DataTableColumnMeta,
102			cell: ({ row }) => {
103				const snippet = createRawSnippet<[{ email: string }]>(
104					(getEmail) => {
105						const { email } = getEmail();
106						return {
107							render: () =>
108								`<span class="lowercase">${email}</span>`,
109						};
110					},
111				);
112				return renderSnippet(snippet, { email: row.original.email });
113			},
114		},
115		{
116			accessorKey: "amount",
117			header: "Amount",
118			meta: {
119				mobileLabel: "Amount (USD)",
120				priority: 3,
121			} as DataTablePrimitives.DataTableColumnMeta,
122			cell: ({ row }) => {
123				const formatter = new Intl.NumberFormat("en-US", {
124					style: "currency",
125					currency: "USD",
126				});
127				const snippet = createRawSnippet<[{ amount: string }]>(
128					(getAmount) => {
129						const { amount } = getAmount();
130						return {
131							render: () =>
132								`<span class="font-semibold">${amount}</span>`,
133						};
134					},
135				);
136				return renderSnippet(snippet, {
137					amount: formatter.format(row.original.amount),
138				});
139			},
140		},
141		{
142			accessorKey: "date",
143			header: "Date",
144			meta: {
145				mobileLabel: "Transaction Date",
146				priority: 4,
147			} as DataTablePrimitives.DataTableColumnMeta,
148		},
149		{
150			accessorKey: "method",
151			header: "Method",
152			meta: {
153				mobileLabel: "Payment Method",
154				priority: 5,
155				hiddenOnMobile: false,
156			} as DataTablePrimitives.DataTableColumnMeta,
157		},
158		{
159			id: "actions",
160			enableHiding: false,
161			cell: ({ row }) =>
162				renderComponent(DataTableActions, {
163					id: row.original.id,
164					copyLabel: "Copy payment ID",
165					actions: [
166						{
167							label: "View details",
168							onclick: () => console.log("View", row.original),
169						},
170						{
171							label: "Refund",
172							onclick: () => console.log("Refund", row.original),
173						},
174						{
175							label: "Delete",
176							onclick: () => console.log("Delete", row.original),
177						},
178					],
179				}),
180		},
181	];
182	</script>
183
184
185	<DataTable
186		data={paymentData2}
187		columns={responsiveColumns}
188		responsiveMode={selectedMode}
189		filterColumn="email"
190		filterPlaceholder="Filter by email..."
191		pageSize={5}
192		variant="default"
193		selectionMode="multi"
194	/>

Column Mobile Metadata

Customize how columns appear in mobile card view using the meta field.