Skip to content

Instantly share code, notes, and snippets.

@mxsxs2
Created September 29, 2025 13:26
Show Gist options
  • Select an option

  • Save mxsxs2/8de4e5b3f798b833fb6a59e07ea6e72b to your computer and use it in GitHub Desktop.

Select an option

Save mxsxs2/8de4e5b3f798b833fb6a59e07ea6e72b to your computer and use it in GitHub Desktop.
Plugin to always show tooltips for chart.js charts
import type {
Chart,
ChartType,
Plugin,
ChartConfiguration,
VisualElement,
} from 'chart.js'
export interface AlwaysShowTooltipPluginOptions {
color?: string
}
declare module 'chart.js' {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface PluginOptionsByType<TType extends ChartType> {
alwaysShowTooltip?: AlwaysShowTooltipPluginOptions
}
}
//TODO: implement tests once we change over from jest to vitest
/**
* Chart.js plugin to display always-visible tooltips (data values) on chart elements.
*
* Supports bar, line, pie, and doughnut charts. Skips values that are 0 or null.
* Tooltip text is centered inside each chart element:
* - Bars: vertically centered in the bar
* - Lines: slightly above each point
* - Pie/Doughnut: centered in each arc
*
* Plugin options can be set via `chartOptions.plugins.alwaysShowTooltip`, for example:
* ```ts
* plugins: {
* alwaysShowTooltip: {
* color: 'white', // sets the tooltip text color
* }
* }
* ```
*
* @type {Plugin<ChartType>}
*/
const AlwaysShowTooltipPlugin: Plugin<ChartType> = {
id: 'alwaysShowTooltip',
afterDatasetsDraw<TType extends ChartType>(
chart: Chart<TType>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
_: any,
pluginOptions: AlwaysShowTooltipPluginOptions
) {
const { ctx } = chart
const chartType = (chart.config as ChartConfiguration<TType>).type
const color = pluginOptions?.color ?? 'black' // default to black
ctx.save()
ctx.font = '12px sans-serif'
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
chart.data.datasets.forEach((dataset, datasetIndex) => {
const meta = chart.getDatasetMeta(datasetIndex)
meta.data.forEach((element, index) => {
const value = dataset.data[index] as number
if (!value) return
if (chartType === 'bar') {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const x = (element as any).x
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const y = ((element as any).base + (element as any).y) / 2
ctx.fillStyle = color
ctx.fillText(String(value), x, y)
}
if (chartType === 'line') {
const { x, y } = element
ctx.fillStyle = color
ctx.fillText(String(value), x, y - 10)
}
if (chartType === 'pie' || chartType === 'doughnut') {
const { x, y } = (
element as unknown as VisualElement
).getCenterPoint()
ctx.fillStyle = color
ctx.fillText(String(value), x, y)
}
})
})
ctx.restore()
},
}
export default AlwaysShowTooltipPlugin
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment