---
title: "Formatting"
framework: react
version: "2.1.2"
---

# Formatting

Every field has a Format that controls how its values are displayed. Each provided Format applies a sensible default, and `formatOptions.format` overrides that default with either an Excel-style format string or - for numeric and date Formats - an `Intl` instance.

## Provided Formats

| Format | Data Type | Default Display Format |
| --- | --- | --- |
| `textFormat` | `string` | `Text Value` |
| `integerFormat` | `number` | `100,000` |
| `decimalFormat` | `number` | `123.00` |
| `booleanFormat` | `boolean` | `True` / `False` (or [Locale](https://www.ag-grid.com/studio/react/localisation/) equivalent) |
| `dateFormat` | `date` | `31/12/2025` (or equivalent for user's locale) |
| `dateTimeFormat` | `datetime` | `31/12/2025, 13:00:00` (or equivalent for user's locale/timezone) |
| `percentageFormat` | `number` | `50%` |
| `currencyFormat` | `number` | `123.00` |

#### Formatting

```tsx
"use client";

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgStudio, AgStudioRef } from "ag-studio-react";
import {
  AgDataEngine,
  AgDataSourcesDefinition,
  AgFieldDefinition,
  AgReportState,
  AgStudioApi,
  AgStudioMode,
  AgStudioProperties,
} from "ag-studio";

const fields: AgFieldDefinition[] = [
  {
    id: "product",
    format: "textFormat",
  },
  {
    id: "quantity",
    format: "integerFormat",
  },
  {
    id: "weight",
    format: "decimalFormat",
  },
  {
    id: "inStock",
    format: "booleanFormat",
  },
  {
    id: "discount",
    format: "percentageFormat",
  },
  {
    id: "price",
    format: "currencyFormat",
  },
  {
    id: "orderDate",
    format: "dateFormat",
  },
  {
    id: "deliveredAt",
    format: "dateTimeFormat",
  },
];

const StudioExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const studioStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
  const [data, setData] = useState<AgDataSource>({
    sources: [
      {
        id: "orders",
        data: [
          {
            product: "Wireless Headphones",
            quantity: 1200,
            weight: 0.345,
            inStock: true,
            discount: 0.15,
            price: 129.99,
            orderDate: "2025-03-15",
            deliveredAt: "2025-03-18T14:30:00",
          },
          {
            product: "Standing Desk",
            quantity: 85,
            weight: 32.5,
            inStock: true,
            discount: 0.05,
            price: 449.0,
            orderDate: "2025-04-02",
            deliveredAt: "2025-04-09T09:15:00",
          },
          {
            product: "Coffee Beans 1kg",
            quantity: 5400,
            weight: 1.0,
            inStock: false,
            discount: 0.0,
            price: 18.5,
            orderDate: "2025-05-21",
            deliveredAt: "2025-05-23T11:45:00",
          },
          {
            product: "Mechanical Keyboard",
            quantity: 640,
            weight: 1.125,
            inStock: true,
            discount: 0.2,
            price: 89.99,
            orderDate: "2025-06-08",
            deliveredAt: "2025-06-11T16:00:00",
          },
        ],
        fields,
      },
    ],
  });
  const initialState = useMemo<AgReportState>(() => {
    return {
      pages: [
        {
          id: "a",
          widgets: {
            "1": {
              type: "grid",
              dataMapping: {
                cols: [
                  { id: "orders.product" },
                  { id: "orders.quantity" },
                  { id: "orders.weight" },
                  { id: "orders.inStock" },
                  { id: "orders.discount" },
                  { id: "orders.price" },
                  { id: "orders.orderDate" },
                  { id: "orders.deliveredAt" },
                ],
              },
            },
          },
          widgetLayout: {
            "1": {
              xTrack: 0,
              yTrack: 0,
              xSpan: 24,
              ySpan: 25,
            },
          },
        },
      ],
      selectedPageId: "a",
      panels: {
        filters: {
          collapsed: true,
        },
        edit: {
          collapsed: true,
        },
      },
    };
  }, []);

  return (
    <div style={containerStyle}>
      <div style={{ display: "flex", flexDirection: "column", height: "100%" }}>
        <AgStudio
          style={studioStyle}
          className="my-studio-container"
          data={data}
          initialState={initialState}
          mode={"edit"}
        />
      </div>
    </div>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <StudioExample />
  </StrictMode>,
);
```

[Live example: Formatting](https://www.ag-grid.com/studio/examples/formatting/formatting-overview/reactFunctionalTs/)

## Format Options

The `formatOptions.format` property on a field accepts a [Format String](#format-strings):

```jsx
const data = useMemo(() => { 
	return {
        sources: [{
            fields: [
                { id: 'priceGBP', format: 'currencyFormat', formatOptions: { format: '£#,##0.00' } },
                { id: 'discount', format: 'percentageFormat', formatOptions: { format: '#,##0.0%' } },
                { id: 'orderDate', format: 'dateFormat', formatOptions: { format: 'dd mmm yyyy' } },
            ],
        }],
    };
}, []);

<AgStudio data={data} />
```

Numeric and date Formats also accept an `Intl.NumberFormat` or `Intl.DateTimeFormat` instance, for cases where locale-aware formatting is required. Format strings are preferred for everything else.

### Format Strings

Format strings follow Excel's number-format syntax with a few extensions. The same syntax handles numeric, date, date-time, text, and boolean data types.

#### Number Tokens

| Token | Meaning |
| --- | --- |
| `0` | Mandatory digit (zero-padded). |
| `#` | Optional digit. |
| `.` | Decimal separator. |
| `,` | Thousands separator. A trailing `,` with no digit placeholder after it divides by 1,000. |
| `%` | Multiply by 100 and append `%`. |
| `‰` | Multiply by 1,000 and append `‰`. |
| `E+0` / `E-0` | Scientific notation. The `0`s set the minimum exponent width. |
| `"…"` | Literal text. |
| `\x` | Literal escape for a single character. |
| `@` | Text placeholder (string values). |

Common patterns:

| String | Output |
| --- | --- |
| `0` | `1235` |
| `0.00` | `1234.50` |
| `#,##0.00` | `1,234.50` |
| `#,##0,"k"` | `1k` |
| `0.00%` | `123450.00%` |
| `0.00E+00` | `1.23E+03` |

#### Date and Time Tokens

| Token | Meaning | Example |
| --- | --- | --- |
| `yy` / `yyyy` | 2- or 4-digit year | `25` / `2025` |
| `qq` | Quarter | `1` |
| `m` / `mm` / `mmm` / `mmmm` / `mmmmm` | Month: number, padded number, short name, long name, initial | `3` / `03` / `Mar` / `March` / `M` |
| `d` / `dd` / `ddd` / `dddd` | Day: number, padded number, short name, long name | `5` / `05` / `Wed` / `Wednesday` |
| `ww` / `www` | ISO week number, padded | `1` / `01` |
| `h` / `hh` | Hour | `9` / `09` |
| `m` / `mm` | Minute (when preceded by `h` or followed by `s`) | `5` / `05` |
| `s` / `ss` | Second | `5` / `05` |
| `am/pm` / `a/p` | Meridiem. Presence switches the hour token to 12-hour | `PM` / `P` |

#### Format Options

```tsx
"use client";

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgStudio, AgStudioRef } from "ag-studio-react";
import {
  AgDataEngine,
  AgDataSourcesDefinition,
  AgFieldDefinition,
  AgReportState,
  AgStudioApi,
  AgStudioMode,
  AgStudioProperties,
} from "ag-studio";

const fields: AgFieldDefinition[] = [
  {
    id: "product",
    format: "textFormat",
  },
  {
    id: "priceGBP",
    format: "currencyFormat",
    formatOptions: { format: "£#,##0.00" },
  },
  {
    id: "priceUSD",
    format: "currencyFormat",
    formatOptions: { format: "$#,##0.00" },
  },
  {
    id: "discount",
    format: "percentageFormat",
    formatOptions: { format: "#,##0.0%" },
  },
  {
    id: "growth",
    format: "percentageFormat",
    formatOptions: { format: "+0.0%;-0.0%;0%" },
  },
  {
    id: "weight",
    format: "decimalFormat",
    formatOptions: { format: '#,##0.000 "kg"' },
  },
  {
    id: "orderDate",
    format: "dateFormat",
    formatOptions: { format: "dd mmm yyyy" },
  },
  {
    id: "deliveredAt",
    format: "dateTimeFormat",
    formatOptions: { format: "dd mmm yyyy, hh:mm" },
  },
];

const StudioExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const studioStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
  const [data, setData] = useState<AgDataSource>({
    sources: [
      {
        id: "orders",
        data: [
          {
            product: "Wireless Headphones",
            priceGBP: 129.99,
            priceUSD: 159.99,
            discount: 0.15,
            growth: 0.082,
            weight: 0.345,
            orderDate: "2025-03-15",
            deliveredAt: "2025-03-18T14:30:00",
          },
          {
            product: "Standing Desk",
            priceGBP: 449.0,
            priceUSD: 549.0,
            discount: 0.05,
            growth: -0.021,
            weight: 32.5,
            orderDate: "2025-04-02",
            deliveredAt: "2025-04-09T09:15:00",
          },
          {
            product: "Coffee Beans 1kg",
            priceGBP: 18.5,
            priceUSD: 22.95,
            discount: 0.0,
            growth: 0.124,
            weight: 1.0,
            orderDate: "2025-05-21",
            deliveredAt: "2025-05-23T11:45:00",
          },
          {
            product: "Mechanical Keyboard",
            priceGBP: 89.99,
            priceUSD: 109.99,
            discount: 0.2,
            growth: 0.045,
            weight: 1.125,
            orderDate: "2025-06-08",
            deliveredAt: "2025-06-11T16:00:00",
          },
        ],
        fields,
      },
    ],
  });
  const initialState = useMemo<AgReportState>(() => {
    return {
      pages: [
        {
          id: "a",
          widgets: {
            "1": {
              type: "grid",
              dataMapping: {
                cols: [
                  { id: "orders.product" },
                  { id: "orders.priceGBP" },
                  { id: "orders.priceUSD" },
                  { id: "orders.discount" },
                  { id: "orders.growth" },
                  { id: "orders.weight" },
                  { id: "orders.orderDate" },
                  { id: "orders.deliveredAt" },
                ],
              },
            },
          },
          widgetLayout: {
            "1": {
              xTrack: 0,
              yTrack: 0,
              xSpan: 24,
              ySpan: 25,
            },
          },
        },
      ],
      selectedPageId: "a",
      panels: {
        filters: {
          collapsed: true,
        },
        edit: {
          collapsed: true,
        },
      },
    };
  }, []);

  return (
    <div style={containerStyle}>
      <div style={{ display: "flex", flexDirection: "column", height: "100%" }}>
        <AgStudio
          style={studioStyle}
          className="my-studio-container"
          data={data}
          initialState={initialState}
          mode={"edit"}
        />
      </div>
    </div>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <StudioExample />
  </StrictMode>,
);
```

[Live example: Format Options](https://www.ag-grid.com/studio/examples/formatting/format-options/reactFunctionalTs/)

### Conditional Formatting

A format string can combine multiple patterns separated by `;`. Pattern can be prefixed with a condition in square brackets (e.g. `[>1000]`) to control when it applies, with the first match being the one used to format the value. The available comparators are `>`, `>=`, `<`, `<=`, `=`, and `<>`.

```
[>1000000]#,##0.0,,"M";[>1000]#,##0.0,"K";#,##0
```

Without explicit conditions, the first two or three patterns apply to positive, negative, and zero values when formatting numbers; and the first two patterns apply to `true` and `false` when formatting booleans:

```
#,##0.00;(#,##0.00);"-"
```

```
"Yes";"No"
```

## Overriding Provided Formats

#### Overriding Formats

```tsx
"use client";

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgStudio, AgStudioRef } from "ag-studio-react";
import {
  AgDataEngine,
  AgDataSourcesDefinition,
  AgFieldDefinition,
  AgReportState,
  AgStudioApi,
  AgStudioApiReadyEvent,
  AgStudioMode,
  AgStudioProperties,
  createFormats,
} from "ag-studio";

const fields: AgFieldDefinition[] = [
  {
    id: "country",
    format: "textFormat",
  },
  {
    id: "purchasePrice",
    format: "currencyFormat",
  },
  {
    id: "salePrice",
    format: "currencyFormat",
    formatOptions: { format: "$#,##0.00" },
  },
];

const formats = createFormats({
  overrides: {
    textFormat: {
      createValueFormatter: () => (value) => value.toUpperCase(),
    },
    currencyFormat: {
      formatOptions: { format: "£#,##0.00" },
    },
  },
});

const StudioExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const studioStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
  const [data, setData] = useState<AgDataSourcesDefinition | AgDataEngine>();
  const initialState = useMemo<AgReportState>(() => {
    return {
      pages: [
        {
          id: "a",
          widgets: {
            "1": {
              type: "grid",
              dataMapping: {
                cols: [
                  { id: "sales.country" },
                  { id: "sales.purchasePrice", aggregation: "sum" },
                  { id: "sales.salePrice", aggregation: "sum" },
                ],
              },
            },
          },
          widgetLayout: {
            "1": {
              xTrack: 0,
              yTrack: 0,
              xSpan: 24,
              ySpan: 25,
            },
          },
        },
      ],
      selectedPageId: "a",
      panels: {
        filters: {
          collapsed: true,
        },
        edit: {
          collapsed: true,
        },
      },
    };
  }, []);

  const onApiReady = useCallback((params: AgStudioApiReadyEvent) => {
    fetch("https://www.ag-grid.com/studio/example-assets/olympic-winners.json")
      .then((resp) => resp.json())
      .then((data: any[]) => {
        setData({
          sources: [
            {
              id: "sales",
              data: data.map((row: any) => ({
                ...row,
                salePrice: window.agRandom() * 10,
                purchasePrice: window.agRandom() * 10,
              })),
              fields,
            },
          ],
          formats,
        });
      });
  }, []);

  return (
    <div style={containerStyle}>
      <div style={{ display: "flex", flexDirection: "column", height: "100%" }}>
        <AgStudio
          style={studioStyle}
          className="my-studio-container"
          data={data}
          initialState={initialState}
          mode={"edit"}
          onApiReady={onApiReady}
        />
      </div>
    </div>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <StudioExample />
  </StrictMode>,
);
```

[Live example: Overriding Formats](https://www.ag-grid.com/studio/examples/formatting/overriding-formats/reactFunctionalTs/)

A field can override any of its Format's value-formatting and serialising properties directly on its definition:

```jsx
const data = useMemo(() => { 
	return {
        sources: [{
            fields: [{
                id: 'salePrice',
                format: 'currencyFormat',
                formatOptions: { format: '$#,##0.00' },
            }],
        }],
    };
}, []);

<AgStudio data={data} />
```

The following Format properties can be set directly on the field definition:

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `serializer` | `AgFieldSerializer<InferDataTypeFromFormat<TRegistry, TFormat>>` |  | Optional. How the field values will be serialized into state. Defaults to format serializer. |
| `deserializer` | `AgFieldDeserializer<InferDataTypeFromFormat<TRegistry, TFormat>>` |  | Optional. How the field values will be deserialized from state. Defaults to format deserializer. |
| `createValueFormatter` | `AgFieldValueFormatterFactory<InferDataTypeFromFormat<TRegistry, TFormat>, TFormatOptions, any>` |  | Optional. Build a value formatter bound to the field's format options and the runtime API. Defaults to format factory. |
| `blankValue` | `string` |  | Optional. How blank values will be displayed. Defaults to format blank value. |
| `formatOptions` | `TFormatOptions` |  | Optional. Will be passed to the value formatter. |

To change the default for a built-in Format across every field that uses it, pass the result of `createFormats` to the `formats` property of the data sources definition. The `overrides` accept any of the [Format Properties](#format-api) except the data type:

```jsx
const data = useMemo(() => { 
	return {
        formats: createFormats({
            overrides: {
                textFormat: {
                    createValueFormatter: () => (value) => value.toUpperCase(),
                },
                currencyFormat: {
                    formatOptions: { format: '£#,##0.00' },
                },
            },
        }),
    };
}, []);

<AgStudio data={data} />
```

## Custom Formats

#### Custom Formats

```tsx
"use client";

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgStudio, AgStudioRef } from "ag-studio-react";
import {
  AgDataEngine,
  AgDataSourcesDefinition,
  AgFieldDefinition,
  AgReportState,
  AgStudioApi,
  AgStudioApiReadyEvent,
  AgStudioMode,
  AgStudioProperties,
  createFormats,
} from "ag-studio";
import { CustomRegistry } from "./interfaces";

const fields: AgFieldDefinition<CustomRegistry>[] = [
  {
    id: "athlete",
    format: "textFormat",
  },
  {
    id: "country",
    format: "textFormat",
  },
  {
    id: "year",
    format: "integerFormat",
    formatOptions: { format: "0" },
  },
  {
    id: "sport",
    format: "textFormat",
  },
  {
    id: "gold",
    format: "myCustomFormat",
  },
  {
    id: "silver",
    format: "integerFormat",
  },
  {
    id: "bronze",
    format: "integerFormat",
  },
];

const formats = createFormats<CustomRegistry>({
  additionalTypes: {
    myCustomFormat: {
      dataType: "number",
      supportedRoles: ["numeric", "category"],
      supportedAggregations: ["sum"],
      serializer: (value) => value,
      deserializer: (value) => value,
      createValueFormatter: () => (value) => `*${value}*`,
      blankValue: "-",
    },
  },
});

const StudioExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const studioStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
  const [data, setData] = useState<AgDataSourcesDefinition | AgDataEngine>();
  const initialState = useMemo<AgReportState<CustomRegistry>>(() => {
    return {
      pages: [
        {
          id: "a",
          widgets: {
            "1": {
              type: "grid",
              dataMapping: {
                cols: [
                  { id: "medals.country" },
                  { id: "medals.sport" },
                  { id: "medals.gold", aggregation: "sum" },
                  { id: "medals.silver", aggregation: "sum" },
                  { id: "medals.bronze", aggregation: "sum" },
                ],
              },
            },
          },
          widgetLayout: {
            "1": {
              xTrack: 0,
              yTrack: 0,
              xSpan: 24,
              ySpan: 25,
            },
          },
        },
      ],
      selectedPageId: "a",
      panels: {
        filters: {
          collapsed: true,
        },
        edit: {
          collapsed: true,
        },
      },
    };
  }, []);

  const onApiReady = useCallback((params: AgStudioApiReadyEvent) => {
    fetch("https://www.ag-grid.com/studio/example-assets/olympic-winners.json")
      .then((resp) => resp.json())
      .then((data: any[]) => {
        setData({
          sources: [{ id: "medals", data, fields }],
          formats,
        });
      });
  }, []);

  return (
    <div style={containerStyle}>
      <div style={{ display: "flex", flexDirection: "column", height: "100%" }}>
        <AgStudio<CustomRegistry>
          style={studioStyle}
          className="my-studio-container"
          data={data}
          initialState={initialState}
          mode={"edit"}
          onApiReady={onApiReady}
        />
      </div>
    </div>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <StudioExample />
  </StrictMode>,
);
```

[Live example: Custom Formats](https://www.ag-grid.com/studio/examples/formatting/custom-formats/reactFunctionalTs/)

Custom Formats are added by passing `additionalTypes` to `createFormats`. See the [Format API](#format-api) for the full list of properties.

If using Typescript, define as a type that includes `AgFormats` (note that it must be a type, not an interface) and set as `formats` on the [Registry Type](https://www.ag-grid.com/studio/react/registry-type/):

```
type CustomFormats = AgFormats & {
    myCustomFormat: AgFormatDefinition<'number'>;
}

interface CustomRegistry extends AgDefaultRegistry {
    formats: CustomFormats;
}
```

```jsx
const data = useMemo(() => { 
	return {
        formats: createFormats<CustomRegistry>({
            additionalTypes: {
                myCustomFormat: {
                    dataType: 'number',
                    supportedRoles: ['numeric', 'category'],
                    supportedAggregations: ['sum'],
                    serializer: (value) => value,
                    deserializer: (value) => value,
                    createValueFormatter: () => (value) => `*${value}*`,
                    blankValue: '-',
                },
            },
        }),
    };
}, []);

<AgStudio data={data} />
```

## Format API

Properties available on the `AgFormatDefinition&lt;TDataType extends AgDataType = AgDataType, TFormatOptions = any&gt;` interface.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `dataType` | `TDataType` |  | The data type. |
| `supportedRoles` | `AgFieldRole[]` |  | How fields of this format can be used within widgets. Ordered by preference. |
| `supportedAggregations` | `AgAggregationFunction[]` |  | The supported aggregations for fields of this format. |
| `serializer` | `AgFieldSerializer<TDataType>` |  | How the field values will be serialized into state. |
| `deserializer` | `AgFieldDeserializer<TDataType>` |  | How the field values will be deserialized from state. |
| `createValueFormatter` | `AgFieldValueFormatterFactory<TDataType, TFormatOptions>` |  | Build a value formatter bound to the field's options and the runtime API. Called once per field at hydration. |
| `supportedBuckets` | `string[]` |  | The buckets that fields will support. |
| `blankValue` | `string` |  | How blank values will be displayed. |
| `formatOptions` | `TFormatOptions` |  | Optional. Will be passed to the value formatter. |
