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 theme8}: {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 getRowId={({ row }) => row.id}20 />21 );22};2324export default ValueFormatterDemo;
Angularvalue-formatter-demo.component.ts
Copy
1import { Component, Input } from "@angular/core";2import {SimpleTableComponent} from "@simple-table/angular";import type { AngularColumnDef, GetRowIdParams, Theme } from "@simple-table/angular";3import { valueFormatterConfig } from "./value-formatter.demo-data";4import "@simple-table/angular/styles.css";5import type { FormattedEmployee } from "./value-formatter.demo-data";67@Component({8 selector: "value-formatter-demo",9 standalone: true,10 imports: [SimpleTableComponent],11 template: `12 <simple-table13 [getRowId]="getRowId"14 [rows]="rows"15 [columns]="headers"16 [height]="height"17 [theme]="theme"18 [selectableCells]="selectableCellsProp"19 ></simple-table>20 `,21})22export class ValueFormatterDemoComponent {23 @Input() height: string | number = "400px";24 @Input() theme?: Theme;2526 readonly rows: FormattedEmployee[] = valueFormatterConfig.rows;27 readonly headers: AngularColumnDef<FormattedEmployee>[] = valueFormatterConfig.headers;28 readonly selectableCellsProp = valueFormatterConfig.tableProps.selectableCells;2930 getRowId = ({ row }: GetRowIdParams<FormattedEmployee>) => row.id;31}323334// value-formatter.demo-data.ts35// Self-contained demo table setup for this example.36import type { AngularColumnDef, ExportValueGetter, ValueFormatterProps } from "@simple-table/angular";3738export interface FormattedEmployee {39 id: number;40 firstName: string;41 lastName: string;42 salary: number;43 joinDate: string;44 performanceScore: number;45 balance: number;46 department: string;47}4849export const valueFormatterData: FormattedEmployee[] = [50 { id: 1, firstName: "Isabella", lastName: "Romano", salary: 125000, joinDate: "2021-03-15", performanceScore: 0.945, balance: 1250.50, department: "Engineering" },51 { id: 2, firstName: "Ethan", lastName: "McKenzie", salary: 98500, joinDate: "2022-07-22", performanceScore: 0.875, balance: -150.00, department: "Marketing" },52 { id: 3, firstName: "Zoe", lastName: "Patterson", salary: 110000, joinDate: "2020-01-10", performanceScore: 0.923, balance: 0, department: "Sales" },53 { id: 4, firstName: "Felix", lastName: "Chang", salary: 135000, joinDate: "2019-11-05", performanceScore: 0.967, balance: 3450.75, department: "Engineering" },54 { id: 5, firstName: "Aria", lastName: "Gonzalez", salary: 92000, joinDate: "2023-02-14", performanceScore: 0.834, balance: 780.25, department: "Design" },55 { id: 6, firstName: "Jasper", lastName: "Flynn", salary: 118000, joinDate: "2021-09-30", performanceScore: 0.891, balance: -425.50, department: "Product" },56 { id: 7, firstName: "Nova", lastName: "Sterling", salary: 105000, joinDate: "2022-04-18", performanceScore: 0.912, balance: 1850.00, department: "Marketing" },57 { id: 8, firstName: "Cruz", lastName: "Martinez", salary: 88500, joinDate: "2023-08-07", performanceScore: 0.798, balance: 245.75, department: "Operations" },58 { id: 9, firstName: "Sage", lastName: "Thompson", salary: 142000, joinDate: "2018-05-20", performanceScore: 0.978, balance: 5620.30, department: "Engineering" },59 { id: 10, firstName: "River", lastName: "Davis", salary: 95000, joinDate: "2022-11-12", performanceScore: 0.856, balance: 0, department: "Sales" },60 { id: 11, firstName: "Phoenix", lastName: "Williams", salary: 128000, joinDate: "2020-06-25", performanceScore: 0.934, balance: 2340.50, department: "Product" },61 { id: 12, firstName: "Atlas", lastName: "Johnson", salary: 102000, joinDate: "2023-01-09", performanceScore: 0.867, balance: -89.25, department: "Design" },62];6364const DEPARTMENT_CODES: Record<string, string> = {65 engineering: "ENG",66 marketing: "MKT",67 sales: "SLS",68 product: "PRD",69 design: "DSN",70 operations: "OPS",71};7273const performanceExportGetter: ExportValueGetter<FormattedEmployee, number> = ({ value }) =>74 `${Math.round(value * 100)}%`;7576const departmentExportGetter: ExportValueGetter<FormattedEmployee, string> = ({ value }) => {77 const str = value.toLowerCase();78 const code = DEPARTMENT_CODES[str] || "OTH";79 return `${value.toUpperCase()} (${code})`;80};8182export const valueFormatterHeaders: AngularColumnDef<FormattedEmployee, any>[] = [83 { accessor: "id", label: "ID", width: 60, type: "number" },84 {85 accessor: "firstName",86 label: "Name",87 width: 180,88 type: "string",89 valueFormatter: ({ value, row }: ValueFormatterProps<FormattedEmployee, string>) => {90 return `${value} ${row.lastName}`;91 },92 },93 {94 accessor: "salary",95 label: "Salary",96 width: 140,97 type: "number",98 valueFormatter: ({ value }: ValueFormatterProps<FormattedEmployee, number>) => {99 return `$${value.toLocaleString("en-US", {100 minimumFractionDigits: 2,101 maximumFractionDigits: 2,102 })}`;103 },104 useFormattedValueForClipboard: true,105 useFormattedValueForCSV: true,106 },107 {108 accessor: "joinDate",109 label: "Join Date",110 width: 140,111 type: "date",112 valueFormatter: ({ value }: ValueFormatterProps<FormattedEmployee, string>) => {113 const date = new Date(value);114 return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });115 },116 },117 {118 accessor: "performanceScore",119 label: "Performance",120 width: 130,121 type: "number",122 valueFormatter: ({ value }: ValueFormatterProps<FormattedEmployee, number>) => `${(value * 100).toFixed(1)}%`,123 useFormattedValueForClipboard: true,124 exportValueGetter: performanceExportGetter,125 },126 {127 accessor: "balance",128 label: "Balance",129 width: 120,130 type: "number",131 valueFormatter: ({ value }: ValueFormatterProps<FormattedEmployee, number>) => {132 if (value === 0) return "\u2014";133 if (value < 0) return `($${Math.abs(value).toFixed(2)})`;134 return `$${value.toFixed(2)}`;135 },136 },137 {138 accessor: "department",139 label: "Department",140 width: 150,141 type: "string",142 valueFormatter: ({ value }: ValueFormatterProps<FormattedEmployee, string>) => value.toUpperCase(),143 exportValueGetter: departmentExportGetter,144 },145];146147export const valueFormatterConfig = {148 headers: valueFormatterHeaders,149 rows: valueFormatterData,150 tableProps: {151 selectableCells: true,152 },153};154
Vue SFC
Copy
1<template>2 <SimpleTable3 :columns="valueFormatterConfig.headers"4 :rows="valueFormatterConfig.rows"5 :get-row-id="getRowId"6 :height="height"7 :theme="theme"8 :selectable-cells="valueFormatterConfig.tableProps.selectableCells"9 />10</template>1112<script setup lang="ts">13import { SimpleTable } from "@simple-table/vue";14import type { Theme, GetRowIdParams } from "@simple-table/vue";15import { valueFormatterConfig } from "./value-formatter.demo-data";16import type { FormattedEmployee } from "./value-formatter.demo-data";17import "@simple-table/vue/styles.css";1819withDefaults(defineProps<{ height?: string | number; theme?: Theme }>(), {20 height: "400px",21});2223const getRowId = ({ row }: GetRowIdParams<FormattedEmployee>) => row.id;24</script>
Svelte
Copy
1<script lang="ts">2 import { SimpleTable } from "@simple-table/svelte";3 import type { Theme, GetRowIdParams } from "@simple-table/svelte";4 import { valueFormatterConfig } from "./value-formatter.demo-data";5 import type { FormattedEmployee } from "./value-formatter.demo-data";6 import "@simple-table/svelte/styles.css";78 let { height = "400px", theme }: { height?: string | number; theme?: Theme } = $props();910 const getRowId = ({ row }: GetRowIdParams<FormattedEmployee>) => row.id;11</script>1213<SimpleTable14 columns={valueFormatterConfig.headers}15 rows={valueFormatterConfig.rows}16 getRowId={getRowId}17 {height}18 {theme}19 selectableCells={valueFormatterConfig.tableProps.selectableCells}20/>
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 getRowId={({ row }) => row.id}13 rows={valueFormatterConfig.rows}14 height={props.height ?? "400px"}15 theme={props.theme}16 selectableCells={valueFormatterConfig.tableProps.selectableCells}17 />18 );19}
TypeScriptValueFormatterDemo.ts
Copy
1import { SimpleTableVanilla } from "simple-table-core";2import type { FormattedEmployee } from "./value-formatter.demo-data";3import type { Theme, GetRowIdParams } from "simple-table-core";4import { valueFormatterConfig } from "./value-formatter.demo-data";5import "simple-table-core/styles.css";678const getRowId = ({ row }: GetRowIdParams<FormattedEmployee>) => row.id;9export function renderValueFormatterDemo(10 container: HTMLElement,11 options?: { height?: string | number; theme?: Theme }12): SimpleTableVanilla<FormattedEmployee> {13 const table = new SimpleTableVanilla(container, {14 getRowId,15 columns: valueFormatterConfig.headers,16 rows: valueFormatterConfig.rows,17 height: options?.height ?? "400px",18 theme: options?.theme,19 selectableCells: valueFormatterConfig.tableProps.selectableCells,20 });21 return table;22}232425// value-formatter.demo-data.ts26// Self-contained demo table setup for this example.27import type { ColumnDef } from "simple-table-core";2829export interface FormattedEmployee {30 id: number;31 firstName: string;32 lastName: string;33 salary: number;34 joinDate: string;35 performanceScore: number;36 balance: number;37 department: string;38}3940export const valueFormatterData: FormattedEmployee[] = [41 { id: 1, firstName: "Isabella", lastName: "Romano", salary: 125000, joinDate: "2021-03-15", performanceScore: 0.945, balance: 1250.50, department: "Engineering" },42 { id: 2, firstName: "Ethan", lastName: "McKenzie", salary: 98500, joinDate: "2022-07-22", performanceScore: 0.875, balance: -150.00, department: "Marketing" },43 { id: 3, firstName: "Zoe", lastName: "Patterson", salary: 110000, joinDate: "2020-01-10", performanceScore: 0.923, balance: 0, department: "Sales" },44 { id: 4, firstName: "Felix", lastName: "Chang", salary: 135000, joinDate: "2019-11-05", performanceScore: 0.967, balance: 3450.75, department: "Engineering" },45 { id: 5, firstName: "Aria", lastName: "Gonzalez", salary: 92000, joinDate: "2023-02-14", performanceScore: 0.834, balance: 780.25, department: "Design" },46 { id: 6, firstName: "Jasper", lastName: "Flynn", salary: 118000, joinDate: "2021-09-30", performanceScore: 0.891, balance: -425.50, department: "Product" },47 { id: 7, firstName: "Nova", lastName: "Sterling", salary: 105000, joinDate: "2022-04-18", performanceScore: 0.912, balance: 1850.00, department: "Marketing" },48 { id: 8, firstName: "Cruz", lastName: "Martinez", salary: 88500, joinDate: "2023-08-07", performanceScore: 0.798, balance: 245.75, department: "Operations" },49 { id: 9, firstName: "Sage", lastName: "Thompson", salary: 142000, joinDate: "2018-05-20", performanceScore: 0.978, balance: 5620.30, department: "Engineering" },50 { id: 10, firstName: "River", lastName: "Davis", salary: 95000, joinDate: "2022-11-12", performanceScore: 0.856, balance: 0, department: "Sales" },51 { id: 11, firstName: "Phoenix", lastName: "Williams", salary: 128000, joinDate: "2020-06-25", performanceScore: 0.934, balance: 2340.50, department: "Product" },52 { id: 12, firstName: "Atlas", lastName: "Johnson", salary: 102000, joinDate: "2023-01-09", performanceScore: 0.867, balance: -89.25, department: "Design" },53];5455const DEPARTMENT_CODES: Record<string, string> = {56 engineering: "ENG",57 marketing: "MKT",58 sales: "SLS",59 product: "PRD",60 design: "DSN",61 operations: "OPS",62};6364export const valueFormatterHeaders: ColumnDef<FormattedEmployee>[] = [65 { accessor: "id", label: "ID", width: 60, type: "number" },66 {67 accessor: "firstName",68 label: "Name",69 width: 180,70 type: "string",71 valueFormatter: ({ value, row }) => `${String(value)} ${row.lastName}`,72 },73 {74 accessor: "salary",75 label: "Salary",76 width: 140,77 type: "number",78 valueFormatter: ({ value }) =>79 `$${Number(value).toLocaleString("en-US", {80 minimumFractionDigits: 2,81 maximumFractionDigits: 2,82 })}`,83 useFormattedValueForClipboard: true,84 useFormattedValueForCSV: true,85 },86 {87 accessor: "joinDate",88 label: "Join Date",89 width: 140,90 type: "date",91 valueFormatter: ({ value }) => {92 const date = new Date(String(value));93 return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });94 },95 },96 {97 accessor: "performanceScore",98 label: "Performance",99 width: 130,100 type: "number",101 valueFormatter: ({ value }) => `${(Number(value) * 100).toFixed(1)}%`,102 useFormattedValueForClipboard: true,103 exportValueGetter: ({ value }) => `${Math.round(Number(value) * 100)}%`,104 },105 {106 accessor: "balance",107 label: "Balance",108 width: 120,109 type: "number",110 valueFormatter: ({ value }) => {111 const balance = Number(value);112 if (balance === 0) return "\u2014";113 if (balance < 0) return `($${Math.abs(balance).toFixed(2)})`;114 return `$${balance.toFixed(2)}`;115 },116 },117 {118 accessor: "department",119 label: "Department",120 width: 150,121 type: "string",122 valueFormatter: ({ value }) => String(value).toUpperCase(),123 exportValueGetter: ({ value }) => {124 const str = String(value).toLowerCase();125 const code = DEPARTMENT_CODES[str] || "OTH";126 return `${String(value).toUpperCase()} (${code})`;127 },128 },129];130131export const valueFormatterConfig = {132 headers: valueFormatterHeaders,133 rows: valueFormatterData,134 tableProps: {135 selectableCells: true,136 },137};138
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 |