Help / Diagrams and charts

Chart.js charts

Every core kind of chart, written the way a chart block reads it. Each example can be pasted into a block as it is.

A chart is a code block

A chart in Ubimate is an ordinary code block whose language is chartjs. Its text is the chart's configuration. A few languages are ones the app can draw as well as show, and this is one of them: instead of the text, the block can show the chart the text describes. Mermaid, on Diagrams and charts, is another.

There are three ways to make one:

  • Type /Chart.js and pick it from the slash menu.
  • At the start of an empty line, type ```chartjs and press Space or Enter, as for any other code block.
  • Turn a code block you already have into one: hover over it, open Select language on its toolbar and pick Chart.js.

A new block shows its text, empty. Write the configuration in it, or paste one of the examples below. Exported as Markdown, or copied into another app, the block is a fenced code block like any other, which is why it shows there as text:

```chartjs
const config = {
  type: 'bar',
  …
};
```

To switch between the text and the chart, hover over the block and use the button at its top right. The eye, Preview chart, draws the chart; the pencil, Edit JSON, goes back to the text. The block keeps whichever it was left showing, so a page opens with its charts drawn.

How a block reads a chart

A chart block holds one configuration: the kind of chart, its data and its options. It can be written as JSON, with every name in double quotes, or as JavaScript, the way the examples on this page are. In JavaScript the configuration needs a name, and config is the one the block looks for first:

const config = {
  type: 'bar',
  data: {
    labels: ['Q1', 'Q2', 'Q3', 'Q4'],
    datasets: [{
      label: 'Sales',
      data: [12, 19, 8, 15],
      backgroundColor: 'rgb(54, 162, 235)',
    }],
  },
};

Other names can stand for values you use more than once, as long as each is written above the place it is used:

const blue = 'rgb(54, 162, 235)';
const quarters = ['Q1', 'Q2', 'Q3', 'Q4'];

const config = {
  type: 'line',
  data: {
    labels: quarters,
    datasets: [{ label: 'Sales', data: [12, 19, 8, 15], borderColor: blue }],
  },
};

The configuration is read, never run. Write every value out in full: numbers, text, lists and objects. Arithmetic, functions and anything else that would need running are left out of the chart, along with the setting they belong to. What a block cannot do has the details.

A dataset with no colors of its own gets one from Chart.js's palette, a different one for each dataset. To choose them, set backgroundColor and borderColor. The examples here use the colors of the Chart.js documentation, as rgb() for lines and borders and as a half-transparent rgba() for fills.

A chart is the full width of the text and 360 pixels tall, whatever its options say about size. Its text and grid lines follow the app's light or dark theme.

Bar

One bar per label, and one color per dataset. With two datasets, the bars stand side by side:

const config = {
  type: 'bar',
  data: {
    labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
    datasets: [
      {
        label: '2025',
        data: [65, 59, 80, 81, 56, 55],
        backgroundColor: 'rgba(255, 99, 132, 0.5)',
        borderColor: 'rgb(255, 99, 132)',
        borderWidth: 1,
      },
      {
        label: '2026',
        data: [28, 48, 40, 19, 86, 27],
        backgroundColor: 'rgba(54, 162, 235, 0.5)',
        borderColor: 'rgb(54, 162, 235)',
        borderWidth: 1,
      },
    ],
  },
};

For bars that run sideways, add indexAxis: 'y' to the options. To stack the datasets on each other instead of side by side, stack both axes:

options: {
  indexAxis: 'y',
  scales: {
    x: { stacked: true },
    y: { stacked: true },
  },
},

options goes beside type and data, inside config. Rounded bars take borderRadius: 6 on the dataset, and a value below zero draws a bar down from the axis.

Line and area

The same shape as a bar chart, with type: 'line'. tension curves the line, from 0 for straight segments to about 0.4:

const config = {
  type: 'line',
  data: {
    labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
    datasets: [
      {
        label: 'This week',
        data: [12, 19, 14, 21, 17, 9, 11],
        borderColor: 'rgb(75, 192, 192)',
        backgroundColor: 'rgba(75, 192, 192, 0.5)',
        tension: 0.3,
      },
      {
        label: 'Last week',
        data: [10, 15, 16, 18, 20, 12, 8],
        borderColor: 'rgb(201, 203, 207)',
        backgroundColor: 'rgba(201, 203, 207, 0.5)',
        borderDash: [6, 4],
        tension: 0.3,
      },
    ],
  },
};

A few more settings on a line's dataset:

  • fill: true colors the area under the line with its backgroundColor, which makes it an area chart. fill: '-1' fills down to the dataset before it instead, and stacking the y axis stacks the areas.
  • borderDash: [6, 4] dashes the line: 6 pixels drawn, 4 skipped.
  • stepped: true draws steps instead of slopes.
  • pointRadius: 0 hides the dots; pointStyle: 'rect', 'triangle', 'star' and others change their shape.
  • null in the data leaves a gap in the line, and spanGaps: true joins across it.

Pie and doughnut

One dataset, and a color for each slice, in the order of the labels:

const config = {
  type: 'doughnut',
  data: {
    labels: ['Design', 'Build', 'Test', 'Launch'],
    datasets: [{
      label: 'Hours',
      data: [30, 50, 15, 5],
      backgroundColor: [
        'rgb(255, 99, 132)',
        'rgb(54, 162, 235)',
        'rgb(255, 205, 86)',
        'rgb(75, 192, 192)',
      ],
    }],
  },
};

type: 'pie' is the same chart with no hole. cutout: '70%' in the options sets the size of the hole, and rotation: -90 with circumference: 180 draws a half doughnut, a gauge. Clicking a label in the legend hides its slice.

Polar area

Like a pie, but every slice has the same angle and the value sets how far it reaches. Half-transparent colors keep the rings behind visible:

const config = {
  type: 'polarArea',
  data: {
    labels: ['Email', 'Chat', 'Phone', 'Forum', 'Other'],
    datasets: [{
      label: 'Requests',
      data: [11, 16, 7, 3, 14],
      backgroundColor: [
        'rgba(255, 99, 132, 0.5)',
        'rgba(75, 192, 192, 0.5)',
        'rgba(255, 205, 86, 0.5)',
        'rgba(201, 203, 207, 0.5)',
        'rgba(54, 162, 235, 0.5)',
      ],
    }],
  },
};

Radar

One spoke per label, for comparing a few things on the same measures. The scale of a radar chart is called r:

const config = {
  type: 'radar',
  data: {
    labels: ['Speed', 'Price', 'Support', 'Features', 'Design'],
    datasets: [
      {
        label: 'Option A',
        data: [8, 5, 7, 9, 6],
        borderColor: 'rgb(255, 99, 132)',
        backgroundColor: 'rgba(255, 99, 132, 0.2)',
        fill: true,
      },
      {
        label: 'Option B',
        data: [6, 9, 6, 5, 8],
        borderColor: 'rgb(54, 162, 235)',
        backgroundColor: 'rgba(54, 162, 235, 0.2)',
        fill: true,
      },
    ],
  },
  options: {
    scales: {
      r: { min: 0, max: 10 },
    },
  },
};

Scatter and bubble

For values on two numeric axes. There are no labels: every point is an object with its x and its y.

const config = {
  type: 'scatter',
  data: {
    datasets: [{
      label: 'Orders',
      data: [
        { x: 12, y: 340 },
        { x: 18, y: 410 },
        { x: 25, y: 520 },
        { x: 31, y: 480 },
        { x: 40, y: 690 },
      ],
      backgroundColor: 'rgb(153, 102, 255)',
    }],
  },
  options: {
    scales: {
      x: { title: { display: true, text: 'Temperature (°C)' } },
      y: { title: { display: true, text: 'Ice creams sold' } },
    },
  },
};

A bubble chart is a scatter chart with a third value, r, the radius of each bubble in pixels:

const config = {
  type: 'bubble',
  data: {
    datasets: [{
      label: 'Projects',
      data: [
        { x: 20, y: 30, r: 15 },
        { x: 40, y: 10, r: 10 },
        { x: 30, y: 22, r: 25 },
      ],
      backgroundColor: 'rgba(255, 159, 64, 0.5)',
      borderColor: 'rgb(255, 159, 64)',
    }],
  },
};

Mixed charts

A dataset can be a different kind of chart from the rest by giving it a type of its own: bars with a line over them, say. Here the line also has an axis of its own on the right, named by yAxisID:

const config = {
  type: 'bar',
  data: {
    labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
    datasets: [
      {
        label: 'Revenue',
        data: [12000, 15000, 11000, 18000, 21000, 19000],
        backgroundColor: 'rgba(54, 162, 235, 0.5)',
      },
      {
        type: 'line',
        label: 'Customers',
        data: [80, 95, 70, 120, 140, 130],
        borderColor: 'rgb(255, 99, 132)',
        backgroundColor: 'rgb(255, 99, 132)',
        yAxisID: 'y1',
      },
    ],
  },
  options: {
    scales: {
      y: { beginAtZero: true },
      y1: {
        position: 'right',
        beginAtZero: true,
        grid: { drawOnChartArea: false },
      },
    },
  },
};

Bar and line mix this way, as do bar or line with scatter and bubble.

Titles, legends and axes

Most of a chart's look is in options. The settings people reach for first, together:

options: {
  plugins: {
    title: { display: true, text: 'Sales by quarter' },
    subtitle: { display: true, text: 'In thousands of euros' },
    legend: { position: 'bottom' },
    tooltip: { mode: 'index', intersect: false },
  },
  scales: {
    x: { title: { display: true, text: 'Quarter' } },
    y: {
      beginAtZero: true,
      max: 100,
      ticks: { stepSize: 20 },
      title: { display: true, text: 'Sales' },
    },
  },
},
  • legend: { display: false } hides the legend; its position can be 'top', 'bottom', 'left' or 'right'.
  • tooltip with mode: 'index' shows every dataset's value for the label under the pointer, not only the one touched.
  • beginAtZero, min and max fix an axis's range; suggestedMin and suggestedMax widen it without cutting data off.
  • type: 'logarithmic' on an axis spaces it by powers of ten.
  • grid: { display: false } on an axis hides its grid lines.
  • animation: false draws the chart at once, without its entrance.

From the Chart.js samples

The samples in the Chart.js documentation do not paste into a block as they are. Each is split into tabs, and its data is made by code that only exists on that website. To bring one across:

  1. Copy the Setup tab first, then the Config tab under it. Setup makes data, which Config uses, so it has to come first.
  2. Leave out the Actions tab. It is the code behind the sample's buttons.
  3. Replace every Utils call with the values it stands for, as in the table below. Numbers from Utils.numbers are random: write your own.
  4. Leave out functions: callback, anything written (ctx) => …, and settings computed from others.
In the sample In the block
Utils.months({ count: 7 }) ['January', 'February', … 'July']
Utils.numbers(NUMBER_CFG) [12, -30, 45, 8, 70, -12, 33], one per label
Utils.CHART_COLORS.red 'rgb(255, 99, 132)'
Utils.transparentize(Utils.CHART_COLORS.red, 0.5) 'rgba(255, 99, 132, 0.5)'

The rest of Utils.CHART_COLORS:

orange 'rgb(255, 159, 64)'
yellow 'rgb(255, 205, 86)'
green 'rgb(75, 192, 192)'
blue 'rgb(54, 162, 235)'
purple 'rgb(153, 102, 255)'
grey 'rgb(201, 203, 207)'

The documentation's reference pages are still the place for every option a chart has; the options are the same in a block.

What a block cannot do

Nothing in a chart block is run, so a chart that arrives with a shared page cannot do anything but draw. That rules out:

  • Functions. A callback that formats a tick or a tooltip, and options that change point by point, are left out. Put units in an axis title instead: 'Revenue (€)'.
  • Computed values. 10 * 2, .map(), new Date() and a template string with a value inserted into it are left out, and so is what holds them: one computed number drops its whole list of data.
  • Names used before they are written. A setting that uses a name the block has not read yet is left out.
  • Time axes. type: 'time' and 'timeseries' need a date library the app does not include. Write dates as labels instead, such as '3 Mar'.
  • Gradients and patterns, which are drawn with code.
  • Plugins outside Chart.js itself, such as data labels, zoom or annotations. Their options are ignored.

What was left out is left out silently: the chart draws without it. When a setting seems to have no effect, check it against this list. A mistake that stops the chart altogether, such as a missing bracket, shows its error in red where the chart would be.