Cookbook / Data Table

Data Table

list

Sortable data table — derived() sorts a copy of the prop rows, each/key renders rows, click headers to toggle direction.

id
data-table
since
0.5.0
file
data-table.aihu
propstatederivedactioneachkeyon:clickinterpolation

Run it

source · static

This recipe doesn't have a hydrated island in the demo gallery yet. An in-browser, WASM-compiled playground (edit this source and re-render live) is planned as a follow-up — read the real source below.

Source

data-table.aihu
governeddata-table.aihuaihu2.3 kB
// data-table — prop rows + action sort(col), derived sortedRows, each with key

@state {
  const rows = prop({
    default: [
        { id: '1', name: 'Alice', role: 'Engineer', score: 92 },
        { id: '2', name: 'Bob', role: 'Designer', score: 78 },
        { id: '3', name: 'Carol', role: 'Manager', score: 85 },
        { id: '4', name: 'Dave', role: 'Engineer', score: 91 },
      ],
  })

  let sortCol = state('name')
  let sortDir = state(1)

  const sortedRows = derived(() => [...rows].sort((a, b) => {
      const av = String(a[sortCol] ?? '')
      const bv = String(b[sortCol] ?? '')
      return av.localeCompare(bv) * sortDir
    }))

  const sort = action((col: string) => {
      if (sortCol === col) {
        sortDir = sortDir === 1 ? -1 : 1
      } else {
        sortCol = col
        sortDir = 1
      }
    })
}

@template {
  <div class="data-table-wrapper">
    <table class="data-table">
      <thead>
        <tr>
          <th on:click={() => sort('name')} class={['col-hdr', sortCol === 'name' && 'sorted']}>Name {sortCol === 'name' ? (sortDir === 1 ? '▲' : '▼') : ''}</th>
          <th on:click={() => sort('role')} class={['col-hdr', sortCol === 'role' && 'sorted']}>Role {sortCol === 'role' ? (sortDir === 1 ? '▲' : '▼') : ''}</th>
          <th on:click={() => sort('score')} class={['col-hdr', sortCol === 'score' && 'sorted']}>Score {sortCol === 'score' ? (sortDir === 1 ? '▲' : '▼') : ''}</th>
        </tr>
      </thead>
      <tbody>
        <tr each={row of sortedRows} key={row.id} class="data-row">
          <td>{row.name}</td>
          <td>{row.role}</td>
          <td class="score">{row.score}</td>
        </tr>
      </tbody>
    </table>
  </div>
}

@style {
  .data-table-wrapper { overflow-x: auto; max-width: 100%; }
  .data-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
  .col-hdr { padding: 0.5rem 0.75rem; text-align: left; background: var(--table-hdr, #f5f5f5); border-bottom: 2px solid var(--border, #ccc); cursor: pointer; user-select: none; white-space: nowrap; }
  .col-hdr.sorted { color: var(--accent, #0066cc); }
  .data-row td { padding: 0.4rem 0.75rem; border-bottom: 1px solid var(--border, #eee); }
  .data-row:hover td { background: var(--hover-bg, #fafafa); }
  .score { font-variant-numeric: tabular-nums; font-weight: 600; }
}

Requires

concerns
state

Anti-patterns

  • Do not sort the prop array in place — [...rows].sort() keeps the source identity stable so the derivation re-runs cleanly.

Related recipes