Label content control including custom formatters, display conditions, text alignment, and multi-line text handling for controlling what and how content is displayed.
Transform and format data values for display in labels.
interface ContentOptions {
/** Custom function to format label content
* @default built-in formatter (converts data to string) */
formatter?: (value: any, context: Context) => any | null;
/** Control label visibility and auto-hiding
* @default true */
display?: Indexable<boolean | string> | Scriptable<boolean | string>;
}
interface Context {
active: boolean; // Whether element is hovered
chart: Chart; // Chart instance
dataIndex: number; // Index of current data point
dataset: ChartDataset; // Current dataset
datasetIndex: number; // Index of current dataset
}Built-in Default Formatter:
The plugin includes a default formatter that handles various data types:
/**
* Default formatter handles primitives, objects, and arrays
* - Null/undefined returns null (no label)
* - Objects: uses .label property, .r property, or key:value pairs
* - Arrays and primitives: converts to string
*/
function defaultFormatter(value: any): string | null;Usage Examples:
// Simple value formatting
datalabels: {
formatter: (value) => '$' + value.toFixed(2)
}
// Percentage formatting
datalabels: {
formatter: (value, context) => {
const sum = context.dataset.data.reduce((a, b) => a + b, 0);
return Math.round((value / sum) * 100) + '%';
}
}
// Conditional formatting
datalabels: {
formatter: (value, context) => {
if (value === 0) return null; // No label for zero values
return value > 1000 ? (value / 1000).toFixed(1) + 'K' : value;
}
}
// Multi-line formatting
datalabels: {
formatter: (value, context) => {
const dataset = context.dataset;
return [dataset.label, value]; // Array creates multiple lines
}
}Control when and how labels are shown.
interface DisplayControl {
/** Control label visibility
* @default true */
display?: Indexable<boolean | string> | Scriptable<boolean | string>;
}Display Options:
true: Always show label (default)false: Never show label'auto': Automatically hide overlapping labelsUsage Examples:
// Hide labels for small values
datalabels: {
display: function(context) {
return context.parsed.y > 5;
}
}
// Auto-hide overlapping labels
datalabels: {
display: 'auto'
}
// Show only for active (hovered) elements
datalabels: {
display: function(context) {
return context.active;
}
}
// Hide labels on small charts
datalabels: {
display: function(context) {
return context.chart.width > 300;
}
}Control multi-line text alignment within labels.
interface TextOptions {
/** Text alignment for multi-line labels
* @default 'start' */
textAlign?: Indexable<TextAlign> | Scriptable<TextAlign>;
}
type TextAlign = 'left' | 'right' | 'start' | 'center' | 'end';Usage Examples:
// Center-aligned multi-line text
datalabels: {
formatter: (value, context) => [
context.dataset.label,
'$' + value.toFixed(2)
],
textAlign: 'center'
}
// Right-aligned for currency values
datalabels: {
formatter: (value) => value.toString().split(''),
textAlign: 'right'
}Complex formatting scenarios for specialized use cases.
Data Type Specific Formatting:
// Handle different chart data structures
datalabels: {
formatter: (value, context) => {
// Scatter plot data: {x, y}
if (typeof value === 'object' && value.x !== undefined) {
return `(${value.x}, ${value.y})`;
}
// Bubble chart data: {x, y, r}
if (typeof value === 'object' && value.r !== undefined) {
return `Size: ${value.r}`;
}
// Regular data
return value;
}
}Dataset-Aware Formatting:
// Different formats per dataset
datalabels: {
formatter: (value, context) => {
switch (context.datasetIndex) {
case 0: return value + ' units';
case 1: return '$' + value;
case 2: return value + '%';
default: return value;
}
}
}Time-Based Formatting:
// Format timestamp data
datalabels: {
formatter: (value, context) => {
if (value instanceof Date) {
return value.toLocaleDateString();
}
// Assume timestamp in milliseconds
return new Date(value).toLocaleDateString();
}
}Statistical Formatting:
// Show rank, value, and percentage
datalabels: {
formatter: (value, context) => {
const data = context.dataset.data;
const sorted = [...data].sort((a, b) => b - a);
const rank = sorted.indexOf(value) + 1;
const total = data.reduce((sum, val) => sum + val, 0);
const percentage = ((value / total) * 100).toFixed(1);
return [
`#${rank}`,
value,
`${percentage}%`
];
}
}Conditional Multi-Format:
// Different formatting based on value range
datalabels: {
formatter: (value, context) => {
if (value === 0) return null;
if (value < 1) {
return (value * 100).toFixed(1) + '%';
} else if (value < 1000) {
return value.toString();
} else if (value < 1000000) {
return (value / 1000).toFixed(1) + 'K';
} else {
return (value / 1000000).toFixed(1) + 'M';
}
}
}Responsive Formatting:
// Adjust format based on available space
datalabels: {
formatter: (value, context) => {
const chartWidth = context.chart.width;
if (chartWidth < 400) {
// Short format for small screens
return value > 1000 ? Math.round(value / 1000) + 'K' : value;
} else {
// Full format for larger screens
return new Intl.NumberFormat().format(value);
}
}
}