shadcn-svelte Custom Component in Datatable
January 16, 2026With the original shadcn for react, using an existing component to be displayed in the datatable cell is as easy as the following:
cell: ({ row }) => ( <Checkbox checked={row.getIsSelected()} onCheckedChange={(value) => row.toggleSelected(!!value)} aria-label="Select row" />),But with svelte, it’s different.
The following code adds the <Checkbox /> component from shadcn-svelte to the table cell.
<script lang="ts"> import Checkbox from '$lib/components/ui/checkbox/checkbox.svelte'; import type { ColumnDef } from '@tanstack/table-core'; import { createRawSnippet, mount } from 'svelte';
const columns: ColumnDef<DataType>[] = [ { accessorKey: 'key', cell: ({ row }) => { const snippet = createRawSnippet(() => ({ render: () => `<div></div>`, setup: (target) => { mount(Checkbox, { target, props: { checked: isChecked, onCheckedChange: (value) => {} } }); } }));
return renderSnippet(snippet); } } ];
const data = [];</script>
<DataTable {columns} {data} />For reference this is the code of my <Datatable />.
129 collapsed lines
<script lang="ts" generics="TData, TValue"> import { type ColumnDef, type PaginationState, type SortingState, type ColumnFiltersState, getCoreRowModel, getPaginationRowModel, getSortedRowModel, getFilteredRowModel } from '@tanstack/table-core'; import { Input } from '$lib/components/ui/input/index.js'; import { createSvelteTable } from '$lib/components/ui/data-table'; import * as Table from '$lib/components/ui/table'; import FlexRender from '$lib/components/ui/data-table/flex-render.svelte'; import type { Snippet } from 'svelte'; import { cn } from '$lib/utils';
type DataTableProps<TData, TValue> = { columns: ColumnDef<TData, TValue>[]; data: TData[]; };
let { columns, data, filterInput, containerClass }: DataTableProps<TData, TValue> & { filterInput?: Snippet; containerClass?: string } = $props();
let pagination = $state<PaginationState>({ pageIndex: 0, pageSize: 100 }); let sorting = $state<SortingState>([]); let columnFilters = $state<ColumnFiltersState>([]);
const table = createSvelteTable({ get data() { return data; }, columns, getCoreRowModel: getCoreRowModel(), getPaginationRowModel: getPaginationRowModel(), getSortedRowModel: getSortedRowModel(), getFilteredRowModel: getFilteredRowModel(), onPaginationChange: (updater) => { if (typeof updater === 'function') { pagination = updater(pagination); } else { pagination = updater; } }, onSortingChange: (updater) => { if (typeof updater === 'function') { sorting = updater(sorting); } else { sorting = updater; } }, onColumnFiltersChange: (updater) => { if (typeof updater === 'function') { columnFilters = updater(columnFilters); } else { columnFilters = updater; } }, state: { get pagination() { return pagination; }, get sorting() { return sorting; }, get columnFilters() { return columnFilters; } } });</script>
<div class={cn(containerClass)}> <div class="flex items-center py-4"> <!-- TODO: Make reusable --> <Input placeholder="Filter IGNs..." value={(table.getColumn('ign')?.getFilterValue() as string) ?? ''} onchange={(e) => { table.getColumn('ign')?.setFilterValue(e.currentTarget.value); }} oninput={(e) => { table.getColumn('ign')?.setFilterValue(e.currentTarget.value); }} class="max-w-sm" /> </div> <div class="rounded-md border w-full overflow-x-auto"> <Table.Root> <Table.Header> {#each table.getHeaderGroups() as headerGroup (headerGroup.id)} <Table.Row> {#each headerGroup.headers as header (header.id)} <Table.Head colspan={header.colSpan}> {#if !header.isPlaceholder} <FlexRender content={header.column.columnDef.header} context={header.getContext()} /> {/if} </Table.Head> {/each} </Table.Row> {/each} </Table.Header> <Table.Body> {#each table.getRowModel().rows as row (row.id)} <Table.Row data-state={row.getIsSelected() && 'selected'}> {#each row.getVisibleCells() as cell (cell.id)} <Table.Cell> <FlexRender content={cell.column.columnDef.cell} context={cell.getContext()} /> </Table.Cell> {/each} </Table.Row> {:else} <Table.Row> <Table.Cell colspan={columns.length} class="h-24 text-center">No results.</Table.Cell> </Table.Row> {/each} </Table.Body> </Table.Root> </div></div>For more information regarding shadcn-svelte’s <Datatable /> ↗️.