Row Selection


Useful Links

Actions


Table Demo


SelectNameAgeEmailPhone
Kendra Weissnat-Ziemann41Lucious23@hotmail.com(852) 838-0609
Eleanor Hirthe28Dion38@gmail.com(279) 388-6308
Helen Nolan22Casimir.Stokes@gmail.com(618) 580-1053
Richard Bartell-Conn36Adolf51@gmail.com(359) 280-6211
Hubert Gorczany84Madaline27@hotmail.com(444) 771-0168
Clint Jenkins89Linnie22@hotmail.com(796) 471-3480
Mr. Melvin Leuschke-MacGyver66Ressie.Smith@yahoo.com(383) 241-5706
Eddie Dickens IV32Tad18@yahoo.com(698) 637-5272
Christian Conroy77Irwin.Lubowitz@gmail.com(601) 550-2439
Earnest West83Garrett_Crist@hotmail.com(761) 925-7502

Debug


Selection State

{}

Explanation


In this guide, we'll focus on enhancing a table by adding a selection state that allows users to select rows using checkboxes. We'll go through the steps of creating a checkbox wrapper component, defining the columns with a selection column, setting up a reactive state for row selection, and ensuring that the table responds to selection changes.

You should already know how to make a basic table before proceeding.

🗒️ Note: This process works for all kinds of table state.

Creating the Checkbox Wrapper

We start by creating a simple checkbox wrapper component called TableCheckbox. This component will handle the checkbox rendering for each row in the table.

<!-- table-checkbox.svelte -->
<script lang="ts">
  import type { HTMLInputAttributes } from 'svelte/elements';

  type Props = {} & HTMLInputAttributes;

  let { ...inputProps }: Props = $props();
</script>

<input type="checkbox" {...inputProps} />

Creating a Selection Column

Next, we define the table columns using a column helper, with a specific focus on adding a selection column that uses the TableCheckbox component.

<!-- +page.svelte -->
<script lang="ts">
  import { createColumnHelper, renderComponent } from '$lib/table';
  import TableCheckbox from './_components/table-checkbox.svelte';
  import { type UserProfile } from '$lib/services/user-profile';

  // Create a column helper for the user profile data.
  const colHelp = createColumnHelper<UserProfile>();

  // Define the columns including a column for row selection.
  const columnDefs = [
    // Add a column for selection
    colHelp.display({
      header: 'Select',
      cell: ({ row }) =>
        renderComponent(TableCheckbox, {
          checked: row.getIsSelected(),
          onchange: () => {
            row.toggleSelected();
          }
        })
    }),
    colHelp.accessor('name', { header: 'Name' }),
    colHelp.accessor('age', { header: 'Age' }),
    colHelp.accessor('email', { header: 'Email' }),
    colHelp.accessor('phone', { header: 'Phone' })
  ];
</script>

The checkbox's onchange event toggles the selection of the respective row when clicked.

Setting Up Selection State

To manage the row selection state reactively, we use a $state rune to create a reactive signal that tracks which rows are selected.

<script lang="ts">
  import { createColumnHelper, renderComponent } from '$lib/table'; 
  import { type RowSelectionState, createColumnHelper, renderComponent } from '$lib/table'; 
  import TableCheckbox from './_components/table-checkbox.svelte';
  import { type UserProfile } from '$lib/services/user-profile';

  const colHelp = createColumnHelper<UserProfile>();

  const columnDefs = [
    colHelp.display({
      header: 'Select',
      cell: ({ row }) =>
        renderComponent(TableCheckbox, {
          checked: row.getIsSelected(),
          onchange: () => {
            row.toggleSelected();
          }
        })
    }),
    colHelp.accessor('name', { header: 'Name' }),
    colHelp.accessor('age', { header: 'Age' }),
    colHelp.accessor('email', { header: 'Email' }),
    colHelp.accessor('phone', { header: 'Phone' })
  ];

  // Define a reactive state to track the row selection state.
  let rowSelectionState: RowSelectionState = $state({}); 
</script>

Creating the Updater Function

To handle changes in the row selection state, we define an updater function. This function will be called whenever a row's selection state changes.

<script lang="ts">
  import { type RowSelectionState, createColumnHelper, renderComponent } from '$lib/table'; 
  import { 
    type RowSelectionState, 
    type Updater, 
    createColumnHelper, 
    renderComponent 
  } from '$lib/table'; 
  import TableCheckbox from './_components/table-checkbox.svelte';
  import { type UserProfile } from '$lib/services/user-profile';

  const colHelp = createColumnHelper<UserProfile>();

  const columnDefs = [
    colHelp.display({
      header: 'Select',
      cell: ({ row }) =>
        renderComponent(TableCheckbox, {
          checked: row.getIsSelected(),
          onchange: () => {
            row.toggleSelected();
          }
        })
    }),
    colHelp.accessor('name', { header: 'Name' }),
    colHelp.accessor('age', { header: 'Age' }),
    colHelp.accessor('email', { header: 'Email' }),
    colHelp.accessor('phone', { header: 'Phone' })
  ];

  let rowSelectionState: RowSelectionState = $state({});

  function onRowSelectionChange(updater: Updater<RowSelectionState>) { 
    // Update the selection state by reassigning the $state
    if (updater instanceof Function) { 
      rowSelectionState = updater(rowSelectionState); 
    } else { 
      rowSelectionState = updater; 
    } 
  } 
</script>
  • State Update: The updater function modifies the rowSelectionState, ensuring that the state rune and the table itself are synchronized.
  • Reactivity: Reassigning the $state triggers the reactive updates automatically.

Setting Up the Table

When setting up the table, it is essential to make the rowSelection state a getter to ensure reactivity. This way, the table automatically updates when the selection state changes.

<script lang="ts">
  import {
    type RowSelectionState,
    type Updater,
    createColumnHelper,
    renderComponent,
    createSvelteTable, 
    getCoreRowModel 
  } from '$lib/table';
  import TableCheckbox from './_components/table-checkbox.svelte';
  import { type UserProfile } from '$lib/services/user-profile'; 
  import { type UserProfile, userProfiles } from '$lib/services/user-profile'; 

  const colHelp = createColumnHelper<UserProfile>();

  const columnDefs = [
    colHelp.display({
      header: 'Select',
      cell: ({ row }) =>
        renderComponent(TableCheckbox, {
          checked: row.getIsSelected(),
          onchange: () => {
            row.toggleSelected();
          }
        })
    }),
    colHelp.accessor('name', { header: 'Name' }),
    colHelp.accessor('age', { header: 'Age' }),
    colHelp.accessor('email', { header: 'Email' }),
    colHelp.accessor('phone', { header: 'Phone' })
  ];

  let rowSelectionState: RowSelectionState = $state({});

  function onRowSelectionChange(updater: Updater<RowSelectionState>) {
    if (updater instanceof Function) {
      rowSelectionState = updater(rowSelectionState);
    } else {
      rowSelectionState = updater;
    }
  }

  // Create the table and bind the row selection state using a getter.
  const table = createSvelteTable({ 
    data: userProfiles, 
    columns: columnDefs, 
    state: { 
      get rowSelection() { 
        return rowSelectionState; 
      } 
    }, 
    onRowSelectionChange: onRowSelectionChange, 
    getCoreRowModel: getCoreRowModel() 
  }); 
</script>

Rendering the Table in the Markup

Finally, we render the table and include a button that allows users to toggle the selection of all rows at once.

<div>
  <h2>Actions</h2>
  <hr />
  <button onclick={() => table.toggleAllRowsSelected()}>
    {#if table.getIsAllRowsSelected()}
      Deselect All
    {:else}
      Select All
    {/if}
  </button>
</div>

<table>
  <thead>
    <tr>
      {#each table.getHeaderGroups() as headerGroup}
        {#each headerGroup.headers as header}
          <th>
            <FlexRender content={header.column.columnDef.header} context={header.getContext()} />
          </th>
        {/each}
      {/each}
    </tr>
  </thead>
  <tbody>
    {#each table.getRowModel().rows as row}
      <tr>
        {#each row.getVisibleCells() as cell}
          <td>
            <FlexRender content={cell.column.columnDef.cell} context={cell.getContext()} />
          </td>
        {/each}
      </tr>
    {/each}
  </tbody>
</table>