Responsive Data Visualization: Charts, Graphs, and Tables for Any Screen

Data visualization makes abstract numbers tangible, but only if it renders well on the viewer's device. A chart that looks stunning on a 27-inch monitor but breaks on mobile isn't just a design issue—it's a communication failure. Responsive data visualization ensures insights reach every user, regardless of screen size.
SVG: The Responsive Foundation
SVG scales infinitely because it's vector-based—it never pixelates. Use an SVG-based charting library (D3.js, visx, or Charts.css) rather than canvas-based rendering for responsive contexts:
import { LineChart } from '@visx/xychart';
function SalesChart({ data }) {
return (
<div style={{ width: '100%', height: 400 }}>
<LineChart
width="100%"
height="100%"
data={data}
xScale={{ type: 'time' }}
yScale={{ type: 'linear', domain: [0, maxRevenue] }}
>
<Axis orientation="bottom" />
<Axis orientation="left" />
<LineSeries dataKey="revenue" data={data} />
</LineChart>
</div>
);
}
Set chart containers to percentage-based widths and fixed or dynamic heights. Use ResizeObserver to re-render the chart when the container resizes:
import { useEffect, useRef, useState } from 'react';
function ResponsiveChart() {
const containerRef = useRef(null);
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
useEffect(() => {
const observer = new ResizeObserver((entries) => {
const { width, height } = entries[0].contentRect;
setDimensions({ width, height });
});
if (containerRef.current) {
observer.observe(containerRef.current);
}
return () => observer.disconnect();
}, []);
return (
<div ref={containerRef} style={{ width: '100%', height: 400 }}>
{dimensions.width > 0 && (
<ActualChart width={dimensions.width} height={dimensions.height} />
)}
</div>
);
}
Chart Selection for Small Screens
Not every chart type works on mobile. Apply breakpoint-aware chart transformations:
- Bar charts: Mobile → horizontal bars. Labels fit better and users can scroll vertically.
- Line charts: Reduce the number of series shown. A legend for 8 product lines overwhelms a 375 px screen.
- Pie charts: Avoid below 400 px viewport width. Switch to a sorted horizontal bar chart or a data table.
- Scatter plots: Reduce dot size and thin out data points. Aggressive overplotting makes patterns invisible.
function ChartSelector({ data, viewport }) {
if (viewport === 'mobile') {
return <HorizontalBarChart data={data} maxBars={8} />;
}
if (viewport === 'tablet') {
return <VerticalBarChart data={data} />;
}
return <GroupedBarChart data={data} />;
}
Data Tables: The Underrated Visualization
A well-designed data table often communicates more effectively than a chart on mobile. Prioritize data tables for dense information:
.data-table {
width: 100%;
border-collapse: collapse;
font-size: clamp(0.75rem, 2vw, 0.875rem);
}
@media (max-width: 600px) {
.data-table {
/* Stack rows into cards on mobile */
display: block;
thead {
display: none;
}
tr {
display: block;
padding: 0.75rem;
border: 1px solid var(--color-border);
margin-block-end: 0.5rem;
}
td {
display: grid;
grid-template-columns: 40% 60%;
gap: 0.25rem;
padding: 0.25rem 0;
border: none;
}
td::before {
content: attr(data-label);
font-weight: 600;
}
}
}
This transforms a standard table into stacked card-like rows where each cell has a visible label. The data-label attribute on each <td> makes the context clear:
<td data-label="Revenue">$42,500</td>
<td data-label="Growth">+12.3%</td>
Interactive Elements and Touch Targets
Touch targets on charts need to be at least 44×44 px for accessibility. Adjust hit areas accordingly:
const TOUCH_TARGET = 44;
function Bar({ x, y, width, height, value, onClick }) {
const hitWidth = Math.max(width, TOUCH_TARGET);
const hitX = x - (hitWidth - width) / 2;
return (
<rect
x={hitX}
y={y}
width={hitWidth}
height={height}
fill="transparent"
onClick={onClick}
style={{ cursor: 'pointer' }}
/>
);
}
Tooltips on mobile need special handling. A hover tooltip doesn't exist on touch devices. Use tap-to-show tooltips that persist until the user taps elsewhere:
function TooltipWrapper({ children, content }) {
const [visible, setVisible] = useState(false);
return (
<div
onClick={() => setVisible(!visible)}
onMouseEnter={() => setVisible(true)}
onMouseLeave={() => setVisible(false)}
>
{children}
{visible && <div className="tooltip">{content}</div>}
</div>
);
}
Loading and Empty States
Data visualizations are only useful when they have data. Handle loading, empty, and error states explicitly:
function DataVisualizer({ status, data }) {
if (status === 'loading') {
return <ChartSkeleton rows={5} />;
}
if (status === 'error') {
return <ErrorState message="Could not load chart data" onRetry={refetch} />;
}
if (!data || data.length === 0) {
return <EmptyState message="No data for the selected period" />;
}
return <ResponsiveChart data={data} />;
}
Skeleton states should mimic the chart layout. A bar chart skeleton uses placeholder bars; a line chart skeleton uses a wavy gradient line.
Accessibility for Data Visualizations
Charts must be accessible to screen reader users. Provide text alternatives:
<figure role="figure" aria-label="Monthly revenue January to June 2026">
<ResponsiveChart data={revenueData} />
<figcaption className="sr-only">
Monthly revenue: Jan $38K, Feb $42K, Mar $45K, Apr $51K, May $55K, Jun $62K
</figcaption>
</figure>
For complex charts, add keyboard navigation to navigate data points:
const ChartContainer = styled.div`
&:focus-visible {
outline: 2px solid var(--color-brand);
outline-offset: 2px;
}
`;
Responsive data visualization is about adaptation, not reduction. The mobile version of a chart should communicate the same insight as the desktop version—just presented differently. SVG foundations, viewport-aware chart selection, touch-optimized interactivity, and accessible fallbacks ensure that every user sees the story in the data.
Our web development team builds responsive dashboards and data visualizations that work on any device, with full accessibility support.
Related Insights

AI-Powered Personalization: Building Recommendation Systems for Web Apps
Learn how to build AI-powered personalization and recommendation systems for web applications using collaborative filtering, content-based approaches, and hybrid models.

Core Web Vitals Optimization: Achieving Great Lighthouse Scores in 2026
A practical guide to optimizing Core Web Vitals for great Lighthouse scores including LCP, FID, and CLS improvements for real-world web applications.

CSS Container Queries: Building Truly Responsive Components
Learn how to use CSS Container Queries for building component-level responsive designs that adapt to their container rather than the viewport.