Table columns
Choose which columns to show and keep that choice for later visits in the same browser.
| Invoice | Client | Issued | Amount |
|---|---|---|---|
| INV-1041 | Northwind Traders | 2026-09-21 | 1280 |
| INV-1042 | Blue Yonder Airlines | 2026-09-22 | 460.5 |
| INV-1043 | Contoso | 2026-09-22 | 2150 |
| INV-1044 | Fabrikam | 2026-09-23 | 90 |
<script setup lang="ts">import { ref } from 'vue'import { Plus } from '@lucide/vue'import type { TableColumn } from 'grani'import { UiCard, UiTableToolbar, UiButton, UiTable, useTableColumns } from 'grani' interface Invoice { id: string client: string issued: string amount: number note: string} const ROWS: Invoice[] = [ { id: 'INV-1041', client: 'Northwind Traders', issued: '2026-09-21', amount: 1280, note: 'Paid by card', }, { id: 'INV-1042', client: 'Blue Yonder Airlines', issued: '2026-09-22', amount: 460.5, note: '' }, { id: 'INV-1043', client: 'Contoso', issued: '2026-09-22', amount: 2150, note: 'Second reminder sent', }, { id: 'INV-1044', client: 'Fabrikam', issued: '2026-09-23', amount: 90, note: '' },] const columns: TableColumn<Invoice>[] = [ { key: 'id', label: 'Invoice', width: '110px' }, { key: 'client', label: 'Client', truncate: true }, { key: 'issued', label: 'Issued', width: '120px' }, { key: 'amount', label: 'Amount', align: 'end', width: '110px' }, { key: 'note', label: 'Note', truncate: true },] // The invoice number says what a row is, so it cannot be hidden; the note is off until asked for.const query = ref('') const cols = useTableColumns('demo-invoices', columns, { required: ['id'], hiddenByDefault: ['note'],})</script> <template> <UiCard :padded="false" class="w-full"> <UiTableToolbar v-model:search="query" :columns="cols" class="border-b border-border"> <template #end> <UiButton><Plus />New invoice</UiButton> </template> </UiTableToolbar> <UiTable :columns="cols.visible.value" :items="ROWS" /> </UiCard></template>
Also called column picker, column visibility, show hide columns.
Usage
<UiTableColumns :columns="columns" /> With GraniResolver in the Vite config, a template needs no import: set it up once.
API
| Name · type | Default | Description |
|---|---|---|
| columns*TableColumns<T> | The columns state from useTableColumns: every column, the hidden ones, which may be hidden. The table draws its visible. | |
| sizexs | sm | md | Step of the control ladder for the button: the toolbar row's, the app's size (createGrani({ size })) when unset. |
Attributes: class and style stay on the outer element; the rest (native attributes, aria-*, data-testid) reach the control inside it.
When to use another component
| When you need | Take |
|---|---|
| Narrowing the rows | Filter |
| A period | Date range |
| One choice among a few | Select |
| A second menu beside a toolbar | Never; the toolbar draws it from columns |