> ## Documentation Index > Fetch the complete documentation index at: https://www.canva.dev/docs/apps/llms.txt > Use this file to discover all available pages before exploring further. # Request data table Request a data table from the app's own Data Connector using requestDataTable. ## Running this example To run this example locally: 1. If you haven't already, create a new app in the [Developer Portal](https://www.canva.com/developers/apps). For more information, refer to our [Quickstart guide](https://www.canva.dev/docs/apps/quickstart/). 2. In your app's configuration on the [Developer Portal](https://www.canva.com/developers/apps), ensure the "Development URL" is set to `http://localhost:8080`. 3. Clone the starter kit: ```shell git clone https://github.com/canva-sdks/canva-apps-sdk-starter-kit.git cd canva-apps-sdk-starter-kit ``` 4. Install dependencies: ```shell npm install ``` 5. Run the example: ```shell npm run start:example request_data_table ``` 6. Click the **Preview URL** link shown in the terminal to open the example in the Canva editor. ## Example app source code ```json { "$schema": "https://www.canva.dev/schemas/app/v1/manifest-schema.json", "manifest_schema_version": 1, "runtime": { "permissions": [ { "name": "canva:design:content:read", "type": "mandatory" }, { "name": "canva:design:content:write", "type": "mandatory" } ] }, "intent": { "data_connector": { "enrolled": true }, "design_editor": { "enrolled": true } } } ``` ```typescript // For usage information, see the README.md file. import type { ColumnConfig, DataTable, DataTableRow, } from "@canva/intents/data"; export type TeamMember = { name: string; role: string; location: string; }; // A small hardcoded dataset. A real Data Connector would fetch data like this // from an external API, using the request's `dataSourceRef` to identify what // to fetch. export const TEAM_MEMBERS: TeamMember[] = [ { name: "Ana Silva", role: "Product Designer", location: "Sydney" }, { name: "Wei Chen", role: "Engineer", location: "Singapore" }, { name: "Priya Nair", role: "Engineer", location: "Bengaluru" }, { name: "Tom Baker", role: "Marketing Lead", location: "London" }, { name: "Sofia Reyes", role: "Customer Success", location: "Austin" }, ]; const COLUMN_CONFIGS: ColumnConfig[] = [ { name: "Name", type: "string" }, { name: "Role", type: "string" }, { name: "Location", type: "string" }, ]; export function buildDataTable(members: TeamMember[]): DataTable { const rows: DataTableRow[] = members.map((member) => ({ cells: [ { type: "string", value: member.name }, { type: "string", value: member.role }, { type: "string", value: member.location }, ], })); return { columnConfigs: COLUMN_CONFIGS, rows }; } ``` ```typescript // For usage information, see the README.md file. // This root index file contains the prepare function calls that initialize each intent. // Each intent entrypoint is responsible for exporting the intent contract implementation. import { prepareDataConnector } from "@canva/intents/data"; import { prepareDesignEditor } from "@canva/intents/design"; import dataConnector from "./intents/data_connector"; import designEditor from "./intents/design_editor"; prepareDataConnector(dataConnector); prepareDesignEditor(designEditor); ``` ```typescript // For usage information, see the README.md file. import type { DataConnectorIntent, GetDataTableRequest, GetDataTableResponse, RenderSelectionUiRequest, } from "@canva/intents/data"; import { AppUiProvider } from "@canva/app-ui-kit"; import "@canva/app-ui-kit/styles.css"; import { createRoot } from "react-dom/client"; import { buildDataTable, TEAM_MEMBERS } from "../../data"; import { SelectionUI } from "./selection_ui"; /** * Returns the app's fixed team-directory dataset. * * A real Data Connector would use `request.dataSourceRef.source` to identify * which data to fetch from an external source. */ async function getDataTable( request: GetDataTableRequest, ): Promise { return { status: "completed", dataTable: buildDataTable(TEAM_MEMBERS), metadata: { description: "Team directory", providerInfo: { name: "Team Directory" }, }, }; } function renderSelectionUi(request: RenderSelectionUiRequest) { const root = createRoot(document.getElementById("root") as Element); root.render( , ); } const dataConnector: DataConnectorIntent = { getDataTable, renderSelectionUi }; export default dataConnector; ``` ```typescript // For usage information, see the README.md file. import { Alert, Button, Rows, Text, Title } from "@canva/app-ui-kit"; import type { RenderSelectionUiRequest } from "@canva/intents/data"; import { useState } from "react"; import * as styles from "styles/components.css"; import { TEAM_MEMBERS } from "../../data"; const TEAM_DATA_SOURCE_REF = { source: "team_members", title: "Team members", }; export function SelectionUI({ updateDataRef }: RenderSelectionUiRequest) { const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const handleLoadData = async () => { setIsLoading(true); setError(null); const result = await updateDataRef(TEAM_DATA_SOURCE_REF); if (result.status !== "completed") { setError("Failed to load data."); } setIsLoading(false); }; return (
Team directory A fixed set of {TEAM_MEMBERS.length} team members. Loading them makes this table available to `requestDataTable` in the design editor. {error && {error}}
); } ```
```typescript // For usage information, see the README.md file. import { Alert, Button, Rows, Text, Title } from "@canva/app-ui-kit"; import type { DataTable } from "@canva/intents/data"; import { requestDataTable } from "@canva/intents/data"; import { useState } from "react"; import * as styles from "styles/components.css"; export function App() { const [dataTable, setDataTable] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const handleGetDataTable = async () => { setIsLoading(true); setError(null); try { // Opens the selection UI for this app's own Data Connector (see // intents/data_connector/index.tsx) and resolves with the DataTable the // user loads there. const table = await requestDataTable({ withDataConnector: "self", dataSelectionDisplay: "show", }); setDataTable(table); } catch (err) { setError(err instanceof Error ? err.message : "Failed to get data."); } finally { setIsLoading(false); } }; return (
Request data table Calls requestDataTable to open this app's own Data Connector and fetch the table the user loads. {error && {error}} {dataTable && }
); } function DataTablePreview({ dataTable }: { dataTable: DataTable }) { const columnNames = dataTable.columnConfigs?.map((c) => c.name ?? "") ?? []; return ( {dataTable.rows.length} row{dataTable.rows.length !== 1 ? "s" : ""}{" "} returned
{columnNames.length > 0 && ( {columnNames.map((name, i) => ( ))} )} {dataTable.rows.map((row, rowIndex) => ( {row.cells.map((cell, cellIndex) => ( ))} ))}
{name}
{cell.type === "string" ? (cell.value ?? "") : ""}
); } ```
```typescript // For usage information, see the README.md file. import "@canva/app-ui-kit/styles.css"; import type { DesignEditorIntent } from "@canva/intents/design"; import { AppUiProvider } from "@canva/app-ui-kit"; import { createRoot } from "react-dom/client"; import { App } from "./app"; async function render() { const root = createRoot(document.getElementById("root") as Element); root.render( , ); } const designEditor: DesignEditorIntent = { render }; export default designEditor; // Hot Module Replacement for development (automatically reloads the app when changes are made) if (module.hot) { module.hot.accept("./app", render); } ``` ```markdown # Request data table This example demonstrates how to use `requestDataTable` from `@canva/intents/data` to fetch a `DataTable` from an app's own Data Connector directly from the design editor intent, without the user having to leave the design editor experience. The app implements two intents: - **Data Connector** (`intents/data_connector`) — exposes a small, fixed team-directory dataset as a Data Connector data source. - **Design Editor** (`intents/design_editor`) — a single "Get data" button that calls `requestDataTable({ withDataConnector: "self", dataSelectionDisplay: "show" })`, which opens the Data Connector's selection UI and resolves with the `DataTable` the user loads, then renders it as a simple table. For API reference docs and instructions on running this example, see: . Related examples: see `intents/data_connector_intent` for a fuller Data Connector implementation (search, filtering, multiple data sources), and `intents/implement_multiple_intents` for the general pattern of combining multiple intents in one app. NOTE: This example differs from what is expected for public apps to pass a Canva review: - **Preview/beta API**: `requestDataTable` is a `@beta` API. Check the latest API status in the docs before relying on it in a production app. - **Static/hardcoded data**: The Data Connector always returns the same fixed dataset. Production apps should use `request.dataSourceRef.source` to identify and fetch the actual data requested from an external source. - **Localization**: Text content is hardcoded in English. Production apps require proper internationalization using the `@canva/app-i18n-kit` package for multi-language support. - **Error handling**: Error handling is simplified for demonstration. Production apps must implement comprehensive error handling with clear user feedback and graceful failure modes. ```
## API reference * [App UI Kit](https://www.canva.dev/docs/apps/app-ui-kit/) * [`prepareDataConnector`](https://www.canva.dev/docs/apps/api/preview/intents-data-prepare-data-connector/) * [`prepareDesignEditor`](https://www.canva.dev/docs/apps/api/preview/intents-design-prepare-design-editor/) * [`requestDataTable`](https://www.canva.dev/docs/apps/api/preview/intents-data-request-data-table/) ## Need help? * Join our [Community Forum](https://community.canva.dev/) * Report issues with this example on [GitHub](https://github.com/canva-sdks/canva-apps-sdk-starter-kit/issues)