When building time-series charts, how you format axis labels can make or break readability — especially when users zoom across seconds to years. CanvasJS provides excellent out-of-the-box support for time-based axis labels, but when working with sub-minute or sub-hour data (e.g., sensor readings, real-time dashboards), the default behavior might not always deliver the best readability. This guide shows how to refine axis labels for small time ranges (seconds, minutes) while retaining CanvasJS’s automatic interval calculations for larger ranges.

The Challenge

For small time windows (e.g., 30 seconds or 10 minutes), CanvasJS might:

  • Omit seconds in labels, even when critical for precision.
  • Choose intervals that create cluttered labels (e.g., 1-second intervals for a 30-second range).
  • Fail to align label formats with user expectations (e.g., showing 14:00 instead of 14:00:00).

Solution Overview

We’ll override the valueFormatString property only for sub-hour ranges, while letting CanvasJS handle larger ranges automatically.

Key Improvements:

  • Seconds precision for ranges < 1 minute.
  • Clean intervals (e.g., 5-second or 1-minute increments).
  • Dynamic transitions between formats (seconds → minutes → hours).

Strategy: Let CanvasJS Handle Long Ranges, You Handle Short Ranges

Zoom Level (Visible Range) CanvasJS Auto Format OK? Custom Format Suggested
≥ 1 month ✅ Yes ❌ No
≤ 10 days ⚠ Decent ✅ MMM DD hh:mm
≤ 1 day ❌ No ✅ hh:mm TT
≤ 1 hour ❌ No ✅ hh:mm TT
≤ 1 minute ❌ No ✅ hh:mm:ss TT
≤ 10 seconds ❌ No ✅ hh:mm:ss TT

Dynamic Axis Label Formatting for Short Ranges

function getFormatString(axis) {
    var min = axis.viewportMinimum || axis.minimum;
    var max = axis.viewportMaximum || axis.maximum;
    var rangeMs = max - min;
    var MINUTE = 60 * 1000;
    var HOUR = 60 * MINUTE;
    var DAY = 24 * HOUR;
    var MONTH = 30 * DAY; // Approximation
    var YEAR = 365 * DAY;

    if (rangeMs < 1 * MINUTE) 
        return "HH:mm:ss"; // <1 minute: show seconds
    if (rangeMs < 1 * HOUR) 
        return "HH:mm"; // 1min–1hr: minutes
    if (rangeMs < 1 * DAY) 
        return "HH:mm"; // 1hr–1 day: hours
    if (rangeMs < 10 * DAY) 
        return "MMM DD HH:mm"; // 1 day–10 days: day + month + hours
    if (rangeMs < 3 * MONTH) 
        return "MMM DD"; // 10 days–3 months: day + month
    if (rangeMs < 3 * YEAR) 
        return "MMM YYYY"; // 3 months–3 years: month + year

    return "YYYY"; // >3 years: year only
}

Hook Into Chart Events

var chart = new CanvasJS.Chart("chartContainer", {
    zoomEnabled: true,
    axisX: {
        valueFormatString: "MMM DD, YYYY" // Initial format, may be overridden
    },
    data: [{
        type: "line",
        xValueType: "dateTime",
        dataPoints: generateDataPoints() // Generate your time-series data
    }],
    rangeChanged: function (e) {
        var formatString = null;
        if (e.trigger != "reset")
            formatString = getFormatString(e.axisX[0]);
        e.chart.axisX[0].set("valueFormatString", formatString);
    }
});

chart.render();
// chart.axisX[0].set(“valueFormatString”,
// getFormatString(chart.axisX[0])); // Handle initial format, if not fixed

Why This Approach Works

  • Saves time: No need to manage complex interval settings.
  • Optimized for clarity: Users see what’s most relevant at each zoom level.
  • Minimal code: Focuses only on improving what CanvasJS doesn’t handle well.
  • Scalable: Works across dashboards, analytics apps, stock charts, and more.


By overriding intervals and formats only for sub-hour ranges, this approach balances customization with CanvasJS’s built-in intelligence. Developers gain fine-grained control over seconds/minutes without sacrificing the library’s robustness for larger timeframes. Implement this to build dashboards that shine at all zoom levels—from milliseconds to decades!