Cell formatters
A cell formatter changes how matching values look in Flare's tables. Show a URL as a link, a date as "3 hours ago", an id in monospace or a status in green. You return plain data, and Flare draws it in the grid's own style.
Formatters apply wherever Flare shows rows: SQL tables, Redis keys, query results and the Firestore workspace.
Choose which cells to format
Each formatter's manifest entry has a match:
{
"contributes": {
"cells": [
{
"id": "links",
"title": "Links",
"description": "https addresses in fields ending in _url, as links.",
"match": { "fields": ["*_url", "website"], "types": ["string"] }
},
{
"id": "ids",
"title": "Monospaced ids",
"match": { "types": ["uuid", "objectId"] }
},
{
"id": "json",
"title": "JSON summaries",
"match": { "columnTypes": ["json*"] }
}
]
}
}| Selector | Matches |
|---|---|
fields | The field or column name, such as email or *_url. |
types | The kind of value, using Flare's names that are the same on every engine: string, timestamp, objectId and so on. See Value types. |
columnTypes | The column type the database declares, such as uuid, json* or Cassandra's timeuuid. Only the SQL databases, Supabase and Cassandra declare column types. |
* matches any run of characters. Matching ignores case. A formatter needs at least one selector. When you give more than one, a cell must match all of them. The links formatter above only takes strings in a field that ends in _url or is called website.
Value type or column type?
Use types when you care what the value is on any engine. A timestamp covers SQL's timestamptz, Firestore's Timestamp and a MongoDB Date. Use columnTypes when you need the database's own type, such as jsonb, which the value alone can't tell you.
Write the formatter
A formatter takes a Cell and returns a CellDisplay, or null to leave the cell alone:
import { definePlugin, type Cell, type CellDisplay } from '@flare/plugin';
/** Only https links: Flare opens nothing else from a plugin. */
function links({ value }: Cell): CellDisplay | null {
if (typeof value !== 'string' || !/^https:\/\//i.test(value)) return null;
const url = new URL(value);
return {
text: url.host + url.pathname,
href: url.href,
icon: 'external-link',
title: url.href,
};
}
/** Long ids, shortened, in the monospace face, whole on hover. */
function ids({ value }: Cell): CellDisplay | null {
if (typeof value !== 'string') return null;
const short = value.length > 13 ? `${value.slice(0, 8)}…${value.slice(-4)}` : value;
return { text: short, mono: true, title: value };
}
export default definePlugin({
cells: { links, ids },
});The cell
A Cell has the value, its field, its value type and its container. columnType is the type the database declares, or null on Firestore and MongoDB. record is the whole row, for formatters that need more than the value:
definePlugin({
cells: {
amount: ({ value, record }) => ({
text: new Intl.NumberFormat(undefined, {
style: 'currency',
currency: String(record.values.currency ?? 'EUR'),
}).format(Number(value)),
tone: Number(value) < 0 ? 'danger' : 'neutral',
}),
},
});What to return
| Field | What it does |
|---|---|
text | The text in the cell. Required. |
tone | neutral, positive, warning, danger or info. Colours the text. |
icon | A Lucide icon name in kebab case, shown before the text. |
title | Hover text, such as the full value behind a shortened one. |
href | An https link. Flare asks the user before opening it in the browser. |
mono | true sets the text in monospace, for ids, hashes and codes. |
The full types are in definePlugin.
Keep it fast
Flare calls formatters in batches as the grid draws, and caches the results by value and field.
- Formatters don't get
ctx. They can't read data, fetch or store anything. If you need more data, use a computed column. - Batches have a short time limit. If a batch is too slow or a formatter throws, those cells show as normal. The plugin's log gets one entry, not one per cell.
- Return
nullfor any value you don't handle.
Flare draws every CellDisplay itself, so there's no way to put markup, styles or scripts in a cell.