Skip to main content
Learn how to build ECharts that work perfectly on mobile devices with responsive sizing, touch interactions, and optimized performance.

Basic Responsive Chart

Create a chart that adapts to screen size:
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <title>Responsive Chart</title>
  <script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
    html, body {
      width: 100%;
      height: 100%;
    }
    #main {
      width: 100%;
      height: 100%;
    }
  </style>
</head>
<body>
  <div id="main"></div>
  <script>
    var chart = echarts.init(document.getElementById('main'));

    var option = {
      title: {
        text: 'Mobile Sales',
        left: 'center',
        top: 10,
        textStyle: {
          fontSize: 16
        }
      },
      tooltip: {
        trigger: 'axis',
        // Touch-friendly tooltip
        confine: true,
        backgroundColor: 'rgba(50, 50, 50, 0.9)',
        borderWidth: 0,
        textStyle: {
          fontSize: 14
        }
      },
      legend: {
        data: ['Sales'],
        bottom: 10,
        itemWidth: 20,
        itemHeight: 12,
        textStyle: {
          fontSize: 12
        }
      },
      grid: {
        left: 40,
        right: 20,
        top: 60,
        bottom: 50,
        containLabel: false
      },
      xAxis: {
        type: 'category',
        data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
        axisLabel: {
          fontSize: 11,
          rotate: 0
        }
      },
      yAxis: {
        type: 'value',
        axisLabel: {
          fontSize: 11
        }
      },
      series: [{
        name: 'Sales',
        type: 'line',
        data: [120, 200, 150, 80, 70, 110, 130],
        smooth: true,
        symbolSize: 8,
        lineStyle: {
          width: 3
        },
        areaStyle: {
          opacity: 0.3
        }
      }]
    };

    chart.setOption(option);

    // Handle window resize
    window.addEventListener('resize', function() {
      chart.resize();
    });

    // Handle orientation change
    window.addEventListener('orientationchange', function() {
      setTimeout(function() {
        chart.resize();
      }, 300);
    });
  </script>
</body>
</html>

Touch-Enabled DataZoom

Implement pinch-to-zoom and swipe interactions:
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <title>Touch DataZoom</title>
  <script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
  <style>
    html, body {
      width: 100%;
      height: 100%;
      margin: 0;
      padding: 0;
      overflow: hidden;
    }
    #chart {
      width: 100%;
      height: 100%;
    }
  </style>
</head>
<body>
  <div id="chart"></div>
  <script>
    var chart = echarts.init(document.getElementById('chart'));

    // Generate time series data
    var data = [];
    var baseTime = new Date().getTime();
    for (var i = 0; i < 100; i++) {
      data.push([
        new Date(baseTime + i * 3600000),
        Math.random() * 100 + 50
      ]);
    }

    var option = {
      title: {
        text: 'Touch to Zoom',
        left: 'center',
        top: 10,
        textStyle: {
          fontSize: 14
        }
      },
      tooltip: {
        trigger: 'axis',
        confine: true,
        axisPointer: {
          type: 'cross'
        }
      },
      grid: {
        left: 50,
        right: 20,
        top: 50,
        bottom: 80
      },
      xAxis: {
        type: 'time',
        axisLabel: {
          fontSize: 10
        }
      },
      yAxis: {
        type: 'value',
        scale: true,
        axisLabel: {
          fontSize: 10
        }
      },
      // Touch-friendly dataZoom
      dataZoom: [
        {
          type: 'inside',  // Enable pinch and swipe
          start: 0,
          end: 100,
          zoomOnMouseWheel: false,
          moveOnMouseMove: true,
          moveOnMouseWheel: false
        },
        {
          type: 'slider',
          start: 0,
          end: 100,
          bottom: 10,
          height: 20,
          handleSize: 30,  // Larger handle for touch
          textStyle: {
            fontSize: 10
          }
        }
      ],
      series: [{
        type: 'line',
        data: data,
        symbol: 'circle',
        symbolSize: 6,
        lineStyle: {
          width: 2
        },
        smooth: true
      }]
    };

    chart.setOption(option);

    // Responsive resize
    window.addEventListener('resize', function() {
      chart.resize();
    });
  </script>
</body>
</html>

Responsive Grid Layout

Adjust chart layout based on screen orientation:
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Adaptive Layout</title>
  <script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
  <style>
    body {
      margin: 0;
      padding: 10px;
      font-family: sans-serif;
    }
    #main {
      width: 100%;
      height: 400px;
    }
    @media (orientation: landscape) {
      #main {
        height: 60vh;
      }
    }
    @media (max-width: 600px) {
      #main {
        height: 300px;
      }
    }
  </style>
</head>
<body>
  <div id="main"></div>
  <script>
    var chart = echarts.init(document.getElementById('main'));

    function getResponsiveOption() {
      var width = window.innerWidth;
      var isPortrait = window.innerHeight > window.innerWidth;

      // Adjust based on screen size
      var fontSize = width < 400 ? 10 : (width < 600 ? 12 : 14);
      var legendOrient = isPortrait ? 'horizontal' : 'vertical';
      var legendPosition = isPortrait ? 
        { bottom: 5, left: 'center' } : 
        { right: 10, top: 'middle' };

      return {
        title: {
          text: 'Product Distribution',
          left: 'center',
          top: 10,
          textStyle: {
            fontSize: fontSize + 2
          }
        },
        tooltip: {
          trigger: 'item',
          confine: true,
          formatter: '{a} <br/>{b}: {c} ({d}%)'
        },
        legend: {
          orient: legendOrient,
          ...legendPosition,
          itemWidth: 15,
          itemHeight: 10,
          textStyle: {
            fontSize: fontSize - 2
          },
          data: ['Category A', 'Category B', 'Category C', 'Category D']
        },
        series: [
          {
            name: 'Products',
            type: 'pie',
            radius: isPortrait ? '50%' : '60%',
            center: isPortrait ? ['50%', '45%'] : ['40%', '50%'],
            label: {
              fontSize: fontSize - 1
            },
            data: [
              { value: 335, name: 'Category A' },
              { value: 310, name: 'Category B' },
              { value: 234, name: 'Category C' },
              { value: 135, name: 'Category D' }
            ],
            emphasis: {
              itemStyle: {
                shadowBlur: 10,
                shadowOffsetX: 0,
                shadowColor: 'rgba(0, 0, 0, 0.5)'
              }
            }
          }
        ]
      };
    }

    // Initial render
    chart.setOption(getResponsiveOption());

    // Update on resize and orientation change
    function handleResize() {
      chart.resize();
      chart.setOption(getResponsiveOption());
    }

    window.addEventListener('resize', handleResize);
    window.addEventListener('orientationchange', function() {
      setTimeout(handleResize, 300);
    });
  </script>
</body>
</html>

Touch-Optimized Tooltip

Create touch-friendly tooltips with proper positioning:
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Touch Tooltip</title>
  <script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
  <style>
    html, body {
      margin: 0;
      padding: 0;
      width: 100%;
      height: 100%;
    }
    #main {
      width: 100%;
      height: 100%;
    }
  </style>
</head>
<body>
  <div id="main"></div>
  <script>
    var chart = echarts.init(document.getElementById('main'));

    var option = {
      title: {
        text: 'Touch for Details',
        left: 'center',
        top: 15
      },
      tooltip: {
        trigger: 'axis',
        // Keep tooltip within chart bounds
        confine: true,
        // Custom position for touch
        position: function(point, params, dom, rect, size) {
          // Calculate position to keep tooltip visible
          var x = point[0];
          var y = point[1];
          var viewWidth = size.viewSize[0];
          var viewHeight = size.viewSize[1];
          var boxWidth = size.contentSize[0];
          var boxHeight = size.contentSize[1];

          // Position tooltip above touch point on mobile
          var posX = x - boxWidth / 2;
          var posY = y - boxHeight - 20;

          // Keep within bounds
          if (posX < 0) posX = 0;
          if (posX + boxWidth > viewWidth) posX = viewWidth - boxWidth;
          if (posY < 0) posY = y + 20;

          return [posX, posY];
        },
        backgroundColor: 'rgba(50, 50, 50, 0.95)',
        borderWidth: 0,
        textStyle: {
          fontSize: 13,
          color: '#fff'
        },
        padding: 10
      },
      grid: {
        left: 45,
        right: 20,
        top: 60,
        bottom: 40
      },
      xAxis: {
        type: 'category',
        data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
        axisLabel: {
          fontSize: 11
        }
      },
      yAxis: {
        type: 'value',
        axisLabel: {
          fontSize: 11
        }
      },
      series: [
        {
          name: 'Value',
          type: 'bar',
          data: [120, 200, 150, 80, 70, 110, 130],
          itemStyle: {
            borderRadius: [4, 4, 0, 0]
          },
          barWidth: '60%'
        }
      ]
    };

    chart.setOption(option);

    window.addEventListener('resize', function() {
      chart.resize();
    });
  </script>
</body>
</html>

Performance Optimization for Mobile

Optimize chart performance on mobile devices:
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Mobile Optimized</title>
  <script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
  <style>
    body {
      margin: 0;
      padding: 0;
    }
    #chart {
      width: 100vw;
      height: 100vh;
    }
  </style>
</head>
<body>
  <div id="chart"></div>
  <script>
    var chart = echarts.init(document.getElementById('chart'));

    // Generate large dataset
    var data = [];
    for (var i = 0; i < 10000; i++) {
      data.push([
        i,
        Math.sin(i / 100) * 50 + Math.random() * 20
      ]);
    }

    var option = {
      // Reduce animation for better performance
      animation: false,
      title: {
        text: 'Optimized for Mobile',
        left: 'center',
        top: 10,
        textStyle: {
          fontSize: 14
        }
      },
      tooltip: {
        trigger: 'axis',
        confine: true,
        // Reduce tooltip complexity
        formatter: '{c}'
      },
      grid: {
        left: 40,
        right: 15,
        top: 50,
        bottom: 60
      },
      xAxis: {
        type: 'value',
        axisLabel: {
          fontSize: 10
        }
      },
      yAxis: {
        type: 'value',
        axisLabel: {
          fontSize: 10
        }
      },
      dataZoom: [
        {
          type: 'inside',
          start: 0,
          end: 10,
          // Optimize touch interactions
          throttle: 50
        },
        {
          type: 'slider',
          start: 0,
          end: 10,
          bottom: 10,
          height: 15,
          handleSize: 25,
          // Optimize slider performance
          realtime: false
        }
      ],
      series: [{
        type: 'line',
        data: data,
        // No symbols for better performance
        symbol: 'none',
        // Enable sampling
        sampling: 'lttb',
        lineStyle: {
          width: 1
        },
        // Enable large mode
        large: true,
        largeThreshold: 500
      }]
    };

    chart.setOption(option);

    // Debounced resize
    var resizeTimeout;
    window.addEventListener('resize', function() {
      clearTimeout(resizeTimeout);
      resizeTimeout = setTimeout(function() {
        chart.resize();
      }, 100);
    });
  </script>
</body>
</html>

Responsive Font Sizes

Dynamically adjust text sizes based on viewport:
function getResponsiveFontSize() {
  var width = window.innerWidth;
  return {
    title: width < 400 ? 12 : (width < 600 ? 14 : 16),
    axis: width < 400 ? 9 : (width < 600 ? 10 : 12),
    legend: width < 400 ? 10 : (width < 600 ? 11 : 12),
    label: width < 400 ? 9 : (width < 600 ? 10 : 11)
  };
}

var fontSize = getResponsiveFontSize();

var option = {
  title: {
    textStyle: { fontSize: fontSize.title }
  },
  xAxis: {
    axisLabel: { fontSize: fontSize.axis }
  },
  yAxis: {
    axisLabel: { fontSize: fontSize.axis }
  },
  legend: {
    textStyle: { fontSize: fontSize.legend }
  }
};

Best Practices

Viewport Configuration

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">

Grid Spacing

Adjust grid spacing for mobile screens:
grid: {
  left: 40,    // Minimal left padding
  right: 15,   // Minimal right padding
  top: 60,     // Space for title
  bottom: 80,  // Space for legend/dataZoom
  containLabel: true  // Keep labels within grid
}

Touch Interactions

Optimize for touch:
// Larger touch targets
handleSize: 30,
symbolSize: 8,

// Touch-friendly dataZoom
dataZoom: [{
  type: 'inside',
  throttle: 50  // Smooth touch response
}]

Performance Tips

  1. Disable animations: Set animation: false for complex charts
  2. Reduce data: Show fewer data points on mobile
  3. Simplify styles: Avoid gradients, shadows
  4. Use sampling: Enable sampling: 'lttb' for line charts
  5. Debounce resize: Prevent excessive redraws

Responsive Patterns

// Adjust chart based on viewport
function isMobile() {
  return window.innerWidth < 768;
}

if (isMobile()) {
  option.legend.orient = 'horizontal';
  option.legend.bottom = 0;
  option.grid.bottom = 60;
} else {
  option.legend.orient = 'vertical';
  option.legend.right = 10;
  option.grid.right = 120;
}

Resize Handler Pattern

var chart = echarts.init(dom);
var resizeTimeout;

// Debounced resize
function handleResize() {
  clearTimeout(resizeTimeout);
  resizeTimeout = setTimeout(function() {
    chart.resize();
    // Optionally update options
    chart.setOption(getResponsiveOption());
  }, 100);
}

window.addEventListener('resize', handleResize);
window.addEventListener('orientationchange', function() {
  setTimeout(handleResize, 300);
});

// Cleanup on page unload
window.addEventListener('beforeunload', function() {
  chart.dispose();
});

Testing on Mobile

Browser DevTools

  1. Open Chrome DevTools (F12)
  2. Click device toolbar (Ctrl+Shift+M)
  3. Select device or custom dimensions
  4. Test touch interactions and orientations

Real Device Testing

  • Test on actual iOS and Android devices
  • Verify touch interactions (pinch, swipe)
  • Check performance with large datasets
  • Test in both portrait and landscape

Next Steps

Large Datasets

Optimize mobile charts for large data

Getting Started

Learn basic chart types and setup

Build docs developers (and LLMs) love