---
title: "Formatting"
framework: vue
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/vue/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

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgStudio } from "ag-studio-vue3";
import {
  AgDataEngine,
  AgDataSourcesDefinition,
  AgFieldDefinition,
  AgReportState,
  AgStudioApi,
  AgStudioApiReadyEvent,
  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 VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <div style="display: flex; flex-direction: column; height: 100%">
      <ag-studio
        style="width: 100%; height: 100%;"
        class="my-studio-container"
        @api-ready="onApiReady"
        :initialState="initialState"
        :mode="mode"
        :data="data"></ag-studio>
      </div>
        </div>
    `,
  components: {
    "ag-studio": AgStudio,
  },
  setup(props) {
    const studioApi = shallowRef<AgStudioApi | null>(null);
    const initialState = ref<AgReportState>({
      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,
        },
      },
    });
    const mode = ref<AgStudioMode>("edit");
    const data = ref<AgDataSourcesDefinition | AgDataEngine>({
      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 onApiReady = (params: AgStudioApiReadyEvent) => {
      studioApi.value = params.api;
    };

    return {
      studioApi,
      initialState,
      mode,
      data,
      onApiReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

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

## Format Options

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

```ts
<ag-studio
    :data="data"
    /* other studio properties ... */>
</ag-studio>

this.data = {
    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' } },
        ],
    }],
};
```

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

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgStudio } from "ag-studio-vue3";
import {
  AgDataEngine,
  AgDataSourcesDefinition,
  AgFieldDefinition,
  AgReportState,
  AgStudioApi,
  AgStudioApiReadyEvent,
  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 VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <div style="display: flex; flex-direction: column; height: 100%">
      <ag-studio
        style="width: 100%; height: 100%;"
        class="my-studio-container"
        @api-ready="onApiReady"
        :initialState="initialState"
        :mode="mode"
        :data="data"></ag-studio>
      </div>
        </div>
    `,
  components: {
    "ag-studio": AgStudio,
  },
  setup(props) {
    const studioApi = shallowRef<AgStudioApi | null>(null);
    const initialState = ref<AgReportState>({
      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,
        },
      },
    });
    const mode = ref<AgStudioMode>("edit");
    const data = ref<AgDataSourcesDefinition | AgDataEngine>({
      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 onApiReady = (params: AgStudioApiReadyEvent) => {
      studioApi.value = params.api;
    };

    return {
      studioApi,
      initialState,
      mode,
      data,
      onApiReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

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

### 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

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgStudio } from "ag-studio-vue3";
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 VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <div style="display: flex; flex-direction: column; height: 100%">
      <ag-studio
        style="width: 100%; height: 100%;"
        class="my-studio-container"
        @api-ready="onApiReady"
        :initialState="initialState"
        :mode="mode"
        :data="data"></ag-studio>
      </div>
        </div>
    `,
  components: {
    "ag-studio": AgStudio,
  },
  setup(props) {
    const studioApi = shallowRef<AgStudioApi | null>(null);
    const initialState = ref<AgReportState>({
      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 mode = ref<AgStudioMode>("edit");
    const data = ref<AgDataSourcesDefinition | AgDataEngine>(null);

    const onApiReady = (params: AgStudioApiReadyEvent) => {
      studioApi.value = params.api;

      const getData = (data) => {
        ({
          sources: [
            {
              id: "sales",
              data: data.map((row: any) => ({
                ...row,
                salePrice: window.agRandom() * 10,
                purchasePrice: window.agRandom() * 10,
              })),
              fields,
            },
          ],
          formats,
        });
      };

      fetch("https://www.ag-grid.com/studio/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((respData) => (data.value = getData(respData)));
    };

    return {
      studioApi,
      initialState,
      mode,
      data,
      onApiReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

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

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

```ts
<ag-studio
    :data="data"
    /* other studio properties ... */>
</ag-studio>

this.data = {
    sources: [{
        fields: [{
            id: 'salePrice',
            format: 'currencyFormat',
            formatOptions: { format: '$#,##0.00' },
        }],
    }],
};
```

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:

```ts
<ag-studio
    :data="data"
    /* other studio properties ... */>
</ag-studio>

this.data = {
    formats: createFormats({
        overrides: {
            textFormat: {
                createValueFormatter: () => (value) => value.toUpperCase(),
            },
            currencyFormat: {
                formatOptions: { format: '£#,##0.00' },
            },
        },
    }),
};
```

## Custom Formats

#### Custom Formats

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgStudio } from "ag-studio-vue3";
import {
  AgDataEngine,
  AgDataSourcesDefinition,
  AgFieldDefinition,
  AgReportState,
  AgStudioApi,
  AgStudioApiReadyEvent,
  AgStudioMode,
  AgStudioProperties,
  createFormats,
} from "ag-studio";
import { CustomRegistry } from "./interfaces.ts";

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 VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <div style="display: flex; flex-direction: column; height: 100%">
      <ag-studio
        style="width: 100%; height: 100%;"
        class="my-studio-container"
        @api-ready="onApiReady"
        :initialState="initialState"
        :mode="mode"
        :data="data"></ag-studio>
      </div>
        </div>
    `,
  components: {
    "ag-studio": AgStudio,
  },
  setup(props) {
    const studioApi = shallowRef<AgStudioApi<CustomRegistry> | null>(null);
    const initialState = ref<AgReportState<CustomRegistry>>({
      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 mode = ref<AgStudioMode>("edit");
    const data = ref<AgDataSourcesDefinition | AgDataEngine>(null);

    const onApiReady = (params: AgStudioApiReadyEvent) => {
      studioApi.value = params.api;

      const getData = (data) => {
        ({
          sources: [{ id: "medals", data, fields }],
          formats,
        });
      };

      fetch("https://www.ag-grid.com/studio/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((respData) => (data.value = getData(respData)));
    };

    return {
      studioApi,
      initialState,
      mode,
      data,
      onApiReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

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

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/vue/registry-type/):

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

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

```ts
<ag-studio
    :data="data"
    /* other studio properties ... */>
</ag-studio>

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

## 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. |
