addExpandedRows

addExpandedRows expands and collapses sub-rows of rows. Sub-rows are defined by plugins such as addSubRows or addGroupBy.

Options

Information:
Options passed into addExpandedRows.
const table = createTable(data, {
  expand: addExpandedRows({ ... }),
});
const table = createTable(data, {
  expand: addExpandedRows({ ... }),
});

initialExpandedIds?: Record<string, boolean>

Initial expanded row ids as an object mapping rowId => boolean. Nested sub-rows use {parentId}>{id}.

Column Options

Information:
Options passed into column definitions.
const columns = table.createColumns([
  table.column({
    header: 'Name',
    accessor: 'name',
    plugins: {
      expand: { ... },
    },
  }),
]);
const columns = table.createColumns([
  table.column({
    header: 'Name',
    accessor: 'name',
    plugins: {
      expand: { ... },
    },
  }),
]);

Prop Set

Information:
Extensions to the view model. Read current.props on table components.
{#each vm.current.headerRows as headerRow (headerRow.id)}
  {headerRow.current.props.expand}
  {#each headerRow.cells as cell (cell.id)}
    {cell.current.props.expand}
  {/each}
{/each}
{#each vm.current.headerRows as headerRow (headerRow.id)}
  {headerRow.current.props.expand}
  {#each headerRow.cells as cell (cell.id)}
    {cell.current.props.expand}
  {/each}
{/each}

Interactions

  • Filters (addColumnFilters, addTableFilter) keep a sub-row when it or one of its descendants matches. Registering the filter plugin before expand filters the tree first; either order yields the same visible rows.
  • With addPagination, expanded children count toward the page size, so expanding a row can push later rows onto the next page.

Plugin State

Information:
State provided by addExpandedRows.
const vm = table.createViewModel(columns)
const { pluginStates } = vm
const { expandedIds, getRowState } = pluginStates.expand
const vm = table.createViewModel(columns)
const { pluginStates } = vm
const { expandedIds, getRowState } = pluginStates.expand

expandedIds: RecordSet

The expanded row ids. Read expandedIds.current (a Record<string, boolean> holding only true entries), or change it with add(id), remove(id), toggle(id), addAll(ids), removeAll(ids) and clear(). Assigning expandedIds.current replaces the whole set.

getRowState: (row) => ExpandedRowsRowState

Returns the expansion state of a BodyRow:

  • isExpanded: Box<boolean>: writing adds or removes the row’s id in expandedIds
  • canExpand: boolean: whether the row currently has sub-rows. Read it from the state object (getRowState(row).canExpand) when you render; a value you destructured earlier is a snapshot.
  • isAllSubRowsExpanded: ReadonlyBox<boolean>

The state holds nothing of its own (it reads expandedIds), so it is safe to call anywhere, including in a template. The same row object always returns the same view.

Information:
v7 removed invalidate(): there is no per-row cache to clear.
<!-- ExpandIndicator.svelte, rendered with createRender(ExpandIndicator, getRowState(row)) -->
<script lang="ts">
    import type { Box } from '@humanspeak/svelte-headless-table'
    const { isExpanded, canExpand }: { isExpanded: Box<boolean>; canExpand: boolean } = $props()
</script>

{#if canExpand}
    <button onclick={() => (isExpanded.current = !isExpanded.current)}>
        {isExpanded.current ? 'Collapse' : 'Expand'}
    </button>
{/if}
<!-- ExpandIndicator.svelte, rendered with createRender(ExpandIndicator, getRowState(row)) -->
<script lang="ts">
    import type { Box } from '@humanspeak/svelte-headless-table'
    const { isExpanded, canExpand }: { isExpanded: Box<boolean>; canExpand: boolean } = $props()
</script>

{#if canExpand}
    <button onclick={() => (isExpanded.current = !isExpanded.current)}>
        {isExpanded.current ? 'Collapse' : 'Expand'}
    </button>
{/if}

Examples

Simple row expanding

Name (10 users)Info
First NameLast NameAgeStatusVisitsProfile Progress
RickyMertz43complicated4233
RobertoKling30single6258
HeleneRuecker53single4213
SharonGrady42single4367
EmeraldEbert40relationship2094
AnjaliProsacco64single8611
DeanReinger36complicated4570
MyrticeKemmer41single2098
PasqualeFay64complicated9551
MajorMedhurst58relationship2644

Simple grouping by column

{
  "groupByIds": []
}
Name (30 users) Info
First Name Last Name Age Status Visits Profile Progress
JanaeLind-Kuvalis21single2750
ArthurEffertz39complicated942
JonasLedner54relationship6747
JosefinaRunte33complicated5727
GillianJerde61complicated1874
KayceeBosco52single5270
ElliotWintheiser63single8547
JessycaCollins34relationship3230
NonaKoelpin55single8968
ShermanSchinner52relationship1466
IvySchuster54complicated042
LisaChamplin29relationship6613
PhillipLemke56relationship9827
ClaudieSporer26complicated6781
ElenaAdams35relationship615
GuyMcLaughlin63relationship10024
JessieMonahan59relationship227
AliHessel38complicated1852
CandidaHodkiewicz27relationship9283
JazminWalker36single6560
TryciaPacocha52single6614
KentSchuppe-Nienow54relationship1831
SylvanBraun50relationship8263
RoderickLangworth48complicated2685
BradlyRomaguera44relationship4690
AllanTrantow46relationship3499
GinaHansen60relationship6533
JalynTromp25single7813
TrevionLedner22relationship1015
UnaCarter28complicated7794