DevExtreme v23.1 is now available.

Explore our newest features/capabilities and share your thoughts with us.

Your search did not match any results.
Bar Charts

Custom Annotations

Annotations are designed to show additional information about visualized data. You can display images, text blocks, and custom content in annotations. To create custom annotations, specify the following Chart properties.


The annotations array specifies a collection of annotations. This demo uses a data source array to create this collection. Each object has the argument property and a data object. The argument property positions an annotation at a specific argument, and the data object stores data from a data source.

You can use the annotations array to configure each annotation individually.


Use the commonAnnotationSettings object to specify settings common to all annotations in the chart. In this object, assign custom to the type property. To anchor annotations to a series point, specify the series property. Finally, declare the SVG markup in the annotation template.

Note that individual annotation settings override the common annotation settings.


You can also implement the customizeAnnotation function to customize an individual chart annotation.
Backend API
Copy to CodeSandBox
<template> <div> <DxChart id="chart" :data-source="populationData" title="Top 5 Most Populated States in US" > <DxSeries type="bar" argument-field="name" value-field="population" name="Population" /> <DxLegend :visible="false" /> <DxCommonAnnotationSettings :allow-dragging="true" type="custom" series="Population" template="annotationTemplate" /> <DxAnnotation v-for="data in populationData" :argument="" :data="data" :key="" /> <template #annotationTemplate="{ data }"> <AnnotationTemplate :annotation="data"/> </template> </DxChart> </div> </template> <script> import { DxChart, DxSeries, DxLegend, DxAnnotation, DxCommonAnnotationSettings, } from 'devextreme-vue/chart'; import { populationData } from './data.js'; import AnnotationTemplate from './AnnotationTemplate.vue'; export default { components: { DxChart, DxSeries, DxLegend, DxAnnotation, DxCommonAnnotationSettings, AnnotationTemplate, }, data() { return { populationData, }; }, }; </script> <style scoped> #chart { height: 440px; } </style>
<template> <svg class="annotation"> <image :href="getImagePath()" width="60" height="40" /> <rect x="0" y="0" class="border" /> <text x="70" y="25" class="state" >{{ annotation.argument }}</text> <text x="0" y="60" > <tspan class="caption">Capital:</tspan><tspan class="capital" dx="5" >{{ }}</tspan><tspan dy="14" x="0" class="caption" >Population:</tspan><tspan class="population" dx="5" >{{ formatNumber(data.population) }}</tspan><tspan dy="14" x="0" class="caption" >Area:</tspan><tspan class="area" dx="5" >{{ formatNumber(data.area) }}</tspan><tspan dx="5">km</tspan><tspan class="sup" dy="-2" >2</tspan> </text> </svg> </template> <script> export default { props: { annotation: { type: Object, default: () => ({}), }, }, data() { return { data:, }; }, methods: { getImagePath() { return `../../../../images/flags/${\s/, '')}.svg`; }, formatNumber: new Intl.NumberFormat('en-US', { minimumFractionDigits: 0, }).format, }, }; </script> <style> .annotation { font-size: 12px; } .border { width: 60px; height: 40px; stroke: rgba(191, 191, 191, 0.25); stroke-width: 1px; fill: transparent; } .state { font-weight: 500; font-size: 14px; } .caption { font-weight: 500; } .sup { font-size: 0.8em; } </style>
import { createApp } from 'vue'; import App from './App.vue'; createApp(App).mount('#app');
<!DOCTYPE html> <html> <head> <title>DevExtreme Demo</title> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" /> <link rel="stylesheet" type="text/css" href="" /> <script src=""></script> <script src=""></script> <script type="text/javascript" src="config.js"></script> <script type="text/javascript"> System.import("./index.js"); </script> </head> <body class="dx-viewport"> <div class="demo-container"> <div id="app"> </div> </div> </body> </html>
export const populationData = [{ name: 'California', population: 38802500, capital: 'Sacramento', area: 423967, }, { name: 'Texas', population: 26956958, capital: 'Austin', area: 695662, }, { name: 'Florida', population: 19893297, capital: 'Tallahassee', area: 170312, }, { name: 'New York', population: 19746227, capital: 'Albany', area: 141297, }, { name: 'Illinois', population: 12880580, capital: 'Springfield', area: 149995, }];
window.config = { transpiler: 'plugin-babel', meta: { '*.vue': { loader: 'vue-loader', }, '*.ts': { loader: 'demo-ts-loader', }, '*.svg': { loader: 'svg-loader', }, 'devextreme/localization.js': { 'esModule': true, }, }, paths: { 'root:': '../../../../../', 'npm:': '', }, map: { 'vue': 'npm:vue@3.3.4/dist/vue.esm-browser.js', 'vue-loader': 'npm:dx-systemjs-vue-browser@1.1.1/index.js', 'demo-ts-loader': 'root:utils/demo-ts-loader.js', 'svg-loader': 'root:utils/svg-loader.js', 'mitt': 'npm:mitt/dist/mitt.umd.js', 'rrule': 'npm:rrule@2.6.4/dist/es5/rrule.js', 'luxon': 'npm:luxon@1.28.1/build/global/luxon.min.js', 'es6-object-assign': 'npm:es6-object-assign@1.1.0', 'devextreme': 'npm:devextreme@23.1.5/cjs', 'devextreme-vue': 'npm:devextreme-vue@23.1.5', 'jszip': 'npm:jszip@3.7.1/dist/jszip.min.js', 'devextreme-quill': 'npm:devextreme-quill@1.6.2/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.2.1/dist/dx-diagram.js', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.48/dist/dx-gantt.js', '@devextreme/runtime': 'npm:@devextreme/runtime@3.0.11', 'inferno': 'npm:inferno@7.4.11/dist/inferno.min.js', 'inferno-compat': 'npm:inferno-compat/dist/inferno-compat.min.js', 'inferno-create-element': 'npm:inferno-create-element@7.4.11/dist/inferno-create-element.min.js', 'inferno-dom': 'npm:inferno-dom/dist/inferno-dom.min.js', 'inferno-hydrate': 'npm:inferno-hydrate@7.4.11/dist/inferno-hydrate.min.js', 'inferno-clone-vnode': 'npm:inferno-clone-vnode/dist/inferno-clone-vnode.min.js', 'inferno-create-class': 'npm:inferno-create-class/dist/inferno-create-class.min.js', 'inferno-extras': 'npm:inferno-extras/dist/inferno-extras.min.js', 'plugin-babel': 'npm:systemjs-plugin-babel@0.0.25/plugin-babel.js', 'systemjs-babel-build': 'npm:systemjs-plugin-babel@0.0.25/systemjs-babel-browser.js', // Prettier 'prettier/standalone': 'npm:prettier@2.8.4/standalone.js', 'prettier/parser-html': 'npm:prettier@2.8.4/parser-html.js', }, packages: { 'devextreme-vue': { main: 'index.js', }, 'devextreme': { defaultExtension: 'js', }, 'devextreme/events/utils': { main: 'index', }, 'devextreme/events': { main: 'index', }, 'es6-object-assign': { main: './index.js', defaultExtension: 'js', }, }, packageConfigPaths: [ 'npm:@devextreme/*/package.json', 'npm:@devextreme/runtime@3.0.11/inferno/package.json', ], babelOptions: { sourceMaps: false, stage0: true, }, }; System.config(window.config);