Библиотека Nivo построена вокруг идеи декларативного описания графиков с возможностью точечного расширения через переопределяемые компоненты. Основной принцип заключается в том, что любой визуальный элемент внутри графика — узел, подпись, подсказка, слой, маркер — может быть заменён собственной реализацией, не затрагивая остальную систему рендеринга.
Ключевая особенность архитектуры заключается в том, что график собирается как композиция слоёв и компонентов, каждый из которых отвечает за строго ограниченную часть визуализации. Это позволяет внедрять кастомные элементы без переписывания логики построения данных.
В большинстве визуализаций Nivo используется концепция слоёв (layers). Каждый слой — это функция или React-компонент, который получает доступ к текущему состоянию графика и отрисовывает часть SVG/Canvas.
Типичный порядок слоёв в линейном графике может выглядеть так:
Каждый слой можно заменить или дополнить.
<ResponsiveLine
data={data}
layers={[
'grid',
'markers',
'axes',
'lines',
'points',
customLayer,
'legends'
]}
/>
Функция кастомного слоя принимает контекст визуализации:
const customLayer = ({ series, xScale, yScale, innerWidth, innerHeight }) => {
return (
<g>
{series.map(s => (
<circle
key={s.id}
cx={xScale(s.data[0].x)}
cy={yScale(s.data[0].y)}
r={6}
fill="red"
/>
))}
</g>
);
};
Внутри слоя доступен весь набор преобразованных данных, что позволяет строить дополнительные визуальные элементы поверх основной графики.
Каждый тип графика предоставляет возможность заменить отрисовку базовых элементов данных.
В ResponsiveLine можно переопределить компонент точек
через pointComponent.
const CustomPoint = ({ x, y, size, color, data }) => (
<g transform={`translate(${x},${y})`}>
<rect
width={size}
height={size}
fill={color}
transform={`translate(${-size / 2},${-size / 2})`}
/>
</g>
);
Подключение:
<ResponsiveLine
data={data}
pointComponent={CustomPoint}
/>
Такой подход позволяет заменить стандартные круги на любые SVG-примитивы или даже композиции элементов.
В столбчатых графиках используется barComponent, который
управляет отрисовкой каждого столбца.
const CustomBar = ({ x, y, width, height, color, data }) => (
<g>
<rect
x={x}
y={y}
width={width}
height={height}
fill={color}
rx={4}
/>
<text
x={x + width / 2}
y={y - 6}
textAnchor="middle"
fontSize={10}
>
{data.value}
</text>
</g>
);
Подсказки в Nivo реализованы как React-компоненты, что делает их полностью заменяемыми. Они получают контекст точки или области, над которой находится курсор.
const CustomTooltip = ({ point }) => {
return (
<div style={{ padding: 12, background: 'white', border: '1px solid #ccc' }}>
<strong>{point.data.x}</strong>
<div>{point.data.y}</div>
</div>
);
};
Подключение:
<ResponsiveLine
data={data}
tooltip={CustomTooltip}
/>
В более сложных сценариях tooltip может агрегировать данные нескольких серий или выполнять вычисления на лету, например отображать процентное изменение или скользящее среднее.
Подписи в Nivo часто являются частью слоёв или отдельных компонентов внутри элементов данных.
const CustomLabel = ({ datum, label, percent }) => (
<g>
<text
textAnchor="middle"
dominantBaseline="central"
fill="#000"
>
{label}
</text>
<text
y={14}
textAnchor="middle"
fontSize={10}
fill="#666"
>
{`${Math.round(percent * 100)}%`}
</text>
</g>
);
Подключение:
<Pie
data={data}
arcLabel={CustomLabel}
/>
Многие компоненты Nivo допускают передачу функций вместо JSX-компонентов. Это позволяет управлять рендерингом на основе текущего состояния графика.
Например, настройка цвета точки в зависимости от значения:
const getPointColor = point =>
point.data.y > 100 ? 'red' : 'green';
<ResponsiveLine
data={data}
colors={getPointColor}
/>
Такой подход используется для динамической стилизации без необходимости полного переопределения компонента.
Маркерные линии и аннотации реализуются как отдельные слои или компоненты поверх графика.
const Marker = ({ x1, x2, y1, y2 }) => (
<line
x1={x1}
x2={x2}
y1={y1}
y2={y2}
stroke="black"
strokeDasharray="4 4"
/>
);
Использование позволяет добавлять:
Хотя тема не является визуальным компонентом напрямую, она влияет на все кастомные элементы через контекст.
const theme = {
axis: {
ticks: {
text: {
fontSize: 12,
fill: '#333'
}
}
},
grid: {
line: {
stroke: '#eee'
}
}
};
<ResponsiveLine
data={data}
theme={theme}
/>
Кастомные компоненты должны учитывать тему, чтобы сохранять визуальную согласованность.
Основной рендер в Nivo осуществляется через SVG, что делает возможным комбинирование стандартных графических примитивов с кастомной геометрией.
Внутри кастомных компонентов можно использовать:
<circle><rect><path><foreignObject> для HTML-вставокПример HTML внутри графика:
<foreignObject x={x} y={y} width={100} height={50}>
<div style={{ fontSize: 10 }}>
динамический блок
</div>
</foreignObject>
Каждый кастомный компонент получает строго типизированный набор данных, который зависит от типа графика. Это может включать:
Эта модель позволяет строить полностью управляемые визуальные элементы без обращения к DOM напрямую.
На практике кастомные компоненты редко используются изолированно. Чаще применяется комбинация:
<ResponsiveLine
data={data}
pointComponent={CustomPoint}
tooltip={CustomTooltip}
layers={[
'grid',
'markers',
customAnnotations,
'axes',
'lines',
'points',
customOverlay
]}
/>
Такая композиция превращает стандартный график в специализированный визуальный инструмент под конкретную предметную область.
При создании собственных компонентов важно учитывать особенности внутреннего рендеринга:
Эти особенности определяют архитектурные решения при создании сложных визуализаций на базе Nivo