Documentation
Value Formatter
Format cell text for display — currency, dates, percentages — without changing the data.
Format display values
Set valueFormatter on a column. It formats for display only — the underlying data stays unchanged. For custom UI, use cellRenderer.
TypeScript
Copy
{accessor: "salary",label: "Salary",type: "number",valueFormatter: ({ value }) =>typeof value === "number" ? `$${value.toLocaleString()}` : "",}
TypeScript
Copy
{accessor: "salary",label: "Salary",type: "number",valueFormatter: ({ value }) =>typeof value === "number" ? `$${value.toLocaleString()}` : "",}
TypeScript
Copy
{accessor: "salary",label: "Salary",type: "number",valueFormatter: ({ value }) =>typeof value === "number" ? `$${value.toLocaleString()}` : "",}
TypeScript
Copy
{accessor: "salary",label: "Salary",type: "number",valueFormatter: ({ value }) =>typeof value === "number" ? `$${value.toLocaleString()}` : "",}
TypeScript
Copy
{accessor: "salary",label: "Salary",type: "number",valueFormatter: ({ value }) =>typeof value === "number" ? `$${value.toLocaleString()}` : "",}
TypeScript
Copy
{accessor: "salary",label: "Salary",type: "number",valueFormatter: ({ value }) =>typeof value === "number" ? `$${value.toLocaleString()}` : "",}
Use other row fields
Read row when the display string needs more than the current cell.
TypeScript
Copy
{accessor: "firstName",label: "Name",valueFormatter: ({ value, row }) => `${value} ${row.lastName ?? ""}`.trim(),}
TypeScript
Copy
{accessor: "firstName",label: "Name",valueFormatter: ({ value, row }) => `${value} ${row.lastName ?? ""}`.trim(),}
TypeScript
Copy
{accessor: "firstName",label: "Name",valueFormatter: ({ value, row }) => `${value} ${row.lastName ?? ""}`.trim(),}
TypeScript
Copy
{accessor: "firstName",label: "Name",valueFormatter: ({ value, row }) => `${value} ${row.lastName ?? ""}`.trim(),}
TypeScript
Copy
{accessor: "firstName",label: "Name",valueFormatter: ({ value, row }) => `${value} ${row.lastName ?? ""}`.trim(),}
TypeScript
Copy
{accessor: "firstName",label: "Name",valueFormatter: ({ value, row }) => `${value} ${row.lastName ?? ""}`.trim(),}
Copy and CSV
With a valueFormatter, copy and CSV use the formatted value by default. Set the flags to false for raw values, or use exportValueGetter for a CSV-only override.
TypeScript
Copy
{accessor: "salary",label: "Salary",type: "number",valueFormatter: ({ value }) =>typeof value === "number" ? `$${value.toLocaleString()}` : "",// useFormattedValueForClipboard: false, // copy raw number// useFormattedValueForCSV: false, // export raw numberexportValueGetter: ({ value }) =>typeof value === "number" ? value.toFixed(2) : "",}
TypeScript
Copy
{accessor: "salary",label: "Salary",type: "number",valueFormatter: ({ value }) =>typeof value === "number" ? `$${value.toLocaleString()}` : "",// useFormattedValueForClipboard: false, // copy raw number// useFormattedValueForCSV: false, // export raw numberexportValueGetter: ({ value }) =>typeof value === "number" ? value.toFixed(2) : "",}
TypeScript
Copy
{accessor: "salary",label: "Salary",type: "number",valueFormatter: ({ value }) =>typeof value === "number" ? `$${value.toLocaleString()}` : "",// useFormattedValueForClipboard: false, // copy raw number// useFormattedValueForCSV: false, // export raw numberexportValueGetter: ({ value }) =>typeof value === "number" ? value.toFixed(2) : "",}
TypeScript
Copy
{accessor: "salary",label: "Salary",type: "number",valueFormatter: ({ value }) =>typeof value === "number" ? `$${value.toLocaleString()}` : "",// useFormattedValueForClipboard: false, // copy raw number// useFormattedValueForCSV: false, // export raw numberexportValueGetter: ({ value }) =>typeof value === "number" ? value.toFixed(2) : "",}
TypeScript
Copy
{accessor: "salary",label: "Salary",type: "number",valueFormatter: ({ value }) =>typeof value === "number" ? `$${value.toLocaleString()}` : "",// useFormattedValueForClipboard: false, // copy raw number// useFormattedValueForCSV: false, // export raw numberexportValueGetter: ({ value }) =>typeof value === "number" ? value.toFixed(2) : "",}
TypeScript
Copy
{accessor: "salary",label: "Salary",type: "number",valueFormatter: ({ value }) =>typeof value === "number" ? `$${value.toLocaleString()}` : "",// useFormattedValueForClipboard: false, // copy raw number// useFormattedValueForCSV: false, // export raw numberexportValueGetter: ({ value }) =>typeof value === "number" ? value.toFixed(2) : "",}
Example
Currency, dates, percentages, and combined fields. Use Code or StackBlitz for the full example.
React TSX
Copy
1import {SimpleTable} from "@simple-table/react";import type { Theme } from "@simple-table/react";2import { valueFormatterConfig } from "./value-formatter.demo-data";3import "@simple-table/react/styles.css";45const ValueFormatterDemo = ({6 height = "400px",7 theme,8}: {9 height?: string | number;10 theme?: Theme;11}) => {12 return (13 <SimpleTable14 columns={valueFormatterConfig.headers}15 rows={valueFormatterConfig.rows}16 height={height}17 theme={theme}18 selectableCells={valueFormatterConfig.tableProps.selectableCells}19 />20 );21};2223export default ValueFormatterDemo;
Vue SFC
Copy
1<template>2 <SimpleTable3 :columns="valueFormatterConfig.headers"4 :rows="valueFormatterConfig.rows"5 :height="height"6 :theme="theme"7 :selectable-cells="valueFormatterConfig.tableProps.selectableCells"8 />9</template>1011<script setup lang="ts">12import {SimpleTable} from "@simple-table/vue";import type { Theme } from "@simple-table/vue";13import { valueFormatterConfig } from "./value-formatter.demo-data";14import "@simple-table/vue/styles.css";1516withDefaults(defineProps<{ height?: string | number; theme?: Theme }>(), {17 height: "400px",18});19</script>
Angularvalue-formatter-demo.component.ts
Copy
1import { Component, Input } from "@angular/core";2import {SimpleTableComponent} from "@simple-table/angular";import type { AngularColumnDef, Row, Theme } from "@simple-table/angular";3import { valueFormatterConfig } from "./value-formatter.demo-data";4import "@simple-table/angular/styles.css";56@Component({7 selector: "value-formatter-demo",8 standalone: true,9 imports: [SimpleTableComponent],10 template: `11 <simple-table12 [rows]="rows"13 [columns]="headers"14 [height]="height"15 [theme]="theme"16 [selectableCells]="selectableCellsProp"17 ></simple-table>18 `,19})20export class ValueFormatterDemoComponent {21 @Input() height: string | number = "400px";22 @Input() theme?: Theme;2324 readonly rows: Row[] = valueFormatterConfig.rows;25 readonly headers: AngularColumnDef[] = valueFormatterConfig.headers;26 readonly selectableCellsProp = valueFormatterConfig.tableProps.selectableCells;27}282930// value-formatter.demo-data.ts31// Self-contained demo table setup for this example.32import type { AngularColumnDef, Row } from "@simple-table/angular";333435export const valueFormatterData: Row[] = [36 { id: 1, firstName: "Isabella", lastName: "Romano", salary: 125000, joinDate: "2021-03-15", performanceScore: 0.945, balance: 1250.50, department: "Engineering" },37 { id: 2, firstName: "Ethan", lastName: "McKenzie", salary: 98500, joinDate: "2022-07-22", performanceScore: 0.875, balance: -150.00, department: "Marketing" },38 { id: 3, firstName: "Zoe", lastName: "Patterson", salary: 110000, joinDate: "2020-01-10", performanceScore: 0.923, balance: 0, department: "Sales" },39 { id: 4, firstName: "Felix", lastName: "Chang", salary: 135000, joinDate: "2019-11-05", performanceScore: 0.967, balance: 3450.75, department: "Engineering" },40 { id: 5, firstName: "Aria", lastName: "Gonzalez", salary: 92000, joinDate: "2023-02-14", performanceScore: 0.834, balance: 780.25, department: "Design" },41 { id: 6, firstName: "Jasper", lastName: "Flynn", salary: 118000, joinDate: "2021-09-30", performanceScore: 0.891, balance: -425.50, department: "Product" },42 { id: 7, firstName: "Nova", lastName: "Sterling", salary: 105000, joinDate: "2022-04-18", performanceScore: 0.912, balance: 1850.00, department: "Marketing" },43 { id: 8, firstName: "Cruz", lastName: "Martinez", salary: 88500, joinDate: "2023-08-07", performanceScore: 0.798, balance: 245.75, department: "Operations" },44 { id: 9, firstName: "Sage", lastName: "Thompson", salary: 142000, joinDate: "2018-05-20", performanceScore: 0.978, balance: 5620.30, department: "Engineering" },45 { id: 10, firstName: "River", lastName: "Davis", salary: 95000, joinDate: "2022-11-12", performanceScore: 0.856, balance: 0, department: "Sales" },46 { id: 11, firstName: "Phoenix", lastName: "Williams", salary: 128000, joinDate: "2020-06-25", performanceScore: 0.934, balance: 2340.50, department: "Product" },47 { id: 12, firstName: "Atlas", lastName: "Johnson", salary: 102000, joinDate: "2023-01-09", performanceScore: 0.867, balance: -89.25, department: "Design" },48];4950const DEPARTMENT_CODES: Record<string, string> = {51 engineering: "ENG",52 marketing: "MKT",53 sales: "SLS",54 product: "PRD",55 design: "DSN",56 operations: "OPS",57};5859export const valueFormatterHeaders: AngularColumnDef[] = [60 { accessor: "id", label: "ID", width: 60, type: "number" },61 {62 accessor: "firstName",63 label: "Name",64 width: 180,65 type: "string",66 valueFormatter: ({ value, row }) => {67 return `${value as string} ${row.lastName as string}`;68 },69 },70 {71 accessor: "salary",72 label: "Salary",73 width: 140,74 type: "number",75 valueFormatter: ({ value }) => {76 return `$${(value as number).toLocaleString("en-US", {77 minimumFractionDigits: 2,78 maximumFractionDigits: 2,79 })}`;80 },81 useFormattedValueForClipboard: true,82 useFormattedValueForCSV: true,83 },84 {85 accessor: "joinDate",86 label: "Join Date",87 width: 140,88 type: "date",89 valueFormatter: ({ value }) => {90 const date = new Date(value as string);91 return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });92 },93 },94 {95 accessor: "performanceScore",96 label: "Performance",97 width: 130,98 type: "number",99 valueFormatter: ({ value }) => `${((value as number) * 100).toFixed(1)}%`,100 useFormattedValueForClipboard: true,101 exportValueGetter: ({ value }) => `${Math.round((value as number) * 100)}%`,102 },103 {104 accessor: "balance",105 label: "Balance",106 width: 120,107 type: "number",108 valueFormatter: ({ value }) => {109 const balance = value as number;110 if (balance === 0) return "\u2014";111 if (balance < 0) return `($${Math.abs(balance).toFixed(2)})`;112 return `$${balance.toFixed(2)}`;113 },114 },115 {116 accessor: "department",117 label: "Department",118 width: 150,119 type: "string",120 valueFormatter: ({ value }) => (value as string).toUpperCase(),121 exportValueGetter: ({ value }) => {122 const str = (value as string).toLowerCase();123 const code = DEPARTMENT_CODES[str] || "OTH";124 return `${(value as string).toUpperCase()} (${code})`;125 },126 },127];128129export const valueFormatterConfig = {130 headers: valueFormatterHeaders,131 rows: valueFormatterData,132 tableProps: {133 selectableCells: true,134 },135} as const;136
Svelte
Copy
1<script lang="ts">2 import {SimpleTable} from "@simple-table/svelte"; import type { Theme } from "@simple-table/svelte";3 import { valueFormatterConfig } from "./value-formatter.demo-data";4 import "@simple-table/svelte/styles.css";56 let { height = "400px", theme }: { height?: string | number; theme?: Theme } = $props();7</script>89<SimpleTable10 columns={valueFormatterConfig.headers}11 rows={valueFormatterConfig.rows}12 {height}13 {theme}14 selectableCells={valueFormatterConfig.tableProps.selectableCells}15/>
Solid TSX
Copy
1import {SimpleTable} from "@simple-table/solid";import type { Theme } from "@simple-table/solid";2import { valueFormatterConfig } from "./value-formatter.demo-data";3import "@simple-table/solid/styles.css";45export default function ValueFormatterDemo(props: {6 height?: string | number;7 theme?: Theme;8}) {9 return (10 <SimpleTable11 columns={valueFormatterConfig.headers}12 rows={valueFormatterConfig.rows}13 height={props.height ?? "400px"}14 theme={props.theme}15 selectableCells={valueFormatterConfig.tableProps.selectableCells}16 />17 );18}
TypeScriptValueFormatterDemo.ts
Copy
1import { SimpleTableVanilla } from "simple-table-core";2import type { Theme } from "simple-table-core";3import { valueFormatterConfig } from "./value-formatter.demo-data";4import "simple-table-core/styles.css";56export function renderValueFormatterDemo(7 container: HTMLElement,8 options?: { height?: string | number; theme?: Theme }9): SimpleTableVanilla {10 const table = new SimpleTableVanilla(container, {11 columns: valueFormatterConfig.headers,12 rows: valueFormatterConfig.rows,13 height: options?.height ?? "400px",14 theme: options?.theme,15 selectableCells: valueFormatterConfig.tableProps.selectableCells,16 });17 return table;18}192021// value-formatter.demo-data.ts22// Self-contained demo table setup for this example.23import type { ColumnDef, Row } from "simple-table-core";242526export const valueFormatterData: Row[] = [27 { id: 1, firstName: "Isabella", lastName: "Romano", salary: 125000, joinDate: "2021-03-15", performanceScore: 0.945, balance: 1250.50, department: "Engineering" },28 { id: 2, firstName: "Ethan", lastName: "McKenzie", salary: 98500, joinDate: "2022-07-22", performanceScore: 0.875, balance: -150.00, department: "Marketing" },29 { id: 3, firstName: "Zoe", lastName: "Patterson", salary: 110000, joinDate: "2020-01-10", performanceScore: 0.923, balance: 0, department: "Sales" },30 { id: 4, firstName: "Felix", lastName: "Chang", salary: 135000, joinDate: "2019-11-05", performanceScore: 0.967, balance: 3450.75, department: "Engineering" },31 { id: 5, firstName: "Aria", lastName: "Gonzalez", salary: 92000, joinDate: "2023-02-14", performanceScore: 0.834, balance: 780.25, department: "Design" },32 { id: 6, firstName: "Jasper", lastName: "Flynn", salary: 118000, joinDate: "2021-09-30", performanceScore: 0.891, balance: -425.50, department: "Product" },33 { id: 7, firstName: "Nova", lastName: "Sterling", salary: 105000, joinDate: "2022-04-18", performanceScore: 0.912, balance: 1850.00, department: "Marketing" },34 { id: 8, firstName: "Cruz", lastName: "Martinez", salary: 88500, joinDate: "2023-08-07", performanceScore: 0.798, balance: 245.75, department: "Operations" },35 { id: 9, firstName: "Sage", lastName: "Thompson", salary: 142000, joinDate: "2018-05-20", performanceScore: 0.978, balance: 5620.30, department: "Engineering" },36 { id: 10, firstName: "River", lastName: "Davis", salary: 95000, joinDate: "2022-11-12", performanceScore: 0.856, balance: 0, department: "Sales" },37 { id: 11, firstName: "Phoenix", lastName: "Williams", salary: 128000, joinDate: "2020-06-25", performanceScore: 0.934, balance: 2340.50, department: "Product" },38 { id: 12, firstName: "Atlas", lastName: "Johnson", salary: 102000, joinDate: "2023-01-09", performanceScore: 0.867, balance: -89.25, department: "Design" },39];4041const DEPARTMENT_CODES: Record<string, string> = {42 engineering: "ENG",43 marketing: "MKT",44 sales: "SLS",45 product: "PRD",46 design: "DSN",47 operations: "OPS",48};4950export const valueFormatterHeaders: ColumnDef[] = [51 { accessor: "id", label: "ID", width: 60, type: "number" },52 {53 accessor: "firstName",54 label: "Name",55 width: 180,56 type: "string",57 valueFormatter: ({ value, row }) => {58 return `${value as string} ${row.lastName as string}`;59 },60 },61 {62 accessor: "salary",63 label: "Salary",64 width: 140,65 type: "number",66 valueFormatter: ({ value }) => {67 return `$${(value as number).toLocaleString("en-US", {68 minimumFractionDigits: 2,69 maximumFractionDigits: 2,70 })}`;71 },72 useFormattedValueForClipboard: true,73 useFormattedValueForCSV: true,74 },75 {76 accessor: "joinDate",77 label: "Join Date",78 width: 140,79 type: "date",80 valueFormatter: ({ value }) => {81 const date = new Date(value as string);82 return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });83 },84 },85 {86 accessor: "performanceScore",87 label: "Performance",88 width: 130,89 type: "number",90 valueFormatter: ({ value }) => `${((value as number) * 100).toFixed(1)}%`,91 useFormattedValueForClipboard: true,92 exportValueGetter: ({ value }) => `${Math.round((value as number) * 100)}%`,93 },94 {95 accessor: "balance",96 label: "Balance",97 width: 120,98 type: "number",99 valueFormatter: ({ value }) => {100 const balance = value as number;101 if (balance === 0) return "\u2014";102 if (balance < 0) return `($${Math.abs(balance).toFixed(2)})`;103 return `$${balance.toFixed(2)}`;104 },105 },106 {107 accessor: "department",108 label: "Department",109 width: 150,110 type: "string",111 valueFormatter: ({ value }) => (value as string).toUpperCase(),112 exportValueGetter: ({ value }) => {113 const str = (value as string).toLowerCase();114 const code = DEPARTMENT_CODES[str] || "OTH";115 return `${(value as string).toUpperCase()} (${code})`;116 },117 },118];119120export const valueFormatterConfig = {121 headers: valueFormatterHeaders,122 rows: valueFormatterData,123 tableProps: {124 selectableCells: true,125 },126} as const;127
Props
Value Formatter Configuration
| Property | Required | Description | Example |
|---|---|---|---|
Property | Required | Description | Example |
ColumnDef.valueFormatter | Optional | Formats the cell for display without changing the underlying data. Returns a string, number, or array of those. | |
ColumnDef.useFormattedValueForClipboardboolean | Optional | When a valueFormatter exists, defaults to true (formatted on copy). Set false to copy the raw value. | |
ColumnDef.useFormattedValueForCSVboolean | Optional | When a valueFormatter exists, defaults to true (formatted in CSV). Set false for raw values. Ignored if exportValueGetter is set. | |
ColumnDef.exportValueGetter | Optional | Custom CSV export value. Takes precedence over useFormattedValueForCSV. |
Formatter arguments
ValueFormatterProps
| Property | Required | Description | Example |
|---|---|---|---|
Property | Required | Description | Example |
accessor | Required | The column accessor/key for the cell being formatted | |
colIndexnumber | Required | The column index (0-based) | |
row | Required | The complete row object containing all data for this row | |
rowIndexnumber | Required | The row index (0-based) | |
value | Required | The raw cell value to be formatted |