В библиотеке Nivo ключевая идея адаптивной визуализации реализована через responsive-компоненты, которые автоматически подстраиваются под размеры контейнера. Вместо ручного задания ширины и высоты графика используется механизм измерения DOM-элемента, что позволяет компонентам корректно масштабироваться при любых изменениях layout.
Responsive-версия компонентов строится поверх базовых (например, Line, Bar, Pie), но полностью делегирует управление размерами контейнеру.
Основная особенность заключается в том, что рендер графика происходит только при наличии вычисленных размеров, что делает поведение предсказуемым в flex- и grid-раскладках.
Каждый responsive-компонент (например, ResponsiveLine,
ResponsiveBar) представляет собой обёртку над измерением
контейнера и передачей размеров в базовый график.
Внутренний процесс можно представить так:
width: 100% и
height: 100%width и
heightLine,
Bar, Pie)Таким образом достигается синхронизация графика с layout без ручного управления размерами.
Каждый responsive-компонент требует, чтобы родительский элемент имел явно заданную высоту. Без этого измерение высоты невозможно, и график не отображается.
Пример структуры:
import { ResponsiveLine } from '@nivo/line'
const data = [
{
id: 'series-1',
data: [
{ x: 0, y: 10 },
{ x: 1, y: 20 },
{ x: 2, y: 15 }
]
}
]
export default function Chart() {
return (
<div style={{ height: 400 }}>
<ResponsiveLine
data={data}
margin={{ top: 20, right: 20, bottom: 50, left: 50 }}
xScale={{ type: 'point' }}
yScale={{ type: 'linear', min: 0, max: 'auto' }}
/>
</div>
)
}
Ключевой аспект — контейнер задаёт высоту, компонент управляет шириной автоматически.
ResponsiveLine используется для построения линейных
графиков с адаптивной геометрией.
При изменении размеров контейнера происходит перерасчёт:
Особое значение имеет параметр margin, поскольку при
уменьшении контейнера визуальная область сокращается, но отступы
остаются фиксированными.
<ResponsiveLine
data={data}
margin={{ top: 50, right: 110, bottom: 50, left: 60 }}
xScale={{ type: 'point' }}
yScale={{ type: 'linear', min: 0, max: 'auto' }}
axisBottom={{ tickRotation: -45 }}
/>
При узких контейнерах часто возникает ситуация, когда плотность подписей оси X требует их поворота или сокращения.
ResponsiveBar отличается тем, что масштабирование влияет
не только на геометрию, но и на визуальную читаемость категорий.
При уменьшении ширины контейнера:
import { ResponsiveBar } from '@nivo/bar'
const data = [
{ country: 'A', value: 40 },
{ country: 'B', value: 70 },
{ country: 'C', value: 30 }
]
export default () => (
<div style={{ height: 300 }}>
<ResponsiveBar
data={data}
keys={['value']}
indexBy="country"
margin={{ top: 20, right: 20, bottom: 50, left: 50 }}
padding={0.3}
axisBottom={{ tickRotation: 0 }}
/>
</div>
)
Параметр padding начинает играть критическую роль в
responsive-режиме, так как влияет на минимальную читаемую ширину
столбца.
В круговых диаграммах адаптивность выражается через изменение радиуса окружности в зависимости от доступного пространства.
ResponsivePie вычисляет:
import { ResponsivePie } from '@nivo/pie'
const data = [
{ id: 'A', value: 30 },
{ id: 'B', value: 50 },
{ id: 'C', value: 20 }
]
export default () => (
<div style={{ height: 350 }}>
<ResponsivePie
data={data}
margin={{ top: 20, right: 20, bottom: 20, left: 20 }}
innerRadius={0.5}
padAngle={0.7}
/>
</div>
)
При уменьшении контейнера подписи могут автоматически перемещаться внутрь или скрываться, чтобы сохранить читаемость.
Responsive-компоненты Nivo зависят от вычисления размеров DOM-элемента. Основная проблема возникает при отсутствии заданной высоты у родителя.
Типичные ошибки:
height: auto без контентаmin-heightКорректная схема:
.chart-wrapper {
width: 100%;
height: 400px;
}
или flex-раскладка:
.container {
display: flex;
height: 100vh;
}
.chart {
flex: 1;
}
Responsive-компоненты используют механизм отслеживания изменений контейнера. При ресайзе окна происходит:
Это обеспечивает корректную работу в dashboard-интерфейсах, где панели могут динамически изменять размер.
Особенно важно учитывать производительность при частых изменениях
размеров: анимации (motion) и сложные сетки могут
увеличивать нагрузку.
При серверном рендеринге отсутствует доступ к DOM, поэтому размеры контейнера неизвестны.
Типичное поведение:
В некоторых случаях применяется отложенный рендер:
const [mounted, setMounted] = useState(false)
useEffect(() => {
setMounted(true)
}, [])
return (
<div style={{ height: 400 }}>
{mounted && <ResponsiveLine data={data} />}
</div>
)
Адаптивность не ограничивается геометрией. theme и
margin влияют на поведение графика при разных размерах
контейнера.
Пример:
<ResponsiveLine
data={data}
margin={{ top: 30, right: 30, bottom: 80, left: 60 }}
theme={{
axis: {
ticks: {
text: {
fontSize: 12
}
}
}
}}
/>
Несмотря на автоматизацию, существуют ограничения:
Responsive-подход эффективен в дашбордах, но требует балансировки между автоматикой и настройками отображения.