---
title: "Sharing & Caching Data"
framework: javascript
version: "2.1.2"
---

# Sharing & Caching Data

A Data Engine loads, processes, and caches data for Studio widgets. Studio creates one automatically when you pass data sources via the `data` property, but you can create the engine yourself to share it across instances, or replace it entirely with a custom backend.

## Built-in Engine

When you pass data sources directly to Studio, it creates a built-in Data Engine behind the scenes. Creating the engine externally with `createDataEngine(data)` gives you two benefits:

- **Sharing** - multiple Studio instances can point at the same engine, so they share a single copy of the data.
- **Caching across lifecycles** - the engine survives when Studio is destroyed and recreated, so data doesn't need to be re-fetched or reprocessed on remount.

#### Data Engine

```ts
import {
  AgDataEngine,
  AgReportState,
  AgStudioApi,
  AgStudioProperties,
  createDataEngine,
  createStudio,
} from "ag-studio";

const initialState: AgReportState = {
  pages: [
    {
      id: "page1",
      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" },
              { id: "medals.total", aggregation: "sum" },
            ],
          },
        },
        "2": {
          type: "column-chart-grouped",
          dataMapping: {
            categoryKey: [{ id: "medals.country" }],
            valueKey: [
              { id: "medals.gold", aggregation: "sum" },
              { id: "medals.silver", aggregation: "sum" },
              { id: "medals.bronze", aggregation: "sum" },
            ],
            tooltipKey: [],
          },
        },
      },
      widgetLayout: {
        "1": {
          xTrack: 0,
          yTrack: 0,
          xSpan: 24,
          ySpan: 16,
        },
        "2": {
          xTrack: 0,
          yTrack: 16,
          xSpan: 24,
          ySpan: 16,
        },
      },
    },
  ],
  selectedPageId: "page1",
  panels: {
    filters: {
      collapsed: true,
    },
    edit: {
      collapsed: true,
    },
  },
};

const studioProperties: AgStudioProperties = {
  mode: "edit",
  initialState,
};

let studioApi: AgStudioApi;

let dataEngine: AgDataEngine;

let created = true;

function recreate() {
  const studioDiv = document.querySelector<HTMLElement>("#myStudio")!;
  if (created) {
    studioApi.destroy();
    const placeholder = document.createElement("div");
    placeholder.textContent = "No current Studio instance";
    studioDiv.appendChild(placeholder);
  } else {
    studioDiv.firstElementChild?.remove();
    studioApi = createStudio(studioDiv, {
      ...studioProperties,
      data: dataEngine,
    });
  }
  document.getElementById("recreate")!.textContent = created
    ? "Recreate with Data Engine"
    : "Destroy Studio Instance";
  created = !created;
}

// setup Studio after the page has finished loading
const studioDiv = document.querySelector<HTMLElement>("#myStudio")!;
studioApi = createStudio(studioDiv, studioProperties);

fetch("https://www.ag-grid.com/studio/example-assets/olympic-winners.json")
  .then((response) => response.json())
  .then((data) => {
    dataEngine = createDataEngine({ sources: [{ id: "medals", data }] });
    studioApi!.setProperty("data", dataEngine);
  });

if (typeof window !== "undefined") {
  // Attach external event handlers to window so they can be called from index.html
  (<any>window).recreate = recreate;
}
```

[Live example: Data Engine](https://www.ag-grid.com/studio/examples/sharing-caching-data/data-engine/typescript/)

```ts
const dataEngine = createDataEngine({
    sources: [{
        id: 'medals',
        data: [
            {
                year: 2000,
                sport: 'Swimming',
                country: 'United States',
                // ... other fields
            },
            // ... other rows
        ],
    }],
});
```

```js
const studioProperties = {
    data: dataEngine,

    // other studio properties ...
}
```

See [Sync Data](https://www.ag-grid.com/studio/javascript/sync-data/) and [Async Data](https://www.ag-grid.com/studio/javascript/async-data/) for the full range of data loading patterns.

`createDataEngine(data)` accepts a `data` object of type `AgDataSourcesDefinition`.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `sources` | `AgDataSource<TRegistry>[]` |  | One or more data sources. |
| `relationships` | `AgRelationDefinition[]` |  | When using multiple related tables, this describes the fields that link the tables together. |
| `expressions` | `AgExpressionFieldDefinition<TRegistry, AgFormat<TRegistry>, any>[]` |  | Expression field definitions for calculated columns. |
| `formats` | `TRegistry["formats"]` |  | Overrides to existing formats, or additional custom formats. |
| `description` | `string` |  | AI-facing overview of the entire dataset: what it contains, what it's for, domain quirks. |
| `calendars` | `AgCalendar[]` |  | Named time dimensions (calendars) that supply date fragments and a continuous date spine. |
| `buckets` | `TRegistry["buckets"]` |  | Additional date-fragment bucket definitions to register alongside the built-in set (year, quarter, month, week, day, monthOfYear, dayOfWeek, …). Use this to add project-specific groupings such as `weekend`, `dayOfMonth`, or `hour` that the built-in registry does not include. Provide via createBuckets so type-level registry inference works correctly. |
| `options` | `AgDataSourcesOptions` |  | Engine-wide behavioural options, such as fan-out detection policy. |

## Embedding Single Widgets

A widget cannot be used on its own outside of Studio. To place an individual widget in your own application, run a Studio instance that shows a single widget filling the canvas, with the panels hidden. Several such instances can share one engine, so the data is loaded once.

#### Single Widgets

```ts
import {
  AgDataEngine,
  AgReportState,
  AgStudioApi,
  AgStudioProperties,
  AgWidgetState,
  createDataEngine,
  createStudio,
  studioTheme,
} from "ag-studio";

// A single full-canvas widget, no panels - a Studio instance acting as one embeddable widget.
function singleWidgetState(id: string, widget: AgWidgetState): AgReportState {
  return {
    pages: [
      {
        id: "page1",
        widgets: { [id]: widget },
        widgetLayout: { [id]: { xTrack: 0, yTrack: 0, xSpan: 1, ySpan: 1 } },
      },
    ],
    selectedPageId: "page1",
  };
}

const gridState = singleWidgetState("1", {
  type: "grid",
  dataMapping: {
    cols: [
      { id: "medals.country" },
      { id: "medals.gold", aggregation: "sum" },
      { id: "medals.silver", aggregation: "sum" },
      { id: "medals.bronze", aggregation: "sum" },
    ],
  },
});

const chartState = singleWidgetState("2", {
  type: "column-chart-grouped",
  dataMapping: {
    categoryKey: [{ id: "medals.country" }],
    valueKey: [
      { id: "medals.gold", aggregation: "sum" },
      { id: "medals.silver", aggregation: "sum" },
      { id: "medals.bronze", aggregation: "sum" },
    ],
    tooltipKey: [],
  },
});

// Remove the spacing around the canvas so the widget fills its instance edge to edge.
const theme = studioTheme.withParams({ studioWrapperSpacing: 0 });

// View mode, no panels, a single-cell layout - one widget filling the whole canvas.
const baseProperties: AgStudioProperties = {
  mode: "view",
  panels: {},
  layout: {
    minWidth: 300,
    height: 300,
    columns: 1,
    rowHeight: 300,
    pagePadding: 0,
    widgetPadding: 0,
  },
  theme,
};

let gridApi: AgStudioApi;
let chartApi: AgStudioApi;
let dataEngine: AgDataEngine;

gridApi = createStudio(document.querySelector<HTMLElement>("#myStudio1")!, {
  ...baseProperties,
  initialState: gridState,
});
chartApi = createStudio(document.querySelector<HTMLElement>("#myStudio2")!, {
  ...baseProperties,
  initialState: chartState,
});

fetch("https://www.ag-grid.com/studio/example-assets/olympic-winners.json")
  .then((response) => response.json())
  .then((data) => {
    // One engine, shared by both instances - the data is loaded and cached once.
    dataEngine = createDataEngine({ sources: [{ id: "medals", data }] });
    gridApi.setProperty("data", dataEngine);
    chartApi.setProperty("data", dataEngine);
  });
```

[Live example: Single Widgets](https://www.ag-grid.com/studio/examples/sharing-caching-data/single-widgets/typescript/)

To show a single widget, give the report a one-cell layout and hide the panels:

```js
const studioProperties = {
    mode: 'view',
    panels: {},
    layout: { columns: 1, height: 300, rowHeight: 300, pagePadding: 0, widgetPadding: 0 },

    // other studio properties ...
}
```

Each instance is independent. Panels belong to a single instance, so one panel cannot control several instances. Only the data engine is shared.

## Custom Engines

For larger datasets or when you want to delegate query execution to a backend you already own, see the [Server-Side Data](https://www.ag-grid.com/studio/javascript/server-side-data/) guide.
