Data Sources

A chart can take its data from a Data Source, a place outside the chart: a CSV or JSON file at a URL, a table in the same document, or a provider you write.
New tab

The chart stores the address, which says where the data comes from, and a snapshot, a copy of the data it last read. The chart draws from the snapshot, so it draws offline, in an export and for people who cannot reach the source.

Quick start

editor.commands.linkChartSource({
  provider: 'url-csv',
  address: { url: 'https://example.com/sales.csv' },
  readOnly: true,
})

Stored in the document

AttributeWhat
source{ provider, address, mode, readOnly }
syncedAtThe ISO time the data was last taken from the source, or null

listCharts() reports both.

Linked or detached

mode, 'linked' or 'detached', says whether the chart follows its source.

Linked (mode: 'linked'). The snapshot is read from the source by linkChartSource, syncChartData, requestSync, relinkChartSource, and a provider that pushes changes with subscribe (see Writing a provider). In between, the chart shows the last snapshot. A chart linked with readOnly: false can also be edited by hand.

Detached (mode: 'detached'). detachChartSource stops following the source and keeps the snapshot and the address, so you can edit the data again. relinkChartSource follows the source again and replaces the data with what the source returns.

Read-only

readOnly is a separate flag. It only has an effect while the chart is linked. With readOnly: true, commands that change the data return false, so the next synchronization never overwrites a local edit. The Chart Spec stays editable. Detaching keeps the flag as it is, and it acts again when you relink.

Showing only some columns

A synchronization replaces all columns. To show only some of them, set encoding with updateChartSpec. It survives every synchronization.

editor.commands.updateChartSpec({ spec: { encoding: { label: 'region', series: ['revenue'] } } })

When a chart synchronizes

A synchronization reads the source and writes a new snapshot into the chart.

  • The snapshot is left out of the undo history, so undo never takes back a synchronization together with your edits.
  • Every synchronization sets syncedAt, also when the data is the same. The document then records when the data was last checked. In a version comparison, that counts as a change, see Versions.
  • When a source cannot be read, the chart keeps its snapshot. The extension calls the onSourceError option with the error and { provider, address }.
  • Every client that synchronizes needs the provider registered. A client without it shows the snapshot and cannot read the source.

To synchronize every chart that reads a URL:

await editor.storage.diagrams.requestSync({ provider: 'url-csv', address: { url } })

The extension does not synchronize on a schedule. Your app can, for example with setInterval. Stay within the limits of the service you read.

Built-in providers

ProviderAddressReads
url-csv{ url } or a URL stringCSV, through the fetch option
url-json{ url } or a URL stringJSON: an array of objects, an array of arrays, or Chart Data
document-table{ tableId }A table in the same document

Reading a URL

A URL must be absolute and use http: or https:. When you set the allowSourceUrl option, the URL must also pass it. The server must allow your app's web page to read the file (CORS). A published Google Sheet does.

The read fails, and the chart keeps its data, in these cases:

  • The address is not such a URL.
  • The server answers with an error status.
  • The request is blocked.
  • The answer is an HTML page instead of a file. Many hosts send one with status 200 for an address they do not know.

Reading a table

A chart that follows a table in the document uses document-table, see Tables.

Writing a provider

A provider that reads a weather API and fills a chart with the next seven days:

New tab

A provider is an object with an id and a read function. Pass it in the sources option, or add it at run time with editor.storage.diagrams.registerSource(provider):

import type { ChartSourceProvider } from '@tiptap-pro/extension-diagrams'

const salesApi: ChartSourceProvider = {
  id: 'sales-api',
  read: async (address, { fetch }) => {
    const { region } = address as { region: string }
    const response = await fetch(`/api/sales?region=${encodeURIComponent(region)}`)
    const rows: { month: string; total: number }[] = await response.json()
    return {
      columns: [
        { id: 'month', label: 'Month', role: 'label' },
        { id: 'total', label: 'Total', role: 'series' },
      ],
      rows: rows.map((row) => [row.month, row.total]),
    }
  },
  describe: (address) => `Sales, ${(address as { region: string }).region}`,
}
MemberRequiredDescription
idYesWhat a chart stores as provider
read(address, context)YesReturns Chart Data, or a Promise of it. Throw to report an error
subscribe(address, onChange, context)NoFor a source that pushes changes: call onChange(data) with every new state and return a function that stops
describe(address)NoA label for a user interface
  • context has fetch, decimalSeparator and editor. When your app sets allowSourceUrl, it also has allowUrl.
  • To report an error, throw a ChartSourceError with a message a person can read.
  • The extension keeps one subscription per linked address and writes each change into every chart linked to it.

The document stores the address and shares it with everyone who opens the document. Never put credentials in it. A provider that needs a key or a token takes it from your app when you create the provider.

Google Sheets, Excel and Airtable has a provider for each of these services, with what each service needs set up.

Commands

linkChartSource

Links a chart to a Data Source.

editor.commands.linkChartSource({
  provider: 'url-csv',
  address: { url: 'https://example.com/sales.csv' },
  readOnly: true,
})
OptionTypeDescription
providerstringThe id of a registered provider, such as 'url-csv'
addressunknownWhere the provider reads, in the form it expects
readOnlybooleanWhile linked, the data changes only through the source. Default false
dataChartDataThe first snapshot. Without it, the source is read and the snapshot is written when it arrives
posnumberThe chart's position. Defaults to the selected chart

Returns false for an unknown provider.

syncChartData

Writes a new snapshot.

editor.commands.syncChartData({
  source: { provider: 'url-csv', address: { url: 'https://example.com/sales.csv' } },
})
OptionTypeDescription
dataChartDataThe new snapshot. Without it, the source is read first
source{ provider, address }Writes into every chart linked to that source. Without it, one chart
posnumberThe chart's position. Defaults to the selected chart

Returns false without a matching chart, or without data for a chart that has no source.

detachChartSource

Stops following the source, and keeps the snapshot and the address.

editor.commands.detachChartSource()
OptionTypeDescription
posnumberThe chart's position. Defaults to the selected chart

Returns false for a chart without a source.

relinkChartSource

Follows the source again and reads it.

editor.commands.relinkChartSource()
OptionTypeDescription
posnumberThe chart's position. Defaults to the selected chart

Returns false for a chart without a source.