Skip to main content
Default Gray Amethyst

Line Chart

<mo-line-chart> | MOLineChart
Since 1.3 stable

Line charts are used to represent data points in a series.

Line chart is used to display continuous data, and it is often used to visualize a trend in data over time. By overlaying multiple data entries in one chart, it can present the relationship between values.

<mo-line-chart subtitle="Data from Weather Spark" title="Avg. temp. by month" id="first-example"></mo-line-chart>

<script>
  const chart = document.querySelector('#first-example');
  const labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
  const helsinki = [-4, -5, -1, 4, 10, 14, 18, 16, 11, 6, 2, -2];
  const stockholm = [-2, -2, 1, 5, 11, 15, 18, 16, 12, 7, 3, -1];
  const paris = [4, 5, 8, 11, 14, 18, 20, 20, 16, 12, 8, 5];
  const rome = [7, 8, 11, 13, 18, 22, 25, 25, 21, 17, 12, 8];

  const datasets = [
    { label: 'Helsinki', data: helsinki, tension: 0.4 },
    { label: 'Stockholm', data: stockholm, tension: 0.4 },
    { label: 'Paris', data: paris, tension: 0.4 },
    { label: 'Rome', data: rome, tension: 0.4 }
  ];
  chart.labels = labels;
  chart.datasets = datasets;
</script>
import { MOLineChart } from '@metsooutotec/modes-web-components/dist/react';

const labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const helsinki = [-4, -5, -1, 4, 10, 14, 18, 16, 11, 6, 2, -2];
const stockholm = [-2, -2, 1, 5, 11, 15, 18, 16, 12, 7, 3, -1];
const paris = [4, 5, 8, 11, 14, 18, 20, 20, 16, 12, 8, 5];
const rome = [7, 8, 11, 13, 18, 22, 25, 25, 21, 17, 12, 8];

const datasets = [
  { label: 'Helsinki', data: helsinki, tension: 0.4 },
  { label: 'Stockholm', data: stockholm, tension: 0.4 },
  { label: 'Paris', data: paris, tension: 0.4 },
  { label: 'Rome', data: rome, tension: 0.4 }
];

const App = () => (
  <MOLineChart
    datasets={datasets}
    labels={labels}
    subtitle="Data from Weather Spark"
    title="Avg. temp. by month"
  ></MOLineChart>
);

Examples

Area chart

A line chart can also function as an area chart. Adding the boolean property area to the chart and configuring the datasets to fill the area is enough, as shown in the example. For all fill options, check Chart.js documentation.

<mo-line-chart area subtitle="Data from Weather Spark" title="Avg. temp. by month" id="area"></mo-line-chart>

<script>
  const chart = document.querySelector('#area');
  const labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
  const helsinki = [-4, -5, -1, 4, 10, 14, 18, 16, 11, 6, 2, -2];
  const paris = [4, 5, 8, 11, 14, 18, 20, 20, 16, 12, 8, 5];

  const datasets = [
    { label: 'Helsinki', data: helsinki, tension: 0.4, fill: 'start' },
    { label: 'Paris', data: paris, tension: 0.4, fill: 'start' }
  ];
  const extraOptions = {
    scales: {
      y: {
        min: -10,
        max: 25
      }
    }
  };
  chart.labels = labels;
  chart.options = extraOptions;
  chart.datasets = datasets;
</script>
import { MOLineChart } from '@metsooutotec/modes-web-components/dist/react';

const labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const helsinki = [-4, -5, -1, 4, 10, 14, 18, 16, 11, 6, 2, -2];
const paris = [4, 5, 8, 11, 14, 18, 20, 20, 16, 12, 8, 5];

const datasets = [
  { label: 'Helsinki', data: helsinki, tension: 0.4, fill: 'start' },
  { label: 'Paris', data: paris, tension: 0.4, fill: 'start' }
];

const extraOptions = {
  scales: {
    y: {
      min: -10,
      max: 25
    }
  }
};

const App = () => (
  <MOLineChart
    area
    datasets={datasets}
    labels={labels}
    options={extraOptions}
    subtitle="Data from Weather Spark"
    title="Avg. temp. by month"
  ></MOLineChart>
);

Extra options & subtitle

The line chart comes with a ton of extra options that can be configured, courtery of Chart.js. In this case the chart has multiple y axes and a subtitle.

By default the charts have an aspect ratio of 1:2, and they maintain this aspect ratio. You can use the option maintainAspectRatio: false if you wish to use another aspect ratio, or specify a specific one using the aspectRatio option.

<mo-line-chart subtitle="Data from Weather Spark" title="Climate in Helsinki" id="options"></mo-line-chart>

<script>
  const chart = document.querySelector('#options');
  const labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
  const temps = [-4, -5, -1, 4, 10, 14, 18, 16, 11, 6, 2, -2];
  const rainfalls = [19.7, 13.7, 17.6, 28.0, 35.5, 47.1, 44.1, 53.2, 52.6, 56.3, 45.8, 27.4];
  const datasets = [
    { label: 'Temp.', data: temps, yAxisID: 'A' },
    { label: 'Rainfall', data: rainfalls, yAxisID: 'B' }
  ];

  const extraOptions = {
    maintainAspectRatio: false,
    scales: {
      y: {
        display: false
      },
      A: {
        grid: {
          borderColor: '#6730BF'
        },
        display: true,
        title: {
          display: true,
          text: 'Temperature',
          color: '#6730BF',
          font: {
            family: 'GT-Eesti'
          }
        },
        type: 'linear',
        position: 'left',
        ticks: {
          callback: function (value, index, ticks) {
            return value + ' °C';
          }
        }
      },
      B: {
        grid: {
          borderColor: '#16A889'
        },
        type: 'linear',
        position: 'right',
        title: {
          display: true,
          text: 'Rainfall',
          color: '#16A889',
          font: {
            family: 'GT-Eesti'
          }
        },
        ticks: {
          callback: function (value, index, ticks) {
            return value + ' mm';
          }
        }
      }
    }
  };
  chart.labels = labels;
  chart.datasets = datasets;
  chart.options = extraOptions;
</script>
<style>
  #options::part(base) {
    position: relative;
    max-height: 200px;
  }
  #options::part(canvas) {
    max-height: 200px;
  }
</style>
import { MOLineChart } from '@metsooutotec/modes-web-components/dist/react';

const css = `
  #options::part(base) {
    position: relative;
    max-height: 200px;
  }
  #options::part(canvas) {
    max-height: 200px;
  }
`;

const labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const temps = [-4, -5, -1, 4, 10, 14, 18, 16, 11, 6, 2, -2];
const rainfalls = [19.7, 13.7, 17.6, 28.0, 35.5, 47.1, 44.1, 53.2, 52.6, 56.3, 45.8, 27.4];

const datasets = [
  { label: 'Temp.', data: temps, yAxisID: 'A', borderWidth: 1.5 },
  { label: 'Rainfall', data: rainfalls, yAxisID: 'B', borderWidth: 1.5 }
];

const extraOptions = {
  maintainAspectRatio: false,
  scales: {
    y: {
      display: false
    },
    A: {
      grid: {
        borderColor: '#6730BF',
      },
      display: true,
      title: {
        display: true,
        text: 'Temperature',
        color: '#6730BF',
        font: {
          family: 'GT-Eesti'
        }
      },
      type: 'linear',
      position: 'left',
      ticks: {
        callback: function (value, index, ticks) {
          return value + ' °C';
        }
      }
    },
    B: {
      grid: {
        borderColor: '#16A889'
      },
      type: 'linear',
      position: 'right',
      title: {
        display: true,
        text: 'Rainfall',
        color: '#16A889',
        font: {
          family: 'GT-Eesti'
        }
      },
      ticks: {
        callback: function (value, index, ticks) {
          return value + ' mm';
        }
      }
    }
  }
};

const App = () => (
  <MOLineChart
    options={extraOptions}
    labels={labels}
    subtitle="Data from Weather Spark"
    title="Avg. temp. by month"
    datasets={datasets}
  ></MOLineChart>
  <style>{css}</style>
);

Hiding the legends

You can also use the options property to hide the legends of the line chart.

<mo-line-chart subtitle="Data from Weather Spark" title="Avg. temp. by month" id="legend-example"></mo-line-chart>

<script>
  const chart = document.querySelector('#legend-example');
  const labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
  const helsinki = [-4, -5, -1, 4, 10, 14, 18, 16, 11, 6, 2, -2];
  const stockholm = [-2, -2, 1, 5, 11, 15, 18, 16, 12, 7, 3, -1];
  const paris = [4, 5, 8, 11, 14, 18, 20, 20, 16, 12, 8, 5];
  const rome = [7, 8, 11, 13, 18, 22, 25, 25, 21, 17, 12, 8];
  const options = {
    plugins: {
      htmlLegend: {
        hideLegends: true
      }
    }
  }

  const datasets = [
    { label: 'Helsinki', data: helsinki, tension: 0.4 },
    { label: 'Stockholm', data: stockholm, tension: 0.4 },
    { label: 'Paris', data: paris, tension: 0.4 },
    { label: 'Rome', data: rome, tension: 0.4 }
  ];
  chart.options = options;
  chart.labels = labels;
  chart.datasets = datasets;
</script>
import { MOLineChart } from '@metsooutotec/modes-web-components/dist/react';

const labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const helsinki = [-4, -5, -1, 4, 10, 14, 18, 16, 11, 6, 2, -2];
const stockholm = [-2, -2, 1, 5, 11, 15, 18, 16, 12, 7, 3, -1];
const paris = [4, 5, 8, 11, 14, 18, 20, 20, 16, 12, 8, 5];
const rome = [7, 8, 11, 13, 18, 22, 25, 25, 21, 17, 12, 8];

const options = {
  plugins: {
    htmlLegend: {
      hideLegends: true
    }
  }
}

const datasets = [
  { label: 'Helsinki', data: helsinki, tension: 0.4 },
  { label: 'Stockholm', data: stockholm, tension: 0.4 },
  { label: 'Paris', data: paris, tension: 0.4 },
  { label: 'Rome', data: rome, tension: 0.4 }
];

const App = () => (
  <MOLineChart
    datasets={datasets}
    options={options}
    labels={labels}
    subtitle="Data from Weather Spark"
    title="Avg. temp. by month"
  ></MOLineChart>
);

Zooming and panning

The line chart supports zooming and panning, and it can be enabled by simply enabling the property zoomable. Use the zoomOptions attribute to customize the zooming functionality.

Reset zoom
<mo-line-chart zoomable title="Financial results" id="zooming"></mo-line-chart>
<mo-button id="reset-zoom-btn">Reset zoom</mo-button>

<script>
  const chart = document.querySelector('#zooming');
  const resetBtn = document.querySelector('#reset-zoom-btn');
  const labels = ['Q1', 'Q2', 'Q3', 'Q4'];
  const data = [12, 19, 3, 5];
  const data2 = [7, 11, 6, 2];
  const data3 = [9, 14, 7, 3];
  const data4 = [10, 20, 15, 7];
  const datasets = [
    { label: 'Apple', data: data },
    { label: 'OnePlus', data: data2 },
    { label: 'Nokia', data: data3 },
    { label: 'Samsung', data: data4 }
  ];
  resetBtn.addEventListener('click', () => {
    chart.resetZoom();
  });

  chart.labels = labels;
  chart.datasets = datasets;
</script>
import { MOLineChart, MOButton } from '@metsooutotec/modes-web-components/dist/react';

const lineChartRef = useRef(null);
const labels = ['Q1', 'Q2', 'Q3', 'Q4'];
const data = [12, 19, 3, 5];
const data2 = [7, 11, 6, 2];
const data3 = [9, 14, 7, 3];
const data4 = [10, 20, 15, 7];
const datasets = [
  { label: 'Apple', data: data },
  { label: 'OnePlus', data: data2 },
  { label: 'Nokia', data: data3 },
  { label: 'Samsung', data: data4 }
];

const resetZoom = () => {
  lineChartRef.resetZoom();
};

const App = () => (
  <>
    <MOLineChart
      ref={lineChartRef}
      zoomable
      datasets={datasets}
      labels={labels}
      title="Financial results"
    ></MOLineChart>
    <MOButton onClick={resetZoom}>Reset zoom</MOButton>
  </>
);

Datasets & datapoints

Supplying the line chart with multiple data sets will result in multiple lines, with varying colors and data point shapes.

<mo-line-chart showDatapoints title="Financial results" id="multiple"></mo-line-chart>

<script>
  const chart = document.querySelector('#multiple');
  const labels = ['Q1', 'Q2', 'Q3', 'Q4'];
  const data = [12, 19, 3, 5];
  const data2 = [7, 11, 6, 2];
  const data3 = [9, 14, 7, 3];
  const data4 = [10, 20, 15, 7];
  const datasets = [
    { label: 'Apple', data: data },
    { label: 'OnePlus', data: data2 },
    { label: 'Nokia', data: data3 },
    { label: 'Samsung', data: data4 }
  ];

  chart.labels = labels;
  chart.datasets = datasets;
</script>
import { MOLineChart } from '@metsooutotec/modes-web-components/dist/react';

const labels = ['Q1', 'Q2', 'Q3', 'Q4'];
const data = [12, 19, 3, 5];
const data2 = [7, 11, 6, 2];
const data3 = [9, 14, 7, 3];
const data4 = [10, 20, 15, 7];
const datasets = [
  { label: 'Apple', data: data },
  { label: 'OnePlus', data: data2 },
  { label: 'Nokia', data: data3 },
  { label: 'Samsung', data: data4 }
];

const App = () => (
  <MOLineChart showDatapoints datasets={datasets} labels={labels} title="Financial results"></MOLineChart>
);

Extrapolated/missing data

Missing data should be indicated as either NaN or undefined this the regular data that is given to the chart. Extrapolated data should be given in its own extrapolatedData array in the individual dataset objects inside datasets. This separate set of data will then be internally appended to the regular data and styled differently.

<mo-line-chart
  extrapolation
  subtitle="With some missing data"
  title="Avg. temp. by month"
  id="extrapolation"
></mo-line-chart>

<script>
  const chart = document.querySelector('#extrapolation');
  const labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
  const helsinki = [-4, -5, -1, 4, 10, 14, 18, 16, 11, 6];
  const stockholm = [-2, -2, 1, 5, 11, 15, 18, 16, 12, 7];
  const paris = [4, 5, 8, 11, 14, 18, NaN, 20, 16];
  const rome = [7, 8, 11, 13, undefined, 22, 25, 25, 21, 17];
  const datasets = [
    { label: 'Helsinki', data: helsinki, extrapolatedData: [2, -2] },
    { label: 'Stockholm', data: stockholm, extrapolatedData: [3, -1] },
    { label: 'Paris', data: paris, extrapolatedData: [12, 8, 5] },
    { label: 'Rome', data: rome, extrapolatedData: [12, 8] }
  ];

  chart.labels = labels;
  chart.datasets = datasets;
</script>
import { MOLineChart } from '@metsooutotec/modes-web-components/dist/react';

const chart = document.querySelector('#extrapolation');
const labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const helsinki = [-4, -5, -1, 4, 10, 14, 18, 16, 11, 6];
const stockholm = [-2, -2, 1, 5, 11, 15, 18, 16, 12, 7];
const paris = [4, 5, 8, 11, 14, 18, NaN, 20, 16];
const rome = [7, 8, 11, 13, undefined, 22, 25, 25, 21, 17];
const datasets = [
  { label: 'Helsinki', data: helsinki, extrapolatedData: [2, -2] },
  { label: 'Stockholm', data: stockholm, extrapolatedData: [3, -1] },
  { label: 'Paris', data: paris, extrapolatedData: [12, 8, 5] },
  { label: 'Rome', data: rome, extrapolatedData: [12, 8] }
];

const App = () => (
  <MOLineChart
    extrapolation
    subtitle="With some missing data"
    title="Avg. temp. by month"
    datasets={datasets}
    labels={labels}
  ></MOLineChart>
);

Annotations

The charts in Modes UI can include annotations in the form of lines and boxes. See the full documentation for the plugin here.

<mo-line-chart title="Financial results" id="annotation"></mo-line-chart>

<script>
  const chart = document.querySelector('#annotation');
  const labels = ['Q1', 'Q2', 'Q3', 'Q4'];
  const data = [12, 19, 3, 5];
  const data2 = [7, 11, 6, 2];
  const data3 = [9, 14, 7, 3];
  const data4 = [10, 20, 15, 7];
  const datasets = [
    { label: 'Apple', data: data },
    { label: 'OnePlus', data: data2 },
    { label: 'Nokia', data: data3 },
    { label: 'Samsung', data: data4 }
  ];
  const options = {
    plugins: {
      annotation: {
        annotations: {
          line1: {
            type: 'line',
            yMin: 15,
            yMax: 15,
            borderColor: '#ff2a45',
            borderWidth: 1,
            borderDash: [5, 2]
          },
          line2: {
            type: 'line',
            yMin: 10,
            yMax: 10,
            borderColor: '#FB9613',
            borderWidth: 1,
            borderDash: [5, 2]
          }
        }
      }
    }
  };
  chart.options = options;
  chart.labels = labels;
  chart.datasets = datasets;
</script>

Changing data

The line chart will automatically update when the data changes, along with animations.

Add data Add dataset Reset data
<mo-line-chart showDatapoints title="Line chart" id="animations"></mo-line-chart>
<mo-divider></mo-divider>
<mo-button id="add-data">Add data</mo-button>
<mo-button id="add-dataset">Add dataset</mo-button>
<mo-button variant="secondary" id="reset-data">Reset data</mo-button>

<script>
  const chart = document.querySelector('#animations');
  const addData = document.querySelector('#add-data');
  const addDataset = document.querySelector('#add-dataset');
  const resetData = document.querySelector('#reset-data');
  // set initial data
  let labels = ['0', '1', '2', '3'];
  let data = [12, 19, 3, 5];
  let datasets = [{ label: 'Original dataset with really long text', data: data }];
  let index = 4;
  let datasetIndex = 1;
  chart.labels = labels;
  chart.datasets = datasets;
  // add data point(s)
  addData.addEventListener('click', () => {
    labels.push(`${index}`);
    for (let i = 0; i < datasets.length; i++) {
      datasets[i].data.push(Math.max(1, Math.floor(Math.random() * 20)));
    }
    index++;
    const newDatasets = [...datasets];
    chart.datasets = newDatasets;
  });
  // add new dataset
  addDataset.addEventListener('click', () => {
    datasets.push({
      label: `New dataset #${datasetIndex}`,
      data: Array.from({ length: labels.length }, () => Math.max(1, Math.floor(Math.random() * 20)))
    });
    index++;
    datasetIndex++;
    const newDatasets = [...datasets];
    chart.datasets = newDatasets;
  });
  // reset to initial data
  resetData.addEventListener('click', () => {
    data = [12, 19, 3, 5];
    labels = ['1', '2', '3', '4'];
    datasets = [{ label: 'Original dataset', data: data }];
    chart.datasets = datasets;
    chart.labels = labels;
    index = 4;
  });
</script>
import { MOLineChart } from '@metsooutotec/modes-web-components/dist/react';

const [labels, setLabels] = useState(['0', '1', '2', '3']);
const data = [12, 19, 3, 5];
const [datasets, setDatasets] = useState([{ label: 'Original dataset', data: data }]);
const [index, setIndex] = useState(4);
const [datasetIndex, setDatasetIndex] = useState(1);

const resetData = () => {
  setDatasets([{ label: 'Original dataset', data: [12, 19, 3, 5] }]);
  setLabels(['0', '1', '2', '3']);
  setDatasetIndex(1);
  setIndex(4);
};

const addData = () => {
  setLabels([...labels, `${index}`]);
  for (const i = 0; i < datasets.length; i++) {
    datasets[i].data.push(Math.max(1, Math.floor(Math.random() * 20)));
  }
  setIndex(index + 1);
  const newDatasets = [...datasets];
  setDatasets(newDatasets);
};

const addDataset = () => {
  datasets.push({
    label: `New dataset #${datasetIndex}`,
    data: Array.from({ length: labels.length }, () => Math.max(1, Math.floor(Math.random() * 20)))
  });
  setIndex(index + 1);
  setDatasetIndex(datasetIndex + 1);
  const newDatasets = [...datasets];
  setDatasets(newDatasets);
};

const App = () => (
  <>
    <MOLineChart showDatapoints labels={labels} title="Changing data" datasets={datasets}></MOLineChart>
    <MODivider></MODivider>
    <div>
      <MOButton onClick={addData}>Add data</MOButton>
      <MOButton onClick={addDataset}>Add dataset</MOButton>
      <MOButton onClick={resetData} variant="secondary">
        Reset data
      </MOButton>
    </div>
  </>
);

Custom plugins

Chart.js allows for custom plugins to further customize and add functionality to the chart. Use the custom-plugins attribute to set your own custom plugins. Use the options attribute to customize plugin arguments.

Hover status
not hovering

<mo-label>Hover status</mo-label>
<br/>
<span id="hover-status">not hovering</span>
<br/><br/>
<mo-line-chart title="Custom Plugin Example" id="custom-plugin"></mo-line-chart>

<script>
  const chart = document.querySelector('#custom-plugin');
  const labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
  const data = [12, 19, 3, 5, 2, 3, 9, 10, 15, 20, 25, 30];
  const datasets = [{ label: 'Sample Data', data: data }];

  const customPlugin = {
    id: 'hoverOutPlugin',
    afterEvent: (chart, args, options) => {
      const statusSpan = document.querySelector('#hover-status');
      const event = args.event;
      const mouseOutMessage = options.msg;
      if (event.type === 'mouseout') {
        statusSpan.classList.remove('hovering');
        statusSpan.textContent = 'not hovering'
      } else {
        statusSpan.classList.add('hovering');
        statusSpan.textContent = 'hovering'
      }
    }
  };

  const options = {
    plugins: {
      hoverOutPlugin: {
        msg: 'Hovered out of chart.'
      }
    }
  }

  chart.labels = labels;
  chart.datasets = datasets;
  chart.options = options;
  chart.customPlugins = [customPlugin];
</script>

<style>
  #hover-status {
    background-color: var(--mo-color-status-alert-container);
    border: 1px solid var(--mo-color-status-alert);
    color: var(--mo-color-status-alert-on-container);
    padding: 0.25rem 0.5rem;
  }
  #hover-status.hovering {
    background-color: var(--mo-color-status-success-container);
    color: var(--mo-color-status-success-on-container);
    border-color: var(--mo-color-status-success);
  }
</style>
import { MOLineChart } from '@metsooutotec/modes-web-components/dist/react';

const labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const data = [12, 19, 3, 5, 2, 3, 9, 10, 15, 20, 25, 30];
const datasets = [{ label: 'Sample Data', data: data }];

const customPlugin = {
  id: 'hoverOutPlugin',
  afterEvent: (chart, args, options) => {
    const statusSpan = document.querySelector('#hover-status');
    const event = args.event;
    const mouseOutMessage = options.msg;
    if (event.type === 'mouseout') {
      statusSpan.classList.remove('hovering');
      statusSpan.textContent = 'not hovering'
    } else {
      statusSpan.classList.add('hovering');
      statusSpan.textContent = 'hovering'
    }
  }
};

const options = {
  plugins: {
    hoverOutPlugin: {
      msg: 'Hovered out of chart.'
    }
  }
}

const App = () => (
  <MOLineChart
    datasets={datasets}
    labels={labels}
    customPlugins={[customPlugin]}
    options={options}
    title="Custom Plugin Example"
  ></MOLineChart>
);

Mixed charts (Line + Bar)

Multiple different charts can be rendered in the same view and mixed by defining an individual type for each dataset.

<mo-line-chart title="Financial results" id="mixed"></mo-line-chart>

<script>
  const chart = document.querySelector('#mixed');
  const labels = ['Q1', 'Q2', 'Q3', 'Q4'];
  const data = [12, 19, 3, 5];
  const data2 = [7, 11, 6, 2];
  const data3 = [9, 14, 7, 3];
  const datasets = [
    { label: 'Apple', data: data },
    { label: 'Nokia', data: data3 },
    { label: 'OnePlus', data: data2, type: 'bar' }
  ];
  chart.labels = labels;
  chart.datasets = datasets;
</script>
import { MOLineChart } from '@metsooutotec/modes-web-components/dist/react';

const labels = ['Q1', 'Q2', 'Q3', 'Q4'];
const data = [12, 19, 3, 5];
const data2 = [7, 11, 6, 2];
const data3 = [9, 14, 7, 3];
const datasets = [
  { label: 'Apple', data: data },
  { label: 'Nokia', data: data3 },
  { label: 'OnePlus', data: data2, type: 'bar' }
];

const App = () => <MOLineChart datasets={datasets} labels={labels} title="Financial results"></MOLineChart>;

Timeseries data and interpolation

The line chart can also show timeseries data, to do this set the options object as shown here. Cubic interpolation can be enabled by adding a tension property to the dataset.

<mo-line-chart zoomable title="Timeseries data" id="points"></mo-line-chart>

<script>
  const chart = document.querySelector('#points');
  function makeSeries(seriesCount, points) {
    return Array(seriesCount)
      .fill({
        data: []
      })
      .map(s => {
        return {
          data: Array(points)
            .fill(1)
            .map((d, i) => ({ x: new Date(Date.now() - i * 24 * 3600000), y: Math.max(250, Math.random() * 3000) }))
        };
      });
  }
  const data = makeSeries(3, 20);
  const options = {
    scales: {
      x: {
        position: 'bottom',
        type: 'time',
        ticks: {
          autoSkip: true,
          autoSkipPadding: 50,
          maxRotation: 0
        },
        time: {
          displayFormats: {
            hour: 'HH:mm',
            minute: 'HH:mm',
            second: 'HH:mm:ss'
          }
        }
      }
    }
  };
  const datasets = [
    { label: 'Random data', data: data[0].data, tension: 0.4 },
    { label: 'Random data', data: data[1].data, tension: 0.4 },
    { label: 'Random data', data: data[2].data, tension: 0.4 }
  ];
  chart.options = options;
  chart.datasets = datasets;
</script>
import { MOLineChart } from '@metsooutotec/modes-web-components/dist/react';

function makeSeries(seriesCount, points) {
  return Array(seriesCount)
    .fill({
      data: []
    })
    .map(s => {
      return {
        data: Array(points)
          .fill(1)
          .map((d, i) => ({ x: new Date(Date.now() - i * 24 * 3600000), y: Math.max(250, Math.random() * 3000) }))
      };
    });
}
const data = makeSeries(3, 15);
const options = {
  scales: {
    x: {
      position: 'bottom',
      type: 'time',
      ticks: {
        autoSkip: true,
        autoSkipPadding: 50,
        maxRotation: 0
      },
      time: {
        displayFormats: {
          hour: 'HH:mm',
          minute: 'HH:mm',
          second: 'HH:mm:ss'
        }
      }
    }
  }
};
const datasets = [
  { label: 'Random data', data: data[0].data, tension: 0.4 },
  { label: 'Random data', data: data[1].data, tension: 0.4 },
  { label: 'Random data', data: data[2].data, tension: 0.4 }
];

const App = () => <MOLineChart datasets={datasets} options={options} title="Timeseries data "></MOLineChart>;

Massive timeseries data

Chart.js charts are performant and capable of showing data from massive datasets. This example can have thousands of datapoints, and renders practically instantly. Decimation is a recommended practice to reduce visually redundant data points while simultaneously improving performance. In order to enable decimation, parsing must be disabled, which means the time data must be given as milliseconds from epoch (Jan. 1, 1970).

Amount of points:

500 2,500 5,000 10,000 100,000
Toggle decimation
<mo-line-chart subtitle="With decimation to 250 samples" title="Massive data" id="massive"></mo-line-chart>

<mo-divider></mo-divider>
<div style="display: flex; gap: 16px; align-items: center; justify-content: space-between; flex-wrap: wrap">
  <div style="display: flex; gap: 16px; align-items: center;">
    <p style="font-family: var(--mo-font-sans)">Amount of points:</p>
    <mo-select id="point-select" value="500" style="width: 250px">
      <mo-option value="500">500</mo-option>
      <mo-option value="2500">2,500</mo-option>
      <mo-option value="5000">5,000</mo-option>
      <mo-option value="10000">10,000</mo-option>
      <mo-option value="100000">100,000</mo-option>
    </mo-select>
  </div>
  <mo-switch id="decimation-toggle">
    <mo-icon name="ok" slot="suffix"></mo-icon>
    Toggle decimation
  </mo-switch>
</div>

<script>
  const chart = document.querySelector('#massive');
  function makeSeries(seriesCount, points) {
    return Array(seriesCount)
      .fill({
        data: []
      })
      .map(s => {
        return {
          data: Array(points)
            .fill(1)
            .map((d, i) => ({ x: i * 3600000, y: Math.max(250, Math.random() * 3000) }))
        };
      });
  }
  let data = makeSeries(1, 500);
  const options = {
    // Turn off animations and data parsing for performance (+ decimation)
    animation: false,
    parsing: false,
    plugins: {
      decimation: {
        enabled: false,
        algorithm: 'min-max'
      }
    },
    scales: {
      x: {
        type: 'time',
        time: { unit: 'day' }
      }
    }
  };
  const toggle = document.querySelector('#decimation-toggle');
  const select = document.querySelector('#point-select');
  toggle.addEventListener('mo-change', event => {
    if (event.target.checked) {
      chart.chart.options.plugins.decimation.enabled = true;
      chart.chart.options.plugins.decimation.algorithm = 'lttb';
      chart.chart.options.plugins.decimation.samples = 250;
      chart.chart.options.plugins.decimation.threshold = 250;
    } else {
      chart.chart.options.plugins.decimation.enabled = false;
    }
    chart.chart.update();
  });
  select.addEventListener('mo-change', event => {
    const points = parseInt(event.target.value);
    data = makeSeries(1, parseInt(event.target.value));
    const datasets = [{ label: `${points} points`, data: data[0].data, borderWidth: 1 }];
    toggle.checked = false;
    chart.datasets = datasets;
  });
  // set smaller borderWidth for clarity
  const datasets = [{ label: '500 points', data: data[0].data, borderWidth: 1 }];
  chart.datasets = datasets;
  chart.options = options;
</script>
import {
  MOLineChart,
  MOIcon,
  MOSwitch,
  MODivider,
  MOSelect,
  MOOption
} from '@metsooutotec/modes-web-components/dist/react';

function makeSeries(seriesCount, points) {
  return Array(seriesCount)
    .fill({
      data: []
    })
    .map(s => {
      return {
        data: Array(points)
          .fill(1)
          .map((d, i) => ({ x: i * 3600000, y: Math.max(250, Math.random() * 3000) }))
      };
    });
}
var data = makeSeries(1, 500);
const [datasets, setDatasets] = useState([{ label: '500 points', data: data[0].data, borderWidth: 1 }]);
const [selectValue, setSelectValue] = useState('500');
const [options, setOptions] = useState({
  // Turn off animations and data parsing for performance (+ decimation)
  animation: false,
  parsing: false,
  plugins: {
    decimation: {
      enabled: false,
      algorithm: 'min-max'
    }
  },
  scales: {
    x: {
      type: 'time',
      time: { unit: 'day' }
    }
  }
});

const toggleDecimation = event => {
  const optionsCopy = { ...options };
  if (event.target.checked) {
    optionsCopy.plugins.decimation.enabled = true;
    optionsCopy.plugins.decimation.algorithm = 'lttb';
    optionsCopy.plugins.decimation.samples = 250;
    optionsCopy.plugins.decimation.threshold = 250;
  } else {
    optionsCopy.plugins.decimation.enabled = false;
  }
  setOptions(optionsCopy);
};

const updateData = amount => {
  const newData = makeSeries(1, amount);
  const newDatasets = [{ label: `${amount} points`, data: newData[0].data, borderWidth: 1 }];
  setSelectValue(`${amount}`);
  setDatasets(newDatasets);
};

const App = () => {
  <>
    <MOLineChart title="Massive timeseries data" options={options} datasets={datasets}></MOLineChart>
    <MODivider></MODivider>
    <div
      style={{ display: 'flex', gap: '16px', alignItems: 'center', justifyContent: 'space-between', flexWrap: 'wrap' }}
    >
      <div style={{ display: 'flex', gap: '16px', alignItems: 'center' }}>
        <p style={{ fontFamily: 'var(--mo-font-sans)' }}>Amount of points:</p>
        <MOSelect
          onMoChange={event => updateData(parseInt(event.target.value))}
          value={selectValue}
          style={{ width: '250px' }}
        >
          <MOOption value="500">500</MOOption>
          <MOOption value="2500">2,500</MOOption>
          <MOOption value="5000">5,000</MOOption>
          <MOOption value="10000">10,000</MOOption>
          <MOOption value="100000">100,000</MOOption>
        </MOSelect>
      </div>
      <MOSwitch onMoChange={event => toggleDecimation(event)}>
        <MOIcon name="ok" slot="suffix"></MOIcon>
        Toggle decimation
      </MOSwitch>
    </div>
  </>;
};

CSS variables as colors

You can also assign existing CSS color variables as the colors for the data. Any variables found in the theme files should function here (assuming you have imported the css file your project).

<mo-line-chart subtitle="Data from Weather Spark" title="Avg. temp. by month" id="css-colors"></mo-line-chart>

<script>
  const chart = document.querySelector('#css-colors');
  const labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
  const helsinki = [-4, -5, -1, 4, 10, 14, 18, 16, 11, 6, 2, -2];
  const stockholm = [-2, -2, 1, 5, 11, 15, 18, 16, 12, 7, 3, -1];
  const paris = [4, 5, 8, 11, 14, 18, 20, 20, 16, 12, 8, 5];

  const datasets = [
    {
      label: 'Helsinki',
      data: helsinki,
      tension: 0.4,
      borderColor: 'var(--mo-color-data-10)'
    },
    { label: 'Stockholm', data: stockholm, tension: 0.4, borderColor: '--mo-color-data-11' },
    {
      label: 'Paris',
      data: paris,
      tension: 0.4,
      borderColor: '--mo-color-data-12'
    }
  ];
  chart.labels = labels;
  chart.options = options;
  chart.datasets = datasets;
</script>
import { MOLineChart } from '@metsooutotec/modes-web-components/dist/react';

const labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const helsinki = [-4, -5, -1, 4, 10, 14, 18, 16, 11, 6, 2, -2];
const stockholm = [-2, -2, 1, 5, 11, 15, 18, 16, 12, 7, 3, -1];
const paris = [4, 5, 8, 11, 14, 18, 20, 20, 16, 12, 8, 5];

const datasets = [
  { label: 'Helsinki', data: helsinki, tension: 0.4, borderColor: 'var(--mo-color-data-10)' },
  { label: 'Stockholm', data: stockholm, tension: 0.4, borderColor: '--mo-color-data-11' },
  { label: 'Paris', data: paris, tension: 0.4, borderColor: '--mo-color-data-12' }
];

const App = () => (
  <MOLineChart
    datasets={datasets}
    labels={labels}
    subtitle="Data from Weather Spark"
    title="Avg. temp. by month"
  ></MOLineChart>
);

Custom legend positioning

The legend can be placed next to the chart using the legendPosition and legendAlignment attributes. See the example below on different permutations.

Top Left Bottom Right Start End
<div style="display: flex; gap: 16px;">
  <mo-select id="pos-select" value="right">
    <mo-option value="top">Top</mo-option>
    <mo-option value="left">Left</mo-option>
    <mo-option value="bottom">Bottom</mo-option>
    <mo-option value="right">Right</mo-option>
  </mo-select>
  <mo-select id="align-select" value="start">
    <mo-option value="start">Start</mo-option>
    <mo-option value="end">End</mo-option>
  </mo-select>
</div>
<mo-divider></mo-divider>
<mo-line-chart
  legendPosition="right"
  legendAlignment="start"
  subtitle="Data from Weather Spark"
  title="Avg. temp. by month"
  id="last-example"
></mo-line-chart>

<script>
  const chart = document.querySelector('#last-example');
  const posSelect = document.querySelector('#pos-select');
  const alignSelect = document.querySelector('#align-select');
  const labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
  const helsinki = [-4, -5, -1, 4, 10, 14, 18, 16, 11, 6, 2, -2];
  const stockholm = [-2, -2, 1, 5, 11, 15, 18, 16, 12, 7, 3, -1];
  const paris = [4, 5, 8, 11, 14, 18, 20, 20, 16, 12, 8, 5];
  const rome = [7, 8, 11, 13, 18, 22, 25, 25, 21, 17, 12, 8];

  posSelect.addEventListener('mo-change', e => {
    const newPos = e.target.value;
    chart.legendPosition = newPos;
  });

  alignSelect.addEventListener('mo-change', e => {
    const newAlign = e.target.value;
    chart.legendAlignment = newAlign;
  });

  const datasets = [
    { label: 'Helsinki', data: helsinki, tension: 0.4 },
    { label: 'Stockholm', data: stockholm, tension: 0.4 },
    { label: 'Paris', data: paris, tension: 0.4 },
    { label: 'Rome', data: rome, tension: 0.4 }
  ];
  chart.labels = labels;
  chart.datasets = datasets;
</script>
import { MOLineChart, MODivider, MOSelect, MOOption } from '@metsooutotec/modes-web-components/dist/react';

const lineChartRef = useRef(null);

const labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const helsinki = [-4, -5, -1, 4, 10, 14, 18, 16, 11, 6, 2, -2];
const stockholm = [-2, -2, 1, 5, 11, 15, 18, 16, 12, 7, 3, -1];
const paris = [4, 5, 8, 11, 14, 18, 20, 20, 16, 12, 8, 5];
const rome = [7, 8, 11, 13, 18, 22, 25, 25, 21, 17, 12, 8];

const updatePos = e => {
  lineChartRef.legendPosition = e.target.value;
};

const updateAlign = e => {
  lineChartRef.legendAlignment = e.target.value;
};

const datasets = [
  { label: 'Helsinki', data: helsinki, tension: 0.4 },
  { label: 'Stockholm', data: stockholm, tension: 0.4 },
  { label: 'Paris', data: paris, tension: 0.4 },
  { label: 'Rome', data: rome, tension: 0.4 }
];

const App = () => (
  <>
    <div style={{ display: 'flex', gap: '16px' }}>
      <MOSelect onMoChange={updatePos} value="right">
        <MOOption value="top">Top</MOOption>
        <MOOption value="left">Left</MOOption>
        <MOOption value="bottom">Bottom</MOOption>
        <MOOption value="right">Right</MOOption>
      </MOSelect>
      <MOSelect onMoChange={updateAlign} value="start">
        <MOOption value="start">Start</MOOption>
        <MOOption value="end">End</MOOption>
      </MOSelect>
    </div>
    <MODivider></MODivider>
    <MOLineChart
      datasets={datasets}
      labels={labels}
      legendPosition="right"
      legendAlignment="start"
      subtitle="Data from Weather Spark"
      title="Avg. temp. by month"
      ref={lineChartRef}
    ></MOLineChart>
  </>
);

Importing

If you’re using the autoloader or the traditional loader, you can ignore this section. Otherwise, feel free to use any of the following snippets to cherry pick this component.

Bundler React Script

To import this component using a bundler:

import '@metsooutotec/modes-web-components/dist/components/line-chart/line-chart.js';

To import this component as a React component:

import MOLineChart from '@metsooutotec/modes-web-components/dist/react/line-chart/';

To import this component using a script tag:

<script type="module" src="https://modes-web.metso.com/dist/components/cdn/components/line-chart/line-chart.js"></script>

Properties

Name Description Reflects Type Default
canvas Query for the canvas element instantiated in the html template. HTMLCanvasElement -
type Type of chart. ChartType 'line'
chart Reference to the chart itself. Chart -
datasets Datasets for the chart LineChartDataset[] []
labels Labels to show on the x-axis. string[] []
title Title to be shown above the chart. string ''
subtitle Subtitle to be shown above the chart, but below title. string ''
legendAlignment Alignment of the legends. 'start' | 'end' 'start'
legendPosition Position of the legends. 'bottom' | 'right' | 'left' | 'top' 'bottom'
options Additional options for the chart. ChartOptions {}
zoomOptions Options for the zoom plugin. ZoomPluginOptions { pan: { enabled: true, mode: 'xy', modifierKey: 'ctrl' }, zoom: { drag: { enabled: true }, mode: 'xy' } }
showDatapoints Show datapoints. boolean false
zoomable Enables zooming on the chart. boolean false
area Area chart. boolean false
extrapolation Show dashed line where data is missing. boolean false
thinLines Make likes between datapoints thinner. Should be used with massive data. boolean false
customPlugins
custom-plugins
Use this property to add custom plugins to the created chart.js instance Plugin[] []
updateComplete A read-only promise that resolves when the component has finished updating.

Learn more about attributes and properties.

Parts

Name Description
base The div containing the canvas element.
canvas The canvas element the chart is rendered in.
legends The custom legends below the chart.

Learn more about customizing CSS parts.