---
title: "Sharing & Caching Data"
framework: react
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

```tsx
"use client";

import type {
  AgDataEngine,
  AgDataSourcesDefinition,
  AgReportState,
} from "ag-studio";
import { createDataEngine } from "ag-studio";
import type { AgStudioRef } from "ag-studio-react";
import { AgStudio } from "ag-studio-react";
import React, {
  StrictMode,
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from "react";
import { createRoot } from "react-dom/client";

const StudioExample = () => {
  const studioRef = useRef<AgStudioRef>(null);
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const studioStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
  const [data, setData] = useState<AgDataSourcesDefinition | AgDataEngine>();
  const [created, setCreated] = useState<boolean>(true);
  const initialState = useMemo<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,
        },
      },
    }),
    [],
  );

  useEffect(() => {
    fetch("https://www.ag-grid.com/studio/example-assets/olympic-winners.json")
      .then((resp) => resp.json())
      .then((data: any[]) =>
        setData(createDataEngine({ sources: [{ id: "medals", data }] })),
      );
  }, []);

  const recreate = useCallback(() => {
    setCreated((currentCreated) => !currentCreated);
  }, []);

  return (
    <div style={containerStyle}>
      <div style={{ display: "flex", flexDirection: "column", height: "100%" }}>
        <div className="example-controls">
          <div className="controls-row">
            <button onClick={recreate}>
              {created
                ? "Destroy Studio Instance"
                : "Recreate with Data Engine"}
            </button>
          </div>
        </div>

        {created && (
          <AgStudio
            ref={studioRef}
            style={studioStyle}
            className="my-studio-container"
            data={data}
            initialState={initialState}
            mode={"edit"}
          />
        )}
        {!created && (
          <div className="my-studio-container">No current Studio instance</div>
        )}
      </div>
    </div>
  );
};

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

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

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

```jsx
const data = useMemo(() => { 
	return dataEngine;
}, []);

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

See [Sync Data](https://www.ag-grid.com/studio/react/sync-data/) and [Async Data](https://www.ag-grid.com/studio/react/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

```tsx
"use client";

import type { AgDataEngine, AgReportState, AgWidgetState } from "ag-studio";
import { createDataEngine, studioTheme } from "ag-studio";
import { AgStudio } from "ag-studio-react";
import React, { StrictMode, useEffect, useMemo, useState } from "react";
import { createRoot } from "react-dom/client";

// 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 StudioExample = () => {
  const [dataEngine, setDataEngine] = useState<AgDataEngine>();

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

  const gridState = useMemo<AgReportState>(
    () =>
      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 = useMemo<AgReportState>(
    () =>
      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: [],
        },
      }),
    [],
  );

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

  return (
    <div className="single-widgets">
      <AgStudio
        className="single-widget"
        mode="view"
        panels={{}}
        layout={{
          minWidth: 300,
          height: 300,
          columns: 1,
          rowHeight: 300,
          pagePadding: 0,
          widgetPadding: 0,
        }}
        theme={theme}
        initialState={gridState}
        data={dataEngine}
      />
      <div className="app-area">Your Application</div>
      <AgStudio
        className="single-widget"
        mode="view"
        panels={{}}
        layout={{
          minWidth: 300,
          height: 300,
          columns: 1,
          rowHeight: 300,
          pagePadding: 0,
          widgetPadding: 0,
        }}
        theme={theme}
        initialState={chartState}
        data={dataEngine}
      />
    </div>
  );
};

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

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

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

```jsx
const [mode, setMode] = useState('view');
const panels = {};
const layout = { columns: 1, height: 300, rowHeight: 300, pagePadding: 0, widgetPadding: 0 };

<AgStudio
    mode={mode}
    panels={panels}
    layout={layout}
/>
```

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/react/server-side-data/) guide.
