# Column definitions

> Describe each column once, with a key, a header and a type, and the grid handles display, sorting, filtering and export.

A grid takes `columns`, an array of column definitions in display order. Each definition names the row field to read (`key`), the title to show (`header`), and a `type` that decides how the value is formatted, aligned, sorted and filtered. Everything else, such as width, pinning, tooltips and editing, is an optional field on the same object.

Sort a column from its header, hover a cut-off value for its tooltip, or drag a header edge to resize:

Example: Column definitions (columns.svelte)

```svelte
<script lang="ts">
  import { Grid, type ColumnDef, type DefaultColumn } from 'bo-grid';
  import { quotes } from './data';

  // Each column names a row field (`key`), a header, and a `type` that picks
  // how the value shows, sorts and filters. Widths are fixed (`width`) or
  // share the space left over (`flex`).
  const columns: ColumnDef[] = [
    { type: 'text', key: 'symbol', header: 'Symbol', sub: 'name', width: 170 },
    { type: 'price', key: 'price', header: 'Price', width: 96 },
    { type: 'percent', key: 'change', header: 'Chg %', width: 90 },
    { type: 'heatmap', key: 'change', header: 'Heat', min: -2, max: 2, width: 80 },
    { type: 'volume', key: 'volume', header: 'Volume', width: 100 },
    { type: 'text', key: 'sector', header: 'Sector', flex: 1, sortable: false },
  ];

  // Settings every column starts from; a column's own fields win.
  const defaultColumn: DefaultColumn = { tooltip: true, minWidth: 64 };

  let rows = $state(quotes());
</script>

<Grid {rows} {columns} {defaultColumn} height={300} />
```

data.ts:

```ts
// Small, fixed data for the docs' examples: every example reads the same
// symbols, so a reader compares features, not datasets.
// A type, not an interface: it then fits GridRow's index signature.
export type Quote = {
  id: number;
  symbol: string;
  name: string;
  sector: string;
  price: number;
  change: number;
  volume: number;
};

const BASE: Omit<Quote, 'id'>[] = [
  { symbol: 'VNM', name: 'Vinamilk', sector: 'Consumer', price: 68.5, change: 1.2, volume: 1_204_000 },
  { symbol: 'FPT', name: 'FPT Corp', sector: 'Technology', price: 132, change: -0.4, volume: 860_500 },
  { symbol: 'HPG', name: 'Hoa Phat', sector: 'Materials', price: 27.35, change: 0.74, volume: 3_412_900 },
  { symbol: 'VCB', name: 'Vietcombank', sector: 'Financials', price: 91.5, change: 0.55, volume: 1_986_200 },
  { symbol: 'MWG', name: 'Mobile World', sector: 'Consumer', price: 51.4, change: -1.1, volume: 2_250_400 },
  { symbol: 'SSI', name: 'SSI Securities', sector: 'Financials', price: 33.3, change: -0.45, volume: 4_104_700 },
  { symbol: 'VIC', name: 'Vingroup', sector: 'Real estate', price: 44.15, change: -0.11, volume: 1_530_100 },
  { symbol: 'GAS', name: 'PV Gas', sector: 'Energy', price: 71.8, change: 0.3, volume: 645_800 },
  { symbol: 'MSN', name: 'Masan', sector: 'Consumer', price: 76.2, change: 2.05, volume: 1_120_300 },
  { symbol: 'TCB', name: 'Techcombank', sector: 'Financials', price: 23.9, change: 0.84, volume: 5_870_000 },
  { symbol: 'VHM', name: 'Vinhomes', sector: 'Real estate', price: 41.6, change: -0.72, volume: 2_904_600 },
  { symbol: 'POW', name: 'PV Power', sector: 'Energy', price: 12.25, change: 1.66, volume: 6_315_200 },
];

/** A fresh copy of the quotes, so one example's edits never reach another. */
export function quotes(): Quote[] {
  return BASE.map((q, i) => ({ id: i + 1, ...q }));
}
```

## The shape of a column

```svelte
<script lang="ts">
  import { Grid, type ColumnDef } from 'bo-grid';

  const columns: ColumnDef[] = [
    { type: 'text', key: 'symbol', header: 'Symbol', sub: 'name', width: 170 },
    { type: 'price', key: 'price', header: 'Price', width: 96 },
    { type: 'percent', key: 'change', header: 'Chg %', width: 90 },
    { type: 'volume', key: 'volume', header: 'Volume', width: 100 },
    { type: 'text', key: 'sector', header: 'Sector', flex: 1 },
  ];

  let rows = $state([
    { id: 1, symbol: 'VNM', name: 'Vinamilk', price: 68.5, change: 1.2, volume: 1_204_000, sector: 'Consumer' },
  ]);
</script>

<Grid {rows} {columns} height={400} />
```

- **`key`** is the row field the column reads. It is also the column's identity: sorting, filtering, hiding and saved layouts refer to columns by key, so keep keys unique.
- **`header`** is the title in the header row.
- **`type`** picks the cell's format and behaviour. `ColumnDef` is a union on `type`, so TypeScript checks each type's own options (`decimals` on `number`, `min` and `max` on `heatmap`, `currency` on `currency`). See [Column types](https://bonguynvan.github.io/bo-grid/docs/column-types/) for the full list.
- Rows are plain objects with a unique `id`: a number, or a string such as a symbol or UUID. If your data has another identity field, pass `getRowId` instead of copying it into `id`.

## Widths

A column is either fixed or flexible:

- **`width`** sets a fixed width in pixels.
- **`flex`** sets a grow weight: flex columns share the width that fixed columns leave over, in proportion to their weights. A column with `flex` ignores `width`.
- **`minWidth`** and **`maxWidth`** bound what a drag-resize can do.

To make every column scale with the grid instead, set `fitColumns` on the grid. [Column sizing](https://bonguynvan.github.io/bo-grid/docs/column-sizing/) covers resizing, `fitColumns` and `autoWidth`.

## Alignment, classes and formatting

Number types line up on the right and the rest on the left. Set `align` to override that for one column, for example a custom column that holds numbers.

`format(value, row)` replaces the built-in formatter. It applies wherever the value is shown as text: the cell, its tooltip, copy and formatted export.

```ts
{ type: 'number', key: 'ratio', header: 'Ratio', format: (v) => `${Number(v).toFixed(1)}×` }
```

`cellClass` and `headerClass` add your own classes. `cellClass` can be a function of the value and the row. Rows render inside the grid, so target the classes with `:global(...)`; see [Row styles](https://bonguynvan.github.io/bo-grid/docs/row-styles/).

## Defaults for every column

`defaultColumn` sets fields that every column starts from. A column's own fields, and a registered `cellType`, win over it:

```svelte
<Grid {rows} {columns} defaultColumn={{ tooltip: true, minWidth: 64, resizable: false }} height={480} />
```

To reuse a whole kind of column across grids, such as a money amount or an order status, register it once with `cellTypes`: see [Registered cell types](https://bonguynvan.github.io/bo-grid/docs/column-types/#registered-cell-types).

## Locale

Built-in number, price, currency and date formatting uses `en-US` by default. Set the grid's `locale` to change it for every column, or a column's own `locale` to change one:

```svelte
<Grid {rows} {columns} locale="vi-VN" height={480} />
```

[Localization](https://bonguynvan.github.io/bo-grid/docs/localization/) covers the grid's own strings too.

## Reference

- `key`: `string` — Field on the row to read for this column's value. Also the column's id: sort, filter, width and hidden state are keyed by it.
- `header`: `string` — The column's title in the header row.
- `width`: `number` (default `96`) — Width in px. A `flex` column starts from it where columns have fixed widths (pinned columns, `virtualizeColumns`), and `fitColumns` scales columns in proportion to it.
- `flex`: `number` (default `undefined`) — flex-grow weight: the column stretches to fill the space the fixed-width columns leave, shared by weight. When the columns no longer fit, the grid scrolls sideways, counting a flex column as its `width`, else its `minWidth`, else 64 px. Unset, the column keeps its `width`.
- `minWidth`: `number` (default `48`) — Smallest width (px) a drag-resize or autosize can give this column; never less than 48. Under `fitColumns`, also the narrowest the column gets before the grid scrolls sideways (64 px without one, or the column's `width` if smaller).
- `maxWidth`: `number` (default `Infinity`) — Largest width (px) a drag-resize, an autosize, `autoWidth` or `fitColumns` can give this column.
- `align`: `Align` (default `'right' for number types, 'left' for the rest`) — Which side the header, cells and footer sit on. Set it to line a custom column's values up under its title.

  ```ts
  { type: 'custom', key: 'pnl', header: 'P&L', align: 'right' }
  ```

- `format`: `(value: unknown, row?: GridRow) => string` — Custom display formatter, overriding the built-in type formatter. Applies to display, tooltip, copy and (formatted) export. `row` is absent for aggregate cells.

  ```ts
  { type: 'number', key: 'qty', header: 'Qty', format: (v) => Number(v).toLocaleString('vi-VN') }
  ```

- `cellClass`: `string | ((value: unknown, row: GridRow) => string | undefined)` (default `undefined`) — Extra class(es) for this column's data cells — a static string, or a function of the cell value/row for conditional styling. Target via `:global(.bo-grid .c.your-class)`.

  ```ts
  { type: 'price', key: 'last', header: 'Last', cellClass: (v, row) => (Number(v) >= Number(row.ceiling) ? 'at-ceiling' : undefined) }
  ```

- `headerClass`: `string` (default `undefined`) — Extra class(es) for this column's header. Target via `:global`.
- `sortable`: `boolean` (default `true`) — Set false to disable header-click sorting on this column.
- `locale`: `string` (default `undefined`) — BCP 47 locale for this column's built-in formatting (`price`, `date`, `currency`, `relative`). Unset, the grid's `locale`, then `en-US`.

- `columns`: `ColumnDef[]` — The columns, in display order. A column's `type` picks how its values show, sort and filter.

  ```ts
  const columns: ColumnDef[] = [
    { type: 'text', key: 'sym', header: 'Symbol', width: 90 },
    { type: 'price', key: 'last', header: 'Last', flash: 'auto' },
  ];
  ```

- `defaultColumn`: `DefaultColumn` (default `undefined`) — Settings applied under every column — its own fields (and a registered `cellType`) win.

  ```svelte
  <Grid {rows} {columns} height={480} defaultColumn={{ minWidth: 72, sortable: false }} />
  ```

- `getRowId`: `(row: GridRow) => string | number` (default `(row) => row.id`) — Identity key for rows: selection, focus, pins and flashes follow it. Override for string/UUID/composite keys.

  ```svelte
  <Grid {rows} {columns} height={480} getRowId={(row) => `${row.account}:${row.sym}`} />
  ```

- `locale`: `string` (default `undefined`) — BCP 47 locale for the grid's own number formatting (aggregation bar, pager row count), and the default for each column's `locale`. Unset, the runtime's locale.

---
Page: https://bonguynvan.github.io/bo-grid/docs/columns/ · All pages: https://bonguynvan.github.io/bo-grid/llms.txt
