Provided cell editors
The grid ships nine built-in editors, selected via editorType on the
column definition. Each editor renders inside the cell while editing
and falls back to the default text render (or your cell snippet) when
the cell is read-only.
editorType |
Renders | Stored value |
|---|---|---|
'text' |
<input type="text"> |
string |
'number' |
<input type="text" inputmode="decimal"> |
number | null |
'date' |
<input type="date"> |
ISO YYYY-MM-DD string |
'datetime' |
<input type="datetime-local"> |
ISO 8601 string |
'checkbox' |
Themed checkbox button | boolean |
'list' |
Custom dropdown (single / multi) | scalar or array |
'chips' |
Removable tag picker | array of values |
'color' |
Native OS color picker | #rrggbb string |
'rating' |
5-star clickable widget | number 0-5 |
Text editor - editorType: 'text'
<input type="text">. Accepts any string. On commit the raw value is
stored.
{ field: 'firstName', header: 'First name', editorType: 'text' }
Number editor - editorType: 'number'
A text input with inputmode="decimal", coerced to a number at commit.
It is deliberately not <input type="number">. That element runs the HTML
value-sanitization algorithm, so it only ever reports a value that parses as a
valid float - and every intermediate state on the way to 12.5 is not one. The
element reported "" for 12., the editor read that into its draft, and the
decimal point the user had just typed disappeared: typing 12.5 produced
125. The same applied to a lone - on the way to a negative, and 1e on the
way to 1e3.
A text input keeps the raw keystrokes, and parseEditorValue coerces at commit
exactly as it already did. Characters that can never be part of a number are
rejected as you type, which is the one guarantee type="number" did provide.
The trade-off is the native increment buttons, which a text input cannot
render. inputmode="decimal" keeps the numeric keypad on mobile.
Non-numeric input is still rejected at commit (parseEditorValue returns
null and the cell stays at its previous value).
The filter row and filter menu still use <input type="number"> for numeric
columns, so a decimal is awkward to type there for the same reason. That is a
separate surface and a separate decision, not an oversight.
{ field: 'age', header: 'Age', editorType: 'number' }
// often paired with a display format:
{
field: 'salary', header: 'Salary',
editorType: 'number',
format: { type: 'currency', currency: 'USD', options: { maximumFractionDigits: 0 } },
}
Date editor - editorType: 'date'
<input type="date">. The value is stored as ISO YYYY-MM-DD.
{
field: 'joinedAt', header: 'Joined',
editorType: 'date',
format: { type: 'date', pattern: 'y-m-d' },
}
Datetime editor - editorType: 'datetime'
<input type="datetime-local">. The value is stored as ISO 8601 with a Z
suffix.
{
field: 'updatedAt', header: 'Updated',
editorType: 'datetime',
format: { type: 'datetime', pattern: 'medium' },
}
Checkbox editor - editorType: 'checkbox'
A themed button that toggles between true and false. Renders centred
in the cell.
{ field: 'active', header: 'Active', editorType: 'checkbox' }
The editor takes focus when it opens, so it works from the keyboard alone:
Space or Enter toggles and commits, Esc leaves the value as it was.
List editor - editorType: 'list'
Single-select dropdown. Accepts an array of strings/numbers or
{ value, label, color } objects. Set editorMultiple: true for a
multi-select.
{
field: 'priority', header: 'Priority',
editorType: 'list',
editorOptions: ['low', 'med', 'high', 'urgent'],
}
// labelled options:
{
field: 'status', header: 'Status',
editorType: 'list',
editorOptions: [
{ value: 'open', label: 'Open' },
{ value: 'in_progress', label: 'In progress' },
{ value: 'done', label: 'Done' },
],
}
Chips editor - editorType: 'chips'
Removable-token picker for multi-select. The value is always an array.
{
field: 'tags', header: 'Tags',
editorType: 'chips',
editorOptions: ['frontend', 'backend', 'design', 'infra', 'docs'],
}
Async option lists
Anywhere editorOptions is accepted it may also return a Promise, for
lists that live on the server. Both the static and the per-row form support it:
// One request for the whole column.
{ field: 'assignee', editorType: 'rich-select',
editorOptions: fetch('/api/users').then((r) => r.json()) }
// Per row - a cascade, where the list depends on another cell.
{ field: 'city', editorType: 'select',
editorOptions: (row) => fetch(`/api/cities?country=${row.country}`).then((r) => r.json()) }
While the request is in flight the dropdown shows Loading… rather than "No options", which would read as "nothing to pick". The cell keeps rendering its raw value.
Results are cached so reopening an editor never refetches. A static source is cached per column; a cascade is cached per row and per that row's data, so editing the cell it depends on supersedes the entry and the next open refetches
- change a row's Country and its City list reloads on its own.
When the list changes server-side rather than in the row, invalidate explicitly:
api.refreshEditorOptions('city') // one column
api.refreshEditorOptions() // everything
A rejected request settles the editor on an empty list, so a failed lookup never leaves it spinning.
Color editor - editorType: 'color'
Native HTML color picker. The cell stores a #rrggbb string. Clicking
the cell opens the OS color overlay; the value commits as soon as the
overlay closes (no need to blur the cell first).
{ field: 'brandColor', header: 'Brand', editorType: 'color' }
Complete example
A minimal grid where each row has its own swatch. The custom cell
snippet shows the colour next to the hex value while not editing; the
editor takes over on double-click.
<script lang="ts">
import {
SvGrid,
tableFeatures,
rowSortingFeature,
renderSnippet,
type ColumnDef,
} from '@svgrid/grid'
type Brand = { id: string; name: string; color: string }
let rows = $state<Brand[]>([
{ id: 'b1', name: 'Acme', color: '#6366f1' },
{ id: 'b2', name: 'Helios', color: '#10b981' },
{ id: 'b3', name: 'Crescent', color: '#f59e0b' },
])
const features = tableFeatures({ rowSortingFeature })
const columns: ColumnDef<typeof features, Brand>[] = [
{ field: 'name', header: 'Brand', editorType: 'text', width: 200 },
{
field: 'color', header: 'Color',
editorType: 'color',
width: 180,
cell: (ctx) => renderSnippet(Swatch, { row: ctx.row.original }),
},
]
</script>
{#snippet Swatch(props: { row: Brand })}
<span style="display: inline-flex; align-items: center; gap: 8px;">
<span style="width: 18px; height: 18px; border-radius: 4px; background: {props.row.color}; box-shadow: inset 0 0 0 1px rgba(0,0,0,0.18);"></span>
<code style="font-family: ui-monospace, Menlo, monospace; font-size: 11.5px;">{props.row.color}</code>
</span>
{/snippet}
<SvGrid
data={rows}
columns={columns}
features={features}
enableInlineEditing={true}
enableCellSelection={true}
containerHeight="100%"
/>
Theme tokens
The color editor inherits the grid's editor chrome. There are no extra tokens; the swatch fills the cell so the clickable area is the whole cell, not the OS default 25 × 13 swatch.
Rating editor - editorType: 'rating'
A row of 5 clickable stars plus a clear button. The cell stores an integer 0-5. Clicking a star commits immediately - no blur required.
{ field: 'csat', header: 'CSAT', editorType: 'rating' }
It is a radiogroup, so it also works from the keyboard: the star holding the
current value takes focus when the editor opens, the arrow keys (plus Home
and End) move across the five, Enter commits and Esc cancels.
Complete example
<script lang="ts">
import {
SvGrid,
tableFeatures,
rowSortingFeature,
renderSnippet,
type ColumnDef,
} from '@svgrid/grid'
type Review = { id: string; product: string; rating: number }
let rows = $state<Review[]>([
{ id: 'r1', product: 'Aurora keyboard', rating: 5 },
{ id: 'r2', product: 'Helios monitor', rating: 4 },
{ id: 'r3', product: 'Crescent mouse', rating: 2 },
])
const features = tableFeatures({ rowSortingFeature })
const columns: ColumnDef<typeof features, Review>[] = [
{ field: 'product', header: 'Product', editorType: 'text', width: 220 },
{
field: 'rating', header: 'Rating',
editorType: 'rating',
width: 160,
cell: (ctx) => renderSnippet(Stars, { row: ctx.row.original }),
},
]
</script>
{#snippet Stars(props: { row: Review })}
<span aria-label={`${props.row.rating} of 5`}>
{#each [1, 2, 3, 4, 5] as n (n)}
<span style="color: {props.row.rating >= n ? '#f59e0b' : '#cbd5e1'}; font-size: 16px;">★</span>
{/each}
</span>
{/snippet}
<SvGrid
data={rows}
columns={columns}
features={features}
enableInlineEditing={true}
enableCellSelection={true}
containerHeight="100%"
/>
Theme tokens
The rating editor reads three optional CSS custom properties so the star colors can match your design system:
| Token | Default | Used for |
|---|---|---|
--sg-rating-empty |
#cbd5e1 |
Unselected stars |
--sg-rating-on |
#f59e0b |
Selected stars |
--sg-rating-hover |
#fbbf24 |
Hover preview |
Override per theme:
[data-theme='dark'] {
--sg-rating-empty: #475569;
--sg-rating-on: #fbbf24;
}
Disabling an editor on a per-cell basis
editable accepts a callback. Return false to lock the cell - the
editor never opens, even if you double-click. This composes with all
the editor types above.
{
field: 'salary', header: 'Salary',
editorType: 'number',
// Only admins can edit salaries.
editable: (ctx) => currentUser.role === 'admin',
}
For declarative when-style rules across many columns, see the
Conditional form schema
recipe.
See also
- Cell data types
- Parsing values
- Validation
- Demo 80 - Cell types showcase - every editor in one enterprise grid
- Demo 66 - Custom cell editors - feature-health board using color, rating, and mood
Live examples
- List + chips editors - Two built-in editors with single & multi-select: dropdown (list) and removable tokens (chips), with options or free-form.
- Async editor options - editorOptions can return a Promise - for the whole column, or per row for a cascade. The dropdown shows Loading… while the request is in flight, results are cached (per column, or per column+row) so reopening never refetches, and api.refreshEditorOptions() invalidates when the server list changes.
- Cell types showcase - Every editor in one grid: color picker, date picker, 5-star rating, mood feedback, list/chips, number formatting, status badge.
- Custom cell editors - Three hand-rolled editors: native colour picker bound to a tag swatch, 5-star rating, emoji feedback mood. All write back through api.setCellValue.
Related articles
- Progress and Percentage Bar Cells in SvGrid - Build in-cell progress bars in your Svelte 5 data grid - with color thresholds, accessible markup, and sorting that still works.
- A Fill Handle (Drag to Fill) in SvGrid - Build a working spreadsheet-style fill handle on top of SvGrid's cell selection and editing - pointer tracking, range highlighting, series fill, and undo/redo integration all covered.
- An Editable Select / Dropdown Cell in SvGrid - Build a dropdown cell editor in SvGrid using a Svelte 5 snippet - constrain user input to a fixed option set while keeping sort, filter, and undo working on the underlying value.