Chart.js para KPIs
Aprende a crear graficas profesionales para tus dashboards con Chart.js, la libreria mas popular para visualizacion de datos en JavaScript vanilla.
1 ΒΏQue es Chart.js?
Chart.js es una libreria de JavaScript open-source que dibuja graficas sobre un elemento
<canvas> del HTML. Es ligera
(~60KB gzipped), responsive por defecto, y soporta 8 tipos de graficas listas para usar.
π¨ Analogia del mundo real:
Chart.js es como una impresora de planos: tΓΊ le pasas los datos (medidas) y la configuraciΓ³n (escala, colores, leyenda), y ella se encarga de dibujar. No te preocupas por calcular pΓxeles ni curvas: tΓΊ piensas en quΓ© quieres comunicar, ella lo dibuja.
Caracteristicas clave:
- β’Canvas API: dibuja sobre pixeles, no sobre el DOM. Rinde mejor con muchos puntos.
- β’Responsive: se adapta solo al tamano del contenedor padre.
- β’Animaciones fluidas: transiciones entre cambios de datos out-of-the-box.
- β’Tree-shakeable: en v4 puedes importar solo lo que uses.
- β’Sin dependencias: no necesita jQuery ni nada.
2 ΒΏPor que es ideal para KPIs?
Un KPI (Key Performance Indicator) necesita comunicar una metrica en segundos. Chart.js te da lo justo para eso:
π Tendencias
Line charts para ver evolucion temporal: ventas por dia, sesiones por hora.
π Comparaciones
Bar charts para comparar categorias: ingresos por canal, productos mas vendidos.
π₯§ Distribuciones
Doughnut/pie para ver proporciones: conversion rate, market share.
π― Multi-metrica
Radar charts para evaluar un objeto por varias dimensiones a la vez.
3 Instalacion
Tienes dos caminos para incluir Chart.js en tu proyecto. ElΓgelo segun el contexto:
A) npm
Para proyectos reales con bundler (Vite, Webpack, Astro, Next, etc). Es como vas a trabajar siempre en una empresa.
Pros: tree-shaking, versionado, autocompletado del IDE, funciona offline. Contras: requiere tener Node y un bundler.
B) CDN
Para prototipos rapidos, demos, codepen, HTML simple sin bundler.
Pros: cero setup, una linea y listo. Contras: no hay tree-shaking, depende de internet, no escala a proyectos grandes.
Opcion A β npm (recomendada)
Es la forma profesional. AcostΓΊmbrate a trabajar asΓ: las librerΓas se instalan, no se copian de un CDN. Te queda registrada en package.json, puedes versionarla, el IDE te autocompleta y funciona offline.
1npm install chart.jsY en tu archivo JS:
1import Chart from 'chart.js/auto'; chart.js/auto registra TODOS los componentes (escalas, controllers, plugins).
Es lo mΓ‘s cΓ³modo para arrancar. Para bundles mΓnimos en producciΓ³n, puedes importar solo lo que uses.
Opcion B β CDN (solo para prototipos)
Pega esto antes de cerrar </body> en tu HTML:
1<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
Esto te deja la variable global Chart disponible en cualquier script que cargues despues.
Γtil para experimentar rΓ‘pido, pero no es lo que vas a usar en un proyecto real.
π‘ ΒΏPor que npm es mejor?
En cuanto trabajes en un proyecto real con Astro, React, Vue o cualquier framework moderno, vas a tener un bundler.
AcostΓΊmbrate desde ya: npm install + import.
Es la base de todo el ecosistema JS moderno y lo que vas a hacer con CADA librerΓa de aquΓ en adelante.
4 Tu primera grafica paso a paso
Crear una grafica son 4 pasos. Vamos uno por uno con un caso real: graficar las ventas mensuales.
Crear el canvas en el HTML
Chart.js dibuja sobre un <canvas>.
Ponele un id para poder referenciarlo desde JS.
Importante: envuΓ©lvelo en un contenedor con tamaΓ±o definido.
1<div style="max-width: 600px; height: 400px;">2 <canvas id="miGrafica"></canvas>3</div>Cargar / importar Chart.js
Con npm (recomendado), instΓ‘lalo y luego impΓ³rtalo en tu archivo JS:
1npm install chart.js1import Chart from 'chart.js/auto';O si estΓ‘s usando CDN en un HTML simple, agrΓ©galo antes de cerrar </body>:
1<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>2<script src="./app.js"></script>Obtener la referencia al canvas
Igual que con cualquier elemento del DOM, usa document.getElementById:
1const ctx = document.getElementById('miGrafica');
La variable se llama ctx por convencion (de "context"),
aunque Chart.js v3+ acepta directamente el elemento canvas.
Instanciar el objeto Chart
Crea una nueva instancia con new Chart(ctx, config).
El segundo parametro es el objeto de configuracion con 3 propiedades: type,
data y options.
1const grafica = new Chart(ctx, {2 type: 'line',3 data: {4 labels: ['Ene', 'Feb', 'Mar', 'Abr', 'May'],5 datasets: [{6 label: 'Ventas 2026',7 data: [120, 190, 300, 250, 420],8 borderColor: 'rgb(168, 85, 247)',9 backgroundColor: 'rgba(168, 85, 247, 0.2)',10 fill: true,11 tension: 0.412 }]13 },14 options: {15 responsive: true,16 plugins: {17 legend: { position: 'top' },18 title: { display: true, text: 'Ventas mensuales' }19 }20 }21});β Listo. Chart.js ya dibujΓ³ la grΓ‘fica en el canvas. Si abres el HTML en el navegador, la ves animada.
π‘ Resumen del paso a paso:
- Canvas en HTML con
id - Cargar Chart.js (CDN o import)
getElementByIdpara la referencianew Chart(ctx, { type, data, options })
5
Anatomia: type, data y options
Toda configuracion de Chart.js sigue exactamente la misma estructura. Estos son los 3 parametros que controlan todo:
1new Chart(ctx, {2 type: '...', // QUE tipo de grafica dibujar3 data: { ... }, // QUE datos mostrar (labels + datasets)4 options: { ... } // COMO se ve y comporta (colores, ejes, leyendas, animaciones)5}); type β los 8 tipos disponibles
Un string que define el tipo de grafica. Estos son TODOS los valores posibles:
| type | Para que sirve | Ejemplo de uso en KPI |
|---|---|---|
| 'line' | Linea continua entre puntos | Ingresos por dia, sesiones por hora |
| 'bar' | Barras verticales (u horizontales con indexAxis) | Ventas por canal, top productos |
| 'pie' | Torta clasica | Distribucion del presupuesto |
| 'doughnut' | Torta con agujero (donut) | Conversion rate, completitud |
| 'radar' | Grafico de areas multidimensional | Performance multi-area, skills |
| 'polarArea' | Como pie pero con segmentos de longitud variable | Distribucion ponderada |
| 'bubble' | Burbujas con tamano variable (3 dimensiones) | Precio vs volumen vs margen |
| 'scatter' | Puntos sueltos en un plano XY | Correlacion entre 2 metricas |
data β los datos a mostrar
Es un objeto con dos propiedades: labels (etiquetas del eje X) y datasets (uno o varios conjuntos de datos a graficar).
1data: {2 labels: ['Ene', 'Feb', 'Mar'], // string[] β eje X3 datasets: [4 {5 label: 'Producto A', // nombre que aparece en la leyenda6 data: [10, 20, 30], // number[] β los valores7 backgroundColor: '...',8 borderColor: '...',9 // ...mas propiedades de estilo10 },11 {12 label: 'Producto B',13 data: [15, 25, 35],14 }15 ]16}Propiedades comunes de un dataset:
| Propiedad | Tipo | Que hace |
|---|---|---|
| label | string | Texto que aparece en la leyenda y en tooltips |
| data | number[] | El array de valores a graficar |
| backgroundColor | string | string[] | Color de relleno (uno solo o uno por punto) |
| borderColor | string | string[] | Color del borde / linea |
| borderWidth | number | Grosor del borde en px |
| fill | boolean | (line) Pintar el area bajo la linea |
| tension | number 0-1 | (line) Curvatura: 0 = recto, 0.4 = suave |
| pointRadius | number | (line) Tamano de los puntos. 0 = sin puntos |
| borderRadius | number | (bar) Esquinas redondeadas en las barras |
options β como se ve y comporta
Las opciones controlan TODO lo visual y de comportamiento. Aca van las mas usadas:
| Opcion | Valores | Para que |
|---|---|---|
| responsive | true | false | Se adapta al contenedor padre |
| maintainAspectRatio | true | false | Mantiene proporcion. false = llena el contenedor |
| indexAxis | 'x' | 'y' | (bar) 'y' = barras horizontales |
| cutout | '50%' | number | (doughnut) Tamano del agujero |
| animation | { duration, easing } | false | Velocidad y tipo de transicion |
| plugins.legend | { display, position } | Leyenda: 'top'|'bottom'|'left'|'right' |
| plugins.title | { display, text } | Titulo arriba del grafico |
| plugins.tooltip | { enabled, callbacks } | Tooltips al hacer hover |
| scales.y | { beginAtZero, min, max } | Configuracion del eje Y |
| scales.x | { display, grid } | Configuracion del eje X |
Valores comunes de animation.easing:
'linear' 'easeInQuad' 'easeOutQuad' 'easeInOutQuad' 'easeInCubic' 'easeOutCubic' 'easeInOutQuart' 'easeOutBounce' 6 Ejemplo completo con TODOS los parametros
Esta es una grafica de tipo line con 2 datasets, cada parametro comentado.
CΓ³pialo, pΓ©galo en un HTML con un <canvas id="ventas"></canvas> y funcionarΓ‘:
1const ctx = document.getElementById('ventas');2 3const grafica = new Chart(ctx, {4 // βββββββββββββββββββββββββββββββββββββββββββββββββββββ5 // PARAMETRO 1: type β que tipo de grafica6 // βββββββββββββββββββββββββββββββββββββββββββββββββββββ7 type: 'line',8 9 // βββββββββββββββββββββββββββββββββββββββββββββββββββββ10 // PARAMETRO 2: data β los datos11 // βββββββββββββββββββββββββββββββββββββββββββββββββββββ12 data: {13 labels: ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun'],14 15 datasets: [16 {17 label: 'Producto A', // nombre en leyenda18 data: [120, 190, 300, 250, 420, 380], // valores19 20 borderColor: 'rgb(168, 85, 247)', // color de la linea21 backgroundColor: 'rgba(168, 85, 247, 0.2)', // relleno22 borderWidth: 3, // grosor en px23 24 fill: true, // pintar area bajo la linea25 tension: 0.4, // curvatura (0 = recto)26 pointRadius: 4, // tamano de puntos27 pointHoverRadius: 6, // tamano al hacer hover28 pointBackgroundColor: 'rgb(168, 85, 247)',29 },30 {31 label: 'Producto B',32 data: [80, 150, 220, 310, 280, 350],33 borderColor: 'rgb(236, 72, 153)',34 backgroundColor: 'rgba(236, 72, 153, 0.2)',35 borderWidth: 3,36 fill: true,37 tension: 0.4,38 }39 ]40 },41 42 // βββββββββββββββββββββββββββββββββββββββββββββββββββββ43 // PARAMETRO 3: options β como se ve y comporta44 // βββββββββββββββββββββββββββββββββββββββββββββββββββββ45 options: {46 responsive: true, // se adapta al contenedor47 maintainAspectRatio: false, // llena todo el contenedor48 49 // Animaciones50 animation: {51 duration: 1000, // ms52 easing: 'easeInOutQuart' // tipo de curva53 },54 55 // Plugins (leyenda, titulo, tooltip)56 plugins: {57 legend: {58 display: true,59 position: 'top', // top|bottom|left|right60 labels: { color: '#e2e8f0', font: { size: 13 } }61 },62 title: {63 display: true,64 text: 'Ventas mensuales 2026',65 color: '#f1f5f9',66 font: { size: 18, weight: 'bold' }67 },68 tooltip: {69 enabled: true,70 backgroundColor: 'rgba(15, 23, 42, 0.95)',71 titleColor: '#f1f5f9',72 bodyColor: '#cbd5e1',73 callbacks: {74 // Personalizar el texto del tooltip75 label: (ctx) => `${ctx.dataset.label}: $${ctx.parsed.y}`76 }77 }78 },79 80 // Configuracion de los ejes81 scales: {82 y: {83 beginAtZero: true,84 min: 0,85 max: 500,86 ticks: { color: '#94a3b8', stepSize: 100 },87 grid: { color: 'rgba(148, 163, 184, 0.1)' },88 title: { display: true, text: 'Ventas ($)' }89 },90 x: {91 ticks: { color: '#94a3b8' },92 grid: { display: false },93 title: { display: true, text: 'Mes' }94 }95 }96 }97});π‘ Nota:
NO necesitas escribir todos los parametros. Chart.js tiene defaults razonables. Este ejemplo te muestra TODO lo que puedes ajustar. En la prΓ‘ctica, una config tΓpica tiene 5-10 lΓneas.
7 Actualizar datos en vivo (el corazon de un dashboard)
Un KPI sin datos frescos es un cadaver. Chart.js expone .update()
para repintar con animaciΓ³n cuando cambias el array de datos.
El patron: mutar + update()
1const grafica = new Chart(ctx, {2 type: 'line',3 data: { labels: [], datasets: [{ label: 'Usuarios', data: [] }] }4});5 6function tick() {7 const ahora = new Date().toLocaleTimeString();8 const valor = Math.floor(Math.random() * 100) + 50;9 10 // Agregamos el nuevo punto11 grafica.data.labels.push(ahora);12 grafica.data.datasets[0].data.push(valor);13 14 // Mantenemos solo los ultimos 2015 if (grafica.data.labels.length > 20) {16 grafica.data.labels.shift();17 grafica.data.datasets[0].data.shift();18 }19 20 grafica.update(); // anima la transicion21}22 23setInterval(tick, 1500); // cada 1.5sβ‘ Gotcha clasico:
NO reemplaces grafica.data = nuevosData sin mas:
rompe las referencias internas. Muta los arrays existentes (con push/shift/splice)
o asigna a grafica.data.datasets[0].data.
Despues, .update().
8 Integrar en Astro (JS vanilla, sin React)
En Astro puedes escribir JS vanilla dentro de un bloque <script>
al final del .astro. Astro lo procesa y lo manda al browser.
No necesitas ni React ni componentes.
1---2// src/pages/dashboard.astro3---4<canvas id="ventas"></canvas>5 6<script>7 import Chart from 'chart.js/auto';8 9 const ctx = document.getElementById('ventas');10 11 const grafica = new Chart(ctx, {12 type: 'line',13 data: {14 labels: [],15 datasets: [{ label: 'Ventas', data: [], borderColor: '#a855f7' }]16 }17 });18 19 setInterval(() => {20 grafica.data.labels.push(new Date().toLocaleTimeString());21 grafica.data.datasets[0].data.push(Math.random() * 100);22 if (grafica.data.labels.length > 15) {23 grafica.data.labels.shift();24 grafica.data.datasets[0].data.shift();25 }26 grafica.update();27 }, 1000);28</script>π‘ Tip Astro:
Si uses is:inline en el script,
Astro NO lo procesa: tienes que cargar Chart.js por CDN. Sin is:inline,
Astro usa Vite y puedes importar chart.js/auto.
9 Buenas practicas
Contenedor con tamano explicito
Envolve el <canvas> en un div con width/height. Si no, Chart.js se va a pantalla completa.
Limita puntos en graficas en vivo
Mantene solo los ultimos N puntos (shift). Un array infinito mata el render.
Limpia el interval
Si la grafica se desmonta (SPA, navegacion), llama clearInterval y grafica.destroy(). Sino, fuga de memoria.
No abuses de animaciones
Si actualizas cada 500ms, desactiva animaciones con options.animation = false o uses update('none').
Cuidado con el dark mode
Los colores por defecto (labels, grids) son para fondo blanco. Configura Chart.defaults.color y Chart.defaults.borderColor.
10 Resumen
- β’Chart.js dibuja sobre
<canvas>: ligero, responsive, sin dependencias. - β’4 pasos: canvas β cargar Chart β getElementById β
new Chart(ctx, config). - β’Toda config tiene type + data + options.
typees uno de los 8 tipos disponibles. - β’Para KPIs: line (tendencia), bar (comparacion), doughnut (proporcion), radar (multi-dimension).
- β’Actualizar en vivo = mutar arrays + llamar update(), nunca reemplazar
data.