Formatting Presets
Formatting presets help Pivot reports show numbers, money, percentages, and dates in a readable way. Conditional formatting presets then help users spot high revenue, low margin, or other important analytical values.
Source code
import { defineCustomElements } from '@revolist/revogrid/loader';
defineCustomElements();
import type { ColumnType, ColumnTypes, DataType } from '@revolist/revogrid';
import {
createPivotConditionalCellProperties,
createPivotValueFormatter,
formatPivotValue,
PivotPlugin,
pivotConditionalFormattingPresets,
type PivotConfig,
} from '@revolist/revogrid-enterprise';
import { AdvanceFilterPlugin, RowOddPlugin } from '@revolist/revogrid-pro';
import { currentTheme } from '../composables/useRandomData';
export interface PivotFormattingPresetRow {
region: string;
closeDate: string;
segment: string;
revenue: number;
marginRate: number;
orders: number;
}
const currencyFormatter = createPivotValueFormatter({
preset: 'currency',
currency: 'USD',
locale: 'en-US',
options: { maximumFractionDigits: 0 },
});
const percentFormatter = createPivotValueFormatter({
preset: 'percent',
locale: 'en-US',
options: { minimumFractionDigits: 1, maximumFractionDigits: 1 },
});
const dateFormatter = createPivotValueFormatter({
preset: 'date',
locale: 'en-US',
options: { month: 'short', day: '2-digit', year: 'numeric' },
});
const integerFormatter = createPivotValueFormatter({
preset: 'number',
locale: 'en-US',
options: { maximumFractionDigits: 0 },
});
function formattedColumn(formatter: (value: unknown) => string): ColumnType {
return {
cellTemplate: (_h, props) => formatter(props.value),
cellProperties: () => ({ class: { 'align-right': true } }),
};
}
export const PIVOT_FORMATTING_PRESETS_ROWS: PivotFormattingPresetRow[] = [
{ region: 'North America', closeDate: '2026-01-16', segment: 'Software', revenue: 184000, marginRate: 0.37, orders: 32 },
{ region: 'North America', closeDate: '2026-01-22', segment: 'Services', revenue: 126000, marginRate: 0.31, orders: 27 },
{ region: 'North America', closeDate: '2026-02-10', segment: 'Software', revenue: 211000, marginRate: 0.41, orders: 35 },
{ region: 'North America', closeDate: '2026-02-18', segment: 'Hardware', revenue: 94000, marginRate: 0.22, orders: 18 },
{ region: 'EMEA', closeDate: '2026-01-19', segment: 'Software', revenue: 156000, marginRate: 0.34, orders: 29 },
{ region: 'EMEA', closeDate: '2026-02-06', segment: 'Services', revenue: 118000, marginRate: 0.28, orders: 24 },
{ region: 'EMEA', closeDate: '2026-02-24', segment: 'Hardware', revenue: 87000, marginRate: 0.19, orders: 16 },
{ region: 'APAC', closeDate: '2026-01-28', segment: 'Software', revenue: 142000, marginRate: 0.36, orders: 26 },
{ region: 'APAC', closeDate: '2026-02-12', segment: 'Services', revenue: 132000, marginRate: 0.33, orders: 31 },
{ region: 'APAC', closeDate: '2026-02-26', segment: 'Hardware', revenue: 76000, marginRate: 0.21, orders: 15 },
];
export const PIVOT_FORMATTING_PRESETS_CELL_PROPERTIES = createPivotConditionalCellProperties(
[
pivotConditionalFormattingPresets.gt(300000, {
field: 'revenue',
class: 'pivot-formatting-strong',
style: {
backgroundColor: 'rgba(22, 163, 74, 0.16)',
color: '#166534',
fontWeight: '600',
},
}),
pivotConditionalFormattingPresets.lt(0.28, {
field: 'marginRate',
class: 'pivot-formatting-risk',
style: {
backgroundColor: 'rgba(220, 38, 38, 0.14)',
color: '#991b1b',
fontWeight: '600',
},
}),
pivotConditionalFormattingPresets.between(0.28, 0.34, {
field: 'marginRate',
style: {
backgroundColor: 'rgba(234, 179, 8, 0.14)',
color: '#854d0e',
},
}),
pivotConditionalFormattingPresets.gt(55, {
field: 'orders',
style: {
backgroundColor: 'rgba(37, 99, 235, 0.14)',
color: '#1d4ed8',
fontWeight: '600',
},
}),
],
{ match: 'all' },
);
export const PIVOT_FORMATTING_PRESETS_COLUMN_TYPES: ColumnTypes = {
pivotCurrency: formattedColumn(currencyFormatter),
pivotPercent: formattedColumn(percentFormatter),
pivotDate: {
cellTemplate: (_h, props) => dateFormatter(props.value),
},
pivotInteger: formattedColumn(integerFormatter),
};
export const PIVOT_FORMATTING_PRESETS_CONFIG: PivotConfig = {
dimensions: [
{ prop: 'region', name: 'Region', sortable: true, size: 170 },
{ prop: 'closeDate', name: 'Close date', columnType: 'pivotDate', sortable: true, size: 140 },
{ prop: 'segment', name: 'Segment', sortable: true, size: 140 },
{
prop: 'revenue',
name: 'Revenue',
columnType: 'pivotCurrency',
cellProperties: PIVOT_FORMATTING_PRESETS_CELL_PROPERTIES,
},
{
prop: 'marginRate',
name: 'Margin',
columnType: 'pivotPercent',
cellProperties: PIVOT_FORMATTING_PRESETS_CELL_PROPERTIES,
},
{
prop: 'orders',
name: 'Orders',
columnType: 'pivotInteger',
cellProperties: PIVOT_FORMATTING_PRESETS_CELL_PROPERTIES,
},
],
rows: ['region', 'closeDate'],
columns: ['segment'],
values: [
{ prop: 'revenue', aggregator: 'sum', label: 'Revenue' },
{ prop: 'marginRate', aggregator: 'avg', label: 'Avg margin' },
{ prop: 'orders', aggregator: 'sum', label: 'Orders' },
],
totals: {
subtotals: true,
grandTotal: true,
},
groupLabels: {
null: formatPivotValue(null, { preset: 'date', nullDisplay: 'No close date' }),
},
fieldPanel: {
visible: true,
allowFieldDragging: true,
showDataFields: true,
showRowFields: true,
showColumnFields: true,
},
};
export const PIVOT_FORMATTING_PRESETS_PLUGINS = [PivotPlugin, AdvanceFilterPlugin, RowOddPlugin] as any[];
export function getPivotFormattingPresetRows(rows?: DataType[] | null) {
return Array.isArray(rows) && rows.length > 0 ? rows : PIVOT_FORMATTING_PRESETS_ROWS;
}
export function load(parentSelector: string, rows?: DataType[]) {
const { isDark } = currentTheme();
const grid = document.createElement('revo-grid') as HTMLRevoGridElement;
grid.className = 'grow h-full w-full cell-border';
grid.range = true;
grid.resize = true;
grid.filter = true;
grid.colSize = 150;
grid.readonly = true;
grid.hideAttribution = true;
grid.theme = isDark() ? 'darkCompact' : 'compact';
grid.columnTypes = PIVOT_FORMATTING_PRESETS_COLUMN_TYPES;
grid.plugins = PIVOT_FORMATTING_PRESETS_PLUGINS;
grid.columns = [];
Object.assign(grid, {
pivot: PIVOT_FORMATTING_PRESETS_CONFIG,
});
document.querySelector(parentSelector)?.appendChild(grid);
grid.source = getPivotFormattingPresetRows(rows);
return () => {
grid.remove();
};
}
<template>
<RevoGrid
class="grow h-full cell-border"
hide-attribution
range
resize
filter
:colSize="150"
:source="rows"
:pivot.prop="pivot"
:theme="isDark ? 'darkCompact' : 'compact'"
:plugins="plugins"
:column-types="columnTypes"
readonly
/>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
import RevoGrid, { type GridPlugin } from '@revolist/vue3-datagrid';
import { currentThemeVue } from '../composables/useRandomData';
import {
getPivotFormattingPresetRows,
PIVOT_FORMATTING_PRESETS_COLUMN_TYPES,
PIVOT_FORMATTING_PRESETS_CONFIG,
PIVOT_FORMATTING_PRESETS_PLUGINS,
} from './PivotFormattingPresets';
const { isDark } = currentThemeVue();
const props = defineProps<{
rows?: any[];
}>();
const rows = computed(() => getPivotFormattingPresetRows(props.rows));
const plugins: GridPlugin[] = PIVOT_FORMATTING_PRESETS_PLUGINS;
const columnTypes = ref(PIVOT_FORMATTING_PRESETS_COLUMN_TYPES);
const pivot = computed(() => PIVOT_FORMATTING_PRESETS_CONFIG);
</script>
import { useMemo } from 'react';
import { RevoGrid } from '@revolist/react-datagrid';
import { currentTheme } from '../composables/useRandomData';
import {
getPivotFormattingPresetRows,
PIVOT_FORMATTING_PRESETS_CELL_PROPERTIES,
PIVOT_FORMATTING_PRESETS_COLUMN_TYPES,
PIVOT_FORMATTING_PRESETS_CONFIG,
PIVOT_FORMATTING_PRESETS_PLUGINS,
} from './PivotFormattingPresets';
interface PivotFormattingPresetsProps {
rows?: any[];
}
function PivotFormattingPresets({ rows }: PivotFormattingPresetsProps) {
const { isDark } = currentTheme();
const data = useMemo(() => getPivotFormattingPresetRows(rows), [rows]);
const plugins = useMemo(() => PIVOT_FORMATTING_PRESETS_PLUGINS, []);
const columnTypes = useMemo(() => PIVOT_FORMATTING_PRESETS_COLUMN_TYPES, []);
const cellProperties = useMemo(() => PIVOT_FORMATTING_PRESETS_CELL_PROPERTIES, []);
const pivot = useMemo(
() => ({
...PIVOT_FORMATTING_PRESETS_CONFIG,
dimensions: PIVOT_FORMATTING_PRESETS_CONFIG.dimensions?.map((dimension) => (
['revenue', 'marginRate', 'orders'].includes(String(dimension.prop))
? { ...dimension, cellProperties }
: dimension
)),
}),
[cellProperties],
);
return (
<RevoGrid
className="grow h-full cell-border"
hideAttribution
range
resize
filter
colSize={150}
source={data}
columns={[]}
pivot={pivot}
theme={isDark() ? 'darkCompact' : 'compact'}
plugins={plugins}
columnTypes={columnTypes}
readonly
/>
);
}
export default PivotFormattingPresets;
import { Component, Input, NO_ERRORS_SCHEMA, ViewEncapsulation } from '@angular/core';
import { RevoGrid } from '@revolist/angular-datagrid';
import { currentTheme } from '../composables/useRandomData';
import {
getPivotFormattingPresetRows,
PIVOT_FORMATTING_PRESETS_COLUMN_TYPES,
PIVOT_FORMATTING_PRESETS_CONFIG,
PIVOT_FORMATTING_PRESETS_PLUGINS,
} from './PivotFormattingPresets';
@Component({
selector: 'pivot-formatting-presets-grid',
standalone: true,
imports: [RevoGrid],
// Allows Angular demos to bind RevoGrid plugin props that are not wrapper inputs.
schemas: [NO_ERRORS_SCHEMA],
template: `
<revo-grid
class="grow h-full cell-border"
style="min-height: 560px"
[hideAttribution]="true"
[range]="true"
[resize]="true"
[filter]="true"
[colSize]="150"
[source]="data"
[pivot]="pivot"
[theme]="theme"
[plugins]="plugins"
[columnTypes]="columnTypes"
[readonly]="true"
></revo-grid>
`,
encapsulation: ViewEncapsulation.None,
})
export class PivotFormattingPresetsGridComponent {
@Input() set rows(value: any[] | undefined) {
this.data = getPivotFormattingPresetRows(value);
}
data = getPivotFormattingPresetRows();
theme = currentTheme().isDark() ? 'darkCompact' : 'compact';
plugins = PIVOT_FORMATTING_PRESETS_PLUGINS;
columnTypes = PIVOT_FORMATTING_PRESETS_COLUMN_TYPES;
pivot = PIVOT_FORMATTING_PRESETS_CONFIG;
}
Basic Setup
Section titled “Basic Setup”Import the helpers from Enterprise and create reusable formatters for the value types in your report:
import { createPivotValueFormatter, formatPivotValue, pivotConditionalFormattingPresets, createPivotConditionalCellProperties, PivotPlugin, type PivotConfig,} from '@revolist/revogrid-enterprise';
const revenueFormatter = createPivotValueFormatter({ preset: 'currency', currency: 'USD', locale: 'en-US', options: { maximumFractionDigits: 0 },});
const marginFormatter = createPivotValueFormatter({ preset: 'percent', locale: 'en-US', options: { minimumFractionDigits: 1, maximumFractionDigits: 1 },});createPivotValueFormatter() returns a function. Use it when the same formatting rule is reused by many cells. Use formatPivotValue() when you only need to format one value:
const formattedLabel = formatPivotValue(null, { preset: 'date', nullDisplay: 'No close date',});Value Formatters
Section titled “Value Formatters”Pivot formatters are display helpers. They do not change the original source data or the aggregated number that Pivot calculates. Keep your source values as real numbers and dates, then format them at render time.
const columnTypes = { pivotCurrency: { cellTemplate: (_h, props) => revenueFormatter(props.value), cellProperties: () => ({ class: { 'align-right': true } }), }, pivotPercent: { cellTemplate: (_h, props) => marginFormatter(props.value), cellProperties: () => ({ class: { 'align-right': true } }), },};Attach those column types to the Pivot dimensions that produce analytical value cells:
const pivot: PivotConfig = { dimensions: [ { prop: 'region', name: 'Region' }, { prop: 'closeDate', name: 'Close date', columnType: 'pivotDate' }, { prop: 'segment', name: 'Segment' }, { prop: 'revenue', name: 'Revenue', columnType: 'pivotCurrency' }, { prop: 'marginRate', name: 'Margin', columnType: 'pivotPercent' }, ], rows: ['region', 'closeDate'], columns: ['segment'], values: [ { prop: 'revenue', aggregator: 'sum', label: 'Revenue' }, { prop: 'marginRate', aggregator: 'avg', label: 'Avg margin' }, ],};Locale And Presets
Section titled “Locale And Presets”The formatter presets use the browser Intl formatters. Choose the preset that matches the value:
number: plain numeric values such as counts, units, or scores.currency: money values. Always providecurrency, for example"USD"or"EUR".percent: ratio values such as0.315, displayed as31.5%.date: date-only values.datetime: date and time values.
const integerFormatter = createPivotValueFormatter({ preset: 'number', locale: 'en-US', options: { maximumFractionDigits: 0 },});
const eurFormatter = createPivotValueFormatter({ preset: 'currency', currency: 'EUR', locale: 'de-DE',});
const dateFormatter = createPivotValueFormatter({ preset: 'date', locale: 'en-US', options: { month: 'short', day: '2-digit', year: 'numeric' },});Conditional Formatting Presets
Section titled “Conditional Formatting Presets”Use pivotConditionalFormattingPresets to describe matching rules, and createPivotConditionalCellProperties() to turn those rules into a RevoGrid cellProperties function.
const conditionalCellProperties = createPivotConditionalCellProperties( [ pivotConditionalFormattingPresets.gt(300000, { field: 'revenue', style: { backgroundColor: 'rgba(22, 163, 74, 0.16)', color: '#166534', fontWeight: '600', }, }), pivotConditionalFormattingPresets.lt(0.28, { field: 'marginRate', style: { backgroundColor: 'rgba(220, 38, 38, 0.14)', color: '#991b1b', fontWeight: '600', }, }), pivotConditionalFormattingPresets.between(0.28, 0.34, { field: 'marginRate', style: { backgroundColor: 'rgba(234, 179, 8, 0.14)', color: '#854d0e', }, }), ], { match: 'all' },);Available presets:
gt(value, props): greater than a number.lt(value, props): less than a number.between(min, max, props): between two numbers, including both bounds.equal(value, props): equal to a value.textContains(value, props): contains text.
The optional field narrows a rule to a generated value field. For example, field: 'revenue' applies to generated revenue cells even when Pivot creates column paths such as Software|revenue.
Analytical Cells
Section titled “Analytical Cells”By default, createPivotConditionalCellProperties() only formats Pivot analytical cells. These are generated cells from the values area, not row header fields such as region or closeDate.
This default prevents a rule like gt(300000) from accidentally styling row dimensions that happen to contain comparable values. If you intentionally want rules to apply outside analytical value cells, enable includeNonAnalytical:
const allCellProperties = createPivotConditionalCellProperties(rules, { includeNonAnalytical: true,});Most Pivot reports should keep the default and attach conditional formatting to value dimensions:
const pivot: PivotConfig = { dimensions: [ { prop: 'region', name: 'Region' }, { prop: 'revenue', name: 'Revenue', columnType: 'pivotCurrency', cellProperties: conditionalCellProperties, }, { prop: 'marginRate', name: 'Margin', columnType: 'pivotPercent', cellProperties: conditionalCellProperties, }, ], rows: ['region'], columns: ['segment'], values: [ { prop: 'revenue', aggregator: 'sum' }, { prop: 'marginRate', aggregator: 'avg' }, ],};Practical Example
Section titled “Practical Example”This report groups opportunities by region and close date, splits generated value columns by segment, formats revenue as currency, formats margin as a percent, and highlights high revenue or weak margin.
const rows = [ { region: 'North America', closeDate: '2026-01-16', segment: 'Software', revenue: 184000, marginRate: 0.37 }, { region: 'North America', closeDate: '2026-01-22', segment: 'Services', revenue: 126000, marginRate: 0.31 }, { region: 'EMEA', closeDate: '2026-02-06', segment: 'Services', revenue: 118000, marginRate: 0.28 },];
const pivot: PivotConfig = { dimensions: [ { prop: 'region', name: 'Region' }, { prop: 'closeDate', name: 'Close date', columnType: 'pivotDate' }, { prop: 'revenue', name: 'Revenue', columnType: 'pivotCurrency', cellProperties: conditionalCellProperties, }, { prop: 'marginRate', name: 'Margin', columnType: 'pivotPercent', cellProperties: conditionalCellProperties, }, ], rows: ['region', 'closeDate'], columns: ['segment'], values: [ { prop: 'revenue', aggregator: 'sum', label: 'Revenue' }, { prop: 'marginRate', aggregator: 'avg', label: 'Avg margin' }, ], totals: { subtotals: true, grandTotal: true, }, groupLabels: { null: formatPivotValue(null, { preset: 'date', nullDisplay: 'No close date' }), },};
const grid = document.querySelector('revo-grid');grid.columnTypes = columnTypes;grid.plugins = [PivotPlugin];grid.pivot = pivot;grid.source = rows;Common Mistakes
Section titled “Common Mistakes”- Formatting the source data before Pivot aggregates it. Keep values numeric so
sum,avg, and comparisons work correctly. - Using
preset: 'percent'with whole percentages.31.5%should usually be stored as0.315, not31.5. - Forgetting
currencywhen using thecurrencypreset. - Expecting conditional formatting to affect row fields by default. It targets analytical value cells unless
includeNonAnalyticalis enabled. - Omitting
fieldon a conditional rule when the report has several value fields. Withoutfield, the rule can match every analytical value cell. - Expecting
cellTemplateformatting to change conditional comparisons. Conditional rules evaluate the raw cell value, not the formatted display string.
Continue with Sorting And Filtering or Configuration Reference.