> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/huntabyte/shadcn-svelte/llms.txt
> Use this file to discover all available pages before exploring further.

# Data Table

> Powerful table and datagrids built using TanStack Table.

## Introduction

Data tables are difficult to componentize because of the wide variety of features they support, and the uniqueness of every data set.

So instead of trying to create a one-size-fits-all solution, we've created a guide to help you build your own data tables.

We'll start with the basic `<Table />` component, and work our way up to a fully-featured data table.

<Note>
  Tip: If you find yourself using the same table in multiple places, you can always extract it into a reusable component.
</Note>

## Table of Contents

This guide will show you how to use [TanStack Table](https://tanstack.com/table) and the `<Table />` component to build your own custom data table. We'll cover the following topics:

* [Basic Table](#basic-table)
* [Row Actions](#row-actions)
* [Pagination](#pagination)
* [Sorting](#sorting)
* [Filtering](#filtering)
* [Visibility](#visibility)
* [Row Selection](#row-selection)
* [Reusable Components](#reusable-components)

## Installation

<Steps>
  <Step title="Add the Table component">
    Add the `<Table />` component to your project along with the `data-table` helpers. These helpers enable TanStack Table v8 to work with Svelte 5 Snippets, Components, etc.

    ```bash theme={null}
    npx shadcn-svelte@next add table data-table
    ```
  </Step>

  <Step title="Install TanStack Table">
    Add `@tanstack/table-core` as a dependency:

    ```bash theme={null}
    npm install @tanstack/table-core
    ```
  </Step>
</Steps>

## Prerequisites

We're going to build a table to show recent payments. Here's what our data looks like:

```ts theme={null}
type Payment = {
  id: string;
  amount: number;
  status: "pending" | "processing" | "success" | "failed";
  email: string;
};

export const data: Payment[] = [
  {
    id: "728ed52f",
    amount: 100,
    status: "pending",
    email: "m@example.com",
  },
  {
    id: "489e1d42",
    amount: 125,
    status: "processing",
    email: "example@gmail.com",
  },
  // ...
];
```

## Project Structure

Start by creating a route where your data table will live (we'll call ours payments), along with the following files:

```txt theme={null}
routes
└── payments
	├── columns.ts
    ├── data-table.svelte
    ├── data-table-actions.svelte
    ├── data-table-checkbox.svelte
	├── data-table-email-button.svelte
    └── +page.svelte
```

* `columns.ts` will contain our column definitions.
* `data-table.svelte` will contain the `<Table />` component and the complete `<DataTable />` component.
* `data-table-actions.svelte` will contain the actions menu for each row.
* `data-table-checkbox.svelte` will contain the checkbox for each row.
* `data-table-email-button.svelte` will contain the sortable email header button.
* `+page.svelte` is where we'll render and access `<DataTable />` component.

## Basic Table

Let's start by building a basic table.

<Steps>
  <Step title="Column Definitions">
    First, we'll define our columns.

    ```ts title="routes/payments/columns.ts" theme={null}
    import type { ColumnDef } from "@tanstack/table-core";

    // This type is used to define the shape of our data.
    // You can use a Zod schema here if you want.
    export type Payment = {
      id: string;
      amount: number;
      status: "pending" | "processing" | "success" | "failed";
      email: string;
    };

    export const columns: ColumnDef<Payment>[] = [
      {
        accessorKey: "status",
        header: "Status",
      },
      {
        accessorKey: "email",
        header: "Email",
      },
      {
        accessorKey: "amount",
        header: "Amount",
      },
    ];
    ```

    <Note>
      **Note:** Columns are where you define the core of what your table will look like. They define the data that will be displayed, how it will be formatted, sorted and filtered.
    </Note>
  </Step>

  <Step title="DataTable Component">
    Next, we'll create a `<DataTable />` component to render our table.

    ```svelte title="routes/payments/data-table.svelte" theme={null}
    <script lang="ts" generics="TData, TValue">
      import { type ColumnDef, getCoreRowModel } from "@tanstack/table-core";
      import {
        createSvelteTable,
        FlexRender,
      } from "$lib/components/ui/data-table/index.js";
      import * as Table from "$lib/components/ui/table/index.js";

      type DataTableProps<TData, TValue> = {
        columns: ColumnDef<TData, TValue>[];
        data: TData[];
      };

      let { data, columns }: DataTableProps<TData, TValue> = $props();

      const table = createSvelteTable({
        get data() {
          return data;
        },
        columns,
        getCoreRowModel: getCoreRowModel(),
      });
    </script>

    <div class="rounded-md border">
      <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>
    ```

    <Note>
      **Tip**: If you find yourself using `<DataTable />` in multiple places, this is the component you could make reusable by extracting it to `components/ui/data-table.svelte`.

      `<DataTable columns={columns} data={data} />`
    </Note>
  </Step>

  <Step title="Render the table">
    Finally, we'll render our table in our page component.

    ```ts title="routes/payments/+page.server.ts" theme={null}
    export async function load() {
      // logic to fetch payments data here
      const payments = await getPayments();
      return {
        payments,
      };
    }
    ```

    ```svelte title="routes/payments/+page.svelte" theme={null}
    <script lang="ts">
      import DataTable from "./data-table.svelte";
      import { columns } from "./columns.js";

      let { data } = $props();
    </script>

    <DataTable data={data.payments} {columns} />
    ```
  </Step>
</Steps>

## Cell Formatting

Let's format the amount cell to display the dollar amount. We'll also align the cell to the right.

<Steps>
  <Step title="Update columns definition">
    Update the `header` and `cell` definitions for amount as follows:

    ```ts title="routes/payments/columns.ts" theme={null}
    import type { ColumnDef } from "@tanstack/table-core";
    import { createRawSnippet } from "svelte";
    import { renderSnippet } from "$lib/components/ui/data-table/index.js";

    export const columns: ColumnDef<Payment>[] = [
      {
        accessorKey: "amount",
        header: () => {
          const amountHeaderSnippet = createRawSnippet(() => ({
            render: () => `<div class="text-end">Amount</div>`,
          }));
          return renderSnippet(amountHeaderSnippet);
        },
        cell: ({ row }) => {
          const formatter = new Intl.NumberFormat("en-US", {
            style: "currency",
            currency: "USD",
          });

          const amountCellSnippet = createRawSnippet<[{ amount: number }]>(
            (getAmount) => {
              const { amount } = getAmount();
              const formatted = formatter.format(amount);
              return {
                render: () =>
                  `<div class="text-end font-medium">${formatted}</div>`,
              };
            }
          );

          return renderSnippet(amountCellSnippet, {
            amount: row.original.amount,
          });
        },
      },
    ];
    ```

    We're using the `createRawSnippet` function to create a Svelte Snippet for rendering simple HTML elements that don't require full lifecycle and state capabilities like a component. We then use the `renderSnippet` helper function to render the snippet.

    You can use the same approach to format other cells and headers.
  </Step>
</Steps>

## Row Actions

Let's add row actions to our table. We'll use the `<DropdownMenu />` and the `<Button />` components for this, so you have install them if not done already:

```bash theme={null}
npx shadcn-svelte@next add button dropdown-menu
```

<Steps>
  <Step title="Create actions component">
    We'll start by defining the actions menu in our `data-table-actions.svelte` component.

    ```svelte title="routes/payments/data-table-actions.svelte" theme={null}
    <script lang="ts">
      import EllipsisIcon from "@lucide/svelte/icons/ellipsis";
      import { Button } from "$lib/components/ui/button/index.js";
      import * as DropdownMenu from "$lib/components/ui/dropdown-menu/index.js";

      let { id }: { id: string } = $props();
    </script>

    <DropdownMenu.Root>
      <DropdownMenu.Trigger>
        {#snippet child({ props })}
          <Button
            {...props}
            variant="ghost"
            size="icon"
            class="relative size-8 p-0"
          >
            <span class="sr-only">Open menu</span>
            <EllipsisIcon />
          </Button>
        {/snippet}
      </DropdownMenu.Trigger>
      <DropdownMenu.Content>
        <DropdownMenu.Group>
          <DropdownMenu.Label>Actions</DropdownMenu.Label>
          <DropdownMenu.Item onclick={() => navigator.clipboard.writeText(id)}>
            Copy payment ID
          </DropdownMenu.Item>
        </DropdownMenu.Group>
        <DropdownMenu.Separator />
        <DropdownMenu.Item>View customer</DropdownMenu.Item>
        <DropdownMenu.Item>View payment details</DropdownMenu.Item>
      </DropdownMenu.Content>
    </DropdownMenu.Root>
    ```
  </Step>

  <Step title="Update columns definition">
    Now that we've defined the `<DataTableActions />` component, let's update our `actions` column definition to use it.

    ```ts title="routes/payments/columns.ts" theme={null}
    import type { ColumnDef } from "@tanstack/table-core";
    import { renderComponent } from "$lib/components/ui/data-table/index.js";
    import DataTableActions from "./data-table-actions.svelte";

    export const columns: ColumnDef<Payment>[] = [
      // ...
      {
        id: "actions",
        cell: ({ row }) => {
          // You can pass whatever you need from `row.original` to the component
          return renderComponent(DataTableActions, { id: row.original.id });
        },
      },
    ];
    ```

    You can access the row data using `row.original` in the `cell` function. Use this to handle actions for your row eg. use the `id` to make a DELETE call to your API.
  </Step>
</Steps>

## Pagination

Next, we'll add pagination to our table.

<Steps>
  <Step title="Update DataTable">
    ```svelte title="routes/payments/data-table.svelte" theme={null}
    <script lang="ts" generics="TData, TValue">
      import {
        type ColumnDef,
        type PaginationState,
        getCoreRowModel,
        getPaginationRowModel,
      } from "@tanstack/table-core";

      type DataTableProps<TData, TValue> = {
        data: TData[];
        columns: ColumnDef<TData, TValue>[];
      };

      let { data, columns }: DataTableProps<TData, TValue> = $props();

      let pagination = $state<PaginationState>({ pageIndex: 0, pageSize: 10 });

      const table = createSvelteTable({
        get data() {
          return data;
        },
        columns,
        state: {
          get pagination() {
            return pagination;
          },
        },
        onPaginationChange: (updater) => {
          if (typeof updater === "function") {
            pagination = updater(pagination);
          } else {
            pagination = updater;
          }
        },
        getCoreRowModel: getCoreRowModel(),
        getPaginationRowModel: getPaginationRowModel(),
      });
    </script>
    ```

    This will automatically paginate your rows into pages of 10. See the [pagination docs](https://tanstack.com/table/v8/docs/api/features/pagination) for more information on customizing page size and implementing manual pagination.
  </Step>

  <Step title="Adding pagination controls">
    We can add pagination controls to our table using the `<Button />` component and the `table.previousPage()`, `table.nextPage()` API methods.

    ```svelte title="routes/payments/data-table.svelte" theme={null}
    <script lang="ts" generics="TData, TValue">
      import { Button } from "$lib/components/ui/button/index.js";

      let { columns, data }: DataTableProps<TData, TValue> = $props();

      let pagination = $state<PaginationState>({ pageIndex: 0, pageSize: 10 });

      const table = createSvelteTable({
        get data() {
          return data;
        },
        columns,
        state: {
          get pagination() {
            return pagination;
          },
        },
        onPaginationChange: (updater) => {
          if (typeof updater === "function") {
            pagination = updater(pagination);
          } else {
            pagination = updater;
          }
        },
        getCoreRowModel: getCoreRowModel(),
        getPaginationRowModel: getPaginationRowModel(),
      });
    </script>

    <div>
      <div class="rounded-md border">
        <Table.Root>
          <!--- ... table implementation -->
        </Table.Root>
      </div>
      <div class="flex items-center justify-end space-x-2 py-4">
        <Button
          variant="outline"
          size="sm"
          onclick={() => table.previousPage()}
          disabled={!table.getCanPreviousPage()}
        >
          Previous
        </Button>
        <Button
          variant="outline"
          size="sm"
          onclick={() => table.nextPage()}
          disabled={!table.getCanNextPage()}
        >
          Next
        </Button>
      </div>
    </div>
    ```

    See [Reusable Components](#reusable-components) section for a more advanced pagination component.
  </Step>
</Steps>

## Sorting

Let's make the email column sortable.

<Steps>
  <Step title="Define DataTableEmailButton component">
    We'll start by creating a component to render a sortable email header button.

    ```svelte title="routes/payments/data-table-email-button.svelte" theme={null}
    <script lang="ts">
      import type { ComponentProps } from "svelte";
      import ArrowUpDownIcon from "@lucide/svelte/icons/arrow-up-down";
      import { Button } from "$lib/components/ui/button/index.js";

      let { variant = "ghost", ...restProps }: ComponentProps<typeof Button> =
        $props();
    </script>

    <Button {variant} {...restProps}>
      Email
      <ArrowUpDownIcon class="ms-2" />
    </Button>
    ```
  </Step>

  <Step title="Update DataTable">
    ```svelte title="routes/payments/data-table.svelte" theme={null}
    <script lang="ts" generics="TData, TValue">
      import {
        type ColumnDef,
        type PaginationState,
        type SortingState,
        getCoreRowModel,
        getPaginationRowModel,
        getSortedRowModel,
      } from "@tanstack/table-core";

      let { columns, data }: DataTableProps<TData, TValue> = $props();

      let pagination = $state<PaginationState>({ pageIndex: 0, pageSize: 10 });
      let sorting = $state<SortingState>([]);

      const table = createSvelteTable({
        get data() {
          return data;
        },
        columns,
        getCoreRowModel: getCoreRowModel(),
        getPaginationRowModel: getPaginationRowModel(),
        getSortedRowModel: getSortedRowModel(),
        onSortingChange: (updater) => {
          if (typeof updater === "function") {
            sorting = updater(sorting);
          } else {
            sorting = updater;
          }
        },
        onPaginationChange: (updater) => {
          if (typeof updater === "function") {
            pagination = updater(pagination);
          } else {
            pagination = updater;
          }
        },
        state: {
          get pagination() {
            return pagination;
          },
          get sorting() {
            return sorting;
          },
        },
      });
    </script>
    ```
  </Step>

  <Step title="Make header cell sortable">
    We can now update the `email` header cell to add sorting controls.

    ```ts title="src/routes/payments/columns.ts" theme={null}
    import type { ColumnDef } from "@tanstack/table-core";
    import { renderComponent } from "$lib/components/ui/data-table/index.js";
    import DataTableEmailButton from "./data-table-email-button.svelte";

    export const columns: ColumnDef<Payment>[] = [
      // ...
      {
        accessorKey: "email",
        header: ({ column }) =>
          renderComponent(DataTableEmailButton, {
            onclick: column.getToggleSortingHandler(),
          }),
      },
    ];
    ```

    This will automatically sort the table (asc and desc) when the user toggles on the header cell.
  </Step>
</Steps>

## Filtering

Let's add a search input to filter emails in our table.

<Steps>
  <Step title="Update DataTable">
    ```svelte title="routes/payments/data-table.svelte" theme={null}
    <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";

      let { columns, data }: DataTableProps<TData, TValue> = $props();

      let pagination = $state<PaginationState>({ pageIndex: 0, pageSize: 10 });
      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>
      <div class="flex items-center py-4">
        <Input
          placeholder="Filter emails..."
          value={(table.getColumn("email")?.getFilterValue() as string) ?? ""}
          onchange={(e) => {
            table.getColumn("email")?.setFilterValue(e.currentTarget.value);
          }}
          oninput={(e) => {
            table.getColumn("email")?.setFilterValue(e.currentTarget.value);
          }}
          class="max-w-sm"
        />
      </div>
      <div class="rounded-md border">
        <Table.Root><!-- ... --></Table.Root>
      </div>
    </div>
    ```

    Filtering is now enabled for the `email` column. You can add filters to other columns as well. See the [filtering docs](https://tanstack.com/table/v8/docs/guide/filters) for more information on customizing filters.
  </Step>
</Steps>

## Visibility

Adding column visibility is fairly simple using `@tanstack/table-core` visibility API.

<Steps>
  <Step title="Update DataTable">
    ```svelte title="routes/payments/data-table.svelte" theme={null}
    <script lang="ts" generics="TData, TValue">
      import {
        type ColumnDef,
        type PaginationState,
        type SortingState,
        type ColumnFiltersState,
        type VisibilityState,
        getCoreRowModel,
        getPaginationRowModel,
        getSortedRowModel,
        getFilteredRowModel,
      } from "@tanstack/table-core";
      import * as DropdownMenu from "$lib/components/ui/dropdown-menu/index.js";

      let { columns, data }: DataTableProps<TData, TValue> = $props();

      let pagination = $state<PaginationState>({ pageIndex: 0, pageSize: 10 });
      let sorting = $state<SortingState>([]);
      let columnFilters = $state<ColumnFiltersState>([]);
      let columnVisibility = $state<VisibilityState>({});

      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;
          }
        },
        onColumnVisibilityChange: (updater) => {
          if (typeof updater === "function") {
            columnVisibility = updater(columnVisibility);
          } else {
            columnVisibility = updater;
          }
        },
        state: {
          get pagination() {
            return pagination;
          },
          get sorting() {
            return sorting;
          },
          get columnFilters() {
            return columnFilters;
          },
          get columnVisibility() {
            return columnVisibility;
          },
        },
      });
    </script>

    <div>
      <div class="flex items-center py-4">
        <Input
          placeholder="Filter emails..."
          value={table.getColumn("email")?.getFilterValue() as string}
          onchange={(e) =>
            table.getColumn("email")?.setFilterValue(e.currentTarget.value)}
          oninput={(e) =>
            table.getColumn("email")?.setFilterValue(e.currentTarget.value)}
          class="max-w-sm"
        />
        <DropdownMenu.Root>
          <DropdownMenu.Trigger>
            {#snippet child({ props })}
              <Button {...props} variant="outline" class="ms-auto">Columns</Button>
            {/snippet}
          </DropdownMenu.Trigger>
          <DropdownMenu.Content align="end">
            {#each table
              .getAllColumns()
              .filter((col) => col.getCanHide()) as column (column.id)}
              <DropdownMenu.CheckboxItem
                class="capitalize"
                bind:checked={
                  () => column.getIsVisible(), (v) => column.toggleVisibility(!!v)
                }
              >
                {column.id}
              </DropdownMenu.CheckboxItem>
            {/each}
          </DropdownMenu.Content>
        </DropdownMenu.Root>
      </div>
      <div class="rounded-md border">
        <Table.Root><!--...--></Table.Root>
      </div>
    </div>
    ```

    This adds a dropdown menu that you can use to toggle column visibility.
  </Step>
</Steps>

## Row Selection

Next, we're going to add row selection to our table.

<Steps>
  <Step title="Define DataTableCheckbox component">
    We'll start by defining the checkbox component in our `data-table-checkbox.svelte` component.

    ```svelte title="routes/payments/data-table-checkbox.svelte" theme={null}
    <script lang="ts">
      import type { ComponentProps } from "svelte";
      import { Checkbox } from "$lib/components/ui/checkbox/index.js";

      let {
        checked = false,
        onCheckedChange = (v) => (checked = v),
        ...restProps
      }: ComponentProps<typeof Checkbox> = $props();
    </script>

    <Checkbox bind:checked={() => checked, onCheckedChange} {...restProps} />
    ```
  </Step>

  <Step title="Update columns definition">
    Now that we have a new component, we can add a `select` column definition to render a checkbox.

    ```ts title="routes/payments/columns.ts" theme={null}
    import type { ColumnDef } from "@tanstack/table-core";
    import { renderComponent } from "$lib/components/ui/data-table/index.js";
    import { Checkbox } from "$lib/components/ui/checkbox/index.js";

    export const columns: ColumnDef<Payment>[] = [
      // ...
      {
        id: "select",
        header: ({ table }) =>
          renderComponent(Checkbox, {
            checked: table.getIsAllPageRowsSelected(),
            indeterminate:
              table.getIsSomePageRowsSelected() &&
              !table.getIsAllPageRowsSelected(),
            onCheckedChange: (value) => table.toggleAllPageRowsSelected(!!value),
            "aria-label": "Select all",
          }),
        cell: ({ row }) =>
          renderComponent(Checkbox, {
            checked: row.getIsSelected(),
            onCheckedChange: (value) => row.toggleSelected(!!value),
            "aria-label": "Select row",
          }),
        enableSorting: false,
        enableHiding: false,
      },
    ];
    ```
  </Step>

  <Step title="Update DataTable">
    ```svelte title="routes/payments/data-table.svelte" theme={null}
    <script lang="ts" generics="TData, TValue">
      import {
        type ColumnDef,
        type PaginationState,
        type SortingState,
        type ColumnFiltersState,
        type VisibilityState,
        type RowSelectionState,
        getCoreRowModel,
        getPaginationRowModel,
        getSortedRowModel,
        getFilteredRowModel,
      } from "@tanstack/table-core";
      import * as DropdownMenu from "$lib/components/ui/dropdown-menu/index.js";

      let { columns, data }: DataTableProps<TData, TValue> = $props();

      let pagination = $state<PaginationState>({ pageIndex: 0, pageSize: 10 });
      let sorting = $state<SortingState>([]);
      let columnFilters = $state<ColumnFiltersState>([]);
      let columnVisibility = $state<VisibilityState>({});
      let rowSelection = $state<RowSelectionState>({});

      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;
          }
        },
        onColumnVisibilityChange: (updater) => {
          if (typeof updater === "function") {
            columnVisibility = updater(columnVisibility);
          } else {
            columnVisibility = updater;
          }
        },
        onRowSelectionChange: (updater) => {
          if (typeof updater === "function") {
            rowSelection = updater(rowSelection);
          } else {
            rowSelection = updater;
          }
        },
        state: {
          get pagination() {
            return pagination;
          },
          get sorting() {
            return sorting;
          },
          get columnFilters() {
            return columnFilters;
          },
          get columnVisibility() {
            return columnVisibility;
          },
          get rowSelection() {
            return rowSelection;
          },
        },
      });
    </script>
    ```

    This adds a checkbox to each row and a checkbox in the header to select all rows.
  </Step>

  <Step title="Show selected rows">
    You can show the number of selected rows using the `table.getFilteredSelectedRowModel()` API.

    ```svelte theme={null}
    <div class="text-muted-foreground flex-1 text-sm">
      {table.getFilteredSelectedRowModel().rows.length} of{" "}
      {table.getFilteredRowModel().rows.length} row(s) selected.
    </div>
    ```
  </Step>
</Steps>

## Reusable Components

Check out the [Tasks](https://www.shadcn-svelte.com/examples/tasks) example to learn about creating reusable components for your data tables.
