
Привет, Хабрчане! Это Леша Жиряков, техлид backend-команды витрины KION. В прошлый раз я писал о секретах популярности Python, а сегодня будет пост о разработке на React. Расскажу, какие библиотеки стоит добавить в свой набор в 2025 году, приведу плюсы и минусы каждой, данные с GitHub и примеры использования. Начнем!
Tremor

Tremor — UI-библиотека для создания дашбордов и аналитических интерфейсов в React. В отличие от инструментов, заточенных исключительно на визуализации данных, Tremor предлагает готовые UI-компоненты: карточки, таблицы, кнопки, графики, фильтры и так далее. Библиотека построена на Tailwind CSS (об этом в блоге писал коллега), так что она удобна для кастомизации.
Главная идея Tremor — ускорить разработку дашбордов, предоставив хорошо стилизованные и адаптивные компоненты. Например, можно за несколько строк кода создать карточку с ключевой метрикой или быстро интегрировать диаграмму из Recharts, Nivo или ECharts. Поддерживается темная и светлая темы, предусмотрена адаптация к разным экранам.
Плюсы:
готовые UI-компоненты, сокращающие время разработки;
легкая интеграция с Recharts, Nivo и ECharts;
гибкость благодаря Tailwind CSS.
Минусы:
ограниченная кастомизация в сравнении с чистыми UI-фреймворками;
не предназначен для сложных и нестандартных интерфейсов.
количество звезд: 2,1 тыс.;
открытых Issues: 11;
год релиза: 2024;
Merge Requests (MRs): разработчики проекта и сообщество.
Отличный выбор, если нужно быстро развернуть дашборд с минимальными усилиями, но для более детальной настройки могут потребоваться другие UI-библиотеки.
Еще о сильных сторонах
Tremor содержит в себе множество готовых компонентов: графики (Area Chart, Bar Chart, Donut Chart, Progress Bar), элементы для ввода (Calendar, Checkbox, Date Picker, Radio Card Group) и другие UI-элементы (Badge, Button, Dialog, Table).
Например, мы хотим добавить кнопку (Button). В документации Tremor весь код уже написан: Ctrl + C, Ctrl + V — и вперед!
Устанавливаем зависимости:
npm install @radix-ui/react-slot tailwind-variants @remixicon/react
Добавляем код в директорию проекта, где хранятся компоненты. Важно не забыть обновить импорты:
// Tremor Button [v0.2.0]
import React from "react"
import { Slot } from "@radix-ui/react-slot"
import { RiLoader2Fill } from "@remixicon/react"
import { tv, type VariantProps } from "tailwind-variants"
import { cx, focusRing } from "@/lib/utils"
const buttonVariants = tv({
base: [
// base
"relative inline-flex items-center justify-center whitespace-nowrap rounded-md border px-3 py-2 text-center text-sm font-medium shadow-sm transition-all duration-100 ease-in-out",
// disabled
"disabled:pointer-events-none disabled:shadow-none",
// focus
focusRing,
],
variants: {
variant: {
primary: [
// border
"border-transparent",
// text color
"text-white dark:text-white",
// background color
"bg-blue-500 dark:bg-blue-500",
// hover color
"hover:bg-blue-600 dark:hover:bg-blue-600",
// disabled
"disabled:bg-blue-300 disabled:text-white",
"disabled:dark:bg-blue-800 disabled:dark:text-blue-400",
],
secondary: [
// border
"border-gray-300 dark:border-gray-800",
// text color
"text-gray-900 dark:text-gray-50",
// background color
"bg-white dark:bg-gray-950",
//hover color
"hover:bg-gray-50 dark:hover:bg-gray-900/60",
// disabled
"disabled:text-gray-400",
"disabled:dark:text-gray-600",
],
light: [
// base
"shadow-none",
// border
"border-transparent",
// text color
"text-gray-900 dark:text-gray-50",
// background color
"bg-gray-200 dark:bg-gray-900",
// hover color
"hover:bg-gray-300/70 dark:hover:bg-gray-800/80",
// disabled
"disabled:bg-gray-100 disabled:text-gray-400",
"disabled:dark:bg-gray-800 disabled:dark:text-gray-600",
],
ghost: [
// base
"shadow-none",
// border
"border-transparent",
// text color
"text-gray-900 dark:text-gray-50",
// hover color
"bg-transparent hover:bg-gray-100 dark:hover:bg-gray-800/80",
// disabled
"disabled:text-gray-400",
"disabled:dark:text-gray-600",
],
destructive: [
// text color
"text-white",
// border
"border-transparent",
// background color
"bg-red-600 dark:bg-red-700",
// hover color
"hover:bg-red-700 dark:hover:bg-red-600",
// disabled
"disabled:bg-red-300 disabled:text-white",
"disabled:dark:bg-red-950 disabled:dark:text-red-400",
],
},
},
defaultVariants: {
variant: "primary",
},
})
interface ButtonProps
extends React.ComponentPropsWithoutRef<"button">,
VariantProps<typeof buttonVariants> {
asChild?: boolean
isLoading?: boolean
loadingText?: string
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
(
{
asChild,
isLoading = false,
loadingText,
className,
disabled,
variant,
children,
...props
}: ButtonProps,
forwardedRef,
) => {
const Component = asChild ? Slot : "button"
return (
<Component
ref={forwardedRef}
className={cx(buttonVariants({ variant }), className)}
disabled={disabled || isLoading}
tremor-id="tremor-raw"
{...props}
>
{isLoading ? (
<span className="pointer-events-none flex shrink-0 items-center justify-center gap-1.5">
<RiLoader2Fill
className="size-4 shrink-0 animate-spin"
aria-hidden="true"
/>
<span className="sr-only">
{loadingText ? loadingText : "Loading"}
</span>
{loadingText ? loadingText : children}
</span>
) : (
children
)}
</Component>
)
},
)
Button.displayName = "Button"
export { Button, buttonVariants, type ButtonProps }
Используем такой блок кода:
import { Button } from '@/components/Button';
export const ButtonHero = () => (
<div className="flex justify-center">
<Button asChild>
<a href="#hello">Hello, Habr!</a>
</Button>
</div>
);
Готово! Получаем следующую кнопку:

Planby

Специализированный React-компонент для отображения таймлайнов, расписаний и диаграмм Ганта. В отличие от универсальных библиотек визуализации вроде Recharts или Nivo, Planby заточен именно под временные шкалы и позволяет эффективно рендерить большие объемы данных.
Библиотека использует виртуализацию, поэтому она хороша даже при работе с тысячами событий. Поддерживает горизонтальную прокрутку, масштабирование временной шкалы и кастомизацию элементов. Planby удобен для интеграции в дашборды, системы управления проектами и любые приложения, где нужно визуализировать временные интервалы.
Плюсы:
гибкость в кастомизации отображения событий;
поддержка горизонтальной прокрутки и масштабирования.
Минусы:
подходит только для таймлайнов и расписаний;
ограниченная документация и сравнительно небольшое сообщество;
может потребовать дополнительных стилей для полной адаптации под дизайн системы.
Количество звезд: 1,5 тыс.
Открытых Issues: 6.
Версия: 1.1.7.
Год релиза: 2022.
Merge Requests (MRs): главный разработчик и сообщество. Сейчас открытых MRs и нет, последние изменения вносились в проект два года назад.
Если нужно отобразить расписание, временную диаграмму или диаграмму Ганта в React, Planby — одно из лучших решений.
Еще о сильных сторонах
Есть три способа, как можно использовать Planby.
Стандартный шаблон:
import { useEpg, Epg, Layout } from 'planby';
const channels = React.useMemo(
() => [
{
logo: 'https://via.placeholder.com',
uuid: '10339a4b-7c48-40ab-abad-f3bcaf95d9fa',
...
},
],
[]
);
const epg = React.useMemo(
() => [
{
channelUuid: '30f5ff1c-1346-480a-8047-a999dd908c1e',
description:
'Ut anim nisi consequat minim deserunt...',
id: 'b67ccaa3-3dd2-4121-8256-33dbddc7f0e6',
image: 'https://via.placeholder.com',
since: "2022-02-02T23:50:00",
till: "2022-02-02T00:55:00",
title: 'Title',
...
},
],
[]
);
const {
getEpgProps,
getLayoutProps,
onScrollToNow,
onScrollLeft,
onScrollRight,
} = useEpg({
epg,
channels,
startDate: '2022/02/02', // or 2022-02-02T00:00:00
});
return (
<div>
<div style={{ height: '600px', width: '1200px' }}>
<Epg {...getEpgProps()}>
<Layout
{...getLayoutProps()}
/>
</Epg>
</div>
</div>
);
Настройка ширины и высоты:
const {
getEpgProps,
getLayoutProps,
...
} = useEpg({
epg,
channels,
startDate: '2022/02/02', // or 2022-02-02T00:00:00
width: 1200,
height: 600
});
return (
<div>
<Epg {...getEpgProps()}>
<Layout
{...getLayoutProps()}
/>
</Epg>
</div>
Настройка временного диапазона:
const {
getEpgProps,
getLayoutProps,
...
} = useEpg({
epg,
channels,
startDate: '2022-02-02T10:00:00',
endDate: '2022-02-02T20:00:00',
width: 1200,
height: 600
});
return (
<div>
<Epg {...getEpgProps()}>
<Layout
{...getLayoutProps()}
/>
</Epg>
</div>
Так выглядит горизонтальный скролл:

React DnD

React DnD — библиотека для реализации Drag & Drop в React на основе HTML5 API. Позволяет настраивать перетаскивание элементов через React-компоненты, упрощая код. Еще одно достоинство — гибкость и высокая производительность.
В отличие от более простых решений, таких как react-beautiful-dnd, библиотека предлагает гибкую систему декораторов (useDrag и useDrop) и контекстов, так что она универсальнее. React DnD поддерживает сложные сценарии перетаскивания, включая вложенные иерархии, перенос элементов между разными списками и обработку состояния в Redux.
Плюсы:
легковесная, интегрируется с React;
гибкость — можно настраивать практически любые сценарии;
хорошая поддержка и документация.
Минусы:
сложнее в освоении, чем react-beautiful-dnd;
требует работы с useDrag и useDrop, что может быть непривычно.
количество звезд: 21,3 тыс.;
открытых Issues: 435;
версия: 16.0.0;
год релиза: 2014;
Merge Requests (MRs): основные разработчики и сообщество.
Если нужен гибкий и мощный инструмент для Drag & Drop, React DnD — отличный выбор. Но если задача ограничивается перетаскиванием элементов в списке, лучше использовать react-beautiful-dnd.
Еще о сильных сторонах
В React DnD два основных компонента: DndProvider и DragPreviewImage, — которые позволяют реализовывать различные сценарии.
DndProvider — предоставляет возможности React-DnD для приложения. Должен быть интегрирован в бэкенд через свойство backend, а еще может быть добавлен с помощью объекта window.
Пример использования:
import { HTML5Backend } from 'react-dnd-html5-backend'
import { DndProvider } from 'react-dnd'
export default class YourApp {
render() {
return (
<DndProvider backend={HTML5Backend}>
/* Ваше Drag-and-Drop приложение */
</DndProvider>
)
}
}
DragPreviewImage — компонент, который отрисовывает HTML-изображение в качестве отсоединяемого перетаскиваемого предварительного просмотра:
import { DragSource, DragPreviewImage } from 'react-dnd'
function DraggableHouse({ connectDragSource, connectDragPreview }) {
return (
<>
<DragPreviewImage src="sun_dragged.png" connect={connectDragPreview} />
<div ref={connectDragSource}>☀️</div>
</>
)
}
export default DragSource(
/* ... */
(connect, monitor) => ({
connectDragSource: connect.dragSource(),
connectDragPreview: connect.dragPreview()
})
)
Advanced Cropper

Advanced Cropper — библиотека для точного кадрирования изображений в React. Она дает гибкие инструменты для масштабирования, вращения, зума и ограничения областей обрезки. Поддерживаются touch-жесты, что пригодится для мобильных устройств.
Главное преимущество Advanced Cropper — высокая точность и возможность глубокой кастомизации. Можно задавать фиксированные пропорции, управлять ограничениями области обрезки и добавлять пользовательские элементы управления. Библиотека поддерживает canvas-отрисовку, что делает ее производительной даже при работе с большими изображениями.
Плюсы:
поддержка зума, вращения и ограничения области обрезки;
гибкость: можно настраивать взаимодействие и UI под свои нужды;
хорошо адаптирован для мобильных устройств.
Минусы:
требует работы с canvas, если нужна глубокая кастомизация;
не самый легковесный вариант.
количество звезд: 763;
открытых Issues: 23;
версия: 0.20.1;
год релиза: 2021;
Merge Requests (MRs): главный разработчик и сообщество.
Если в проекте требуется продвинутая обрезка изображений с точными настройками, Advanced Cropper — отличный выбор.
Еще о сильных сторонах
Для использования нужно добавить в приложение такой код:
import React, { useState } from 'react';
import { CropperRef, Cropper } from 'react-advanced-cropper';
import 'react-advanced-cropper/dist/style.css'
export const GettingStartedExample = () => {
const [image, setImage] = useState(
'https://cdn.prod.website-files.com/65cb50148a423f63add18bc6/65ce1bf224944099ef1159b3_habr.png',
);
const onChange = (cropper: CropperRef) => {
console.log(cropper.getCoordinates(), cropper.getCanvas());
};
return (
<Cropper
src={image}
onChange={onChange}
className={'cropper'}
/>
)
};
На колесико мыши работает зум:

React Resizable Panels

React Resizable Panels — библиотека для создания изменяемых по размеру панелей в React-приложениях. Позволяет перетаскивать границы между панелями, менять их ширину или высоту. Отмечу еще гладкие анимации и отзывчивый интерфейс.
В отличие от ручной реализации через div и onMouseMove, библиотека решает все проблемы с перерисовкой, управлением состоянием и совместимостью с адаптивным дизайном. Можно легко создавать гибкие лейауты, включая боковые панели, редакторы с разделенными областями и многосекционные дашборды.
Плюсы:
простая интеграция и декларативный API;
поддержка горизонтального и вертикального изменения размеров;
хорошо работает с анимациями и адаптивным дизайном.
Минусы:
подходит только для работы с панелями, для сложных лейаутов может потребоваться CSS Grid или Flexbox.
количество звезд: 4,3 тыс.;
открытых Issues: 17;
версия: 2.1.7;
год релиза: 2022;
Merge Requests (MRs): главный разработчик и сообщество.
Если нужно удобное изменение размеров панелей без боли, React Resizable Panels — хорошее решение. Оно идеально для редакторов кода, дашбордов и приложений с гибкой компоновкой.
Еще о сильных сторонах
В React Resizable Panels можно создать горизонтальный и вертикальный макеты. Сначала о первом:
<PanelGroup direction="horizontal">
<Panel defaultSize={30} minSize={20}>
left
</Panel>
<PanelResizeHandle />
<Panel minSize={30}>
middle
</Panel>
<PanelResizeHandle />
<Panel defaultSize={30} minSize={20}>
right
</Panel>
</PanelGroup>
Чтобы изменить размер, нужно кликнуть на пустое пространство между панелями и зажать левую кнопку мыши. Потом двигать курсор влево или вправо — будет меняться размер панелей:

Теперь про вертикальный:
<PanelGroup direction="vertical">
<Panel maxSize={75}>
top
</Panel>
<PanelResizeHandle />
<Panel maxSize={75}>
bottom
</Panel>
</PanelGroup>
Принцип работы тут похожий:

ShadCN UI

ShadCN UI — коллекция UI-компонентов для React, созданная поверх Radix UI и стилизованная с помощью Tailwind CSS. В отличие от традиционных библиотек компонентов (например, MUI или Ant Design), ShadCN не поставляется как отдельный npm-пакет, а предлагает компоненты в виде кода, который можно скопировать в свой проект и кастомизировать.
Главная идея ShadCN UI — гибкость и контроль. Вместо жестко заданных стилей и ограничений — готовые элементы, их легко адаптировать под дизайн системы. Они выглядят минималистично и отлично вписываются в проекты, использующие Tailwind CSS. В библиотеке есть кнопки, поля ввода, диалоги, таблицы, дропдауны и другие базовые элементы UI.
Плюсы:
полный контроль над кодом компонентов, их можно редактировать под себя;
использует Radix UI, что дает доступность (a11y) и хорошую семантику;
отлично совместим с Tailwind CSS;
минималистичный дизайн, легко адаптировать под кастомный UI.
Минусы:
нужно вручную копировать и поддерживать компоненты в проекте;
не такая богатая экосистема, как у Ant Design или MUI;
может не подойти, если нужен полный набор готовых решений «из коробки».
количество звезд: 83,6 тыс.;
открытых Issues: 840;
версия: 2.3.0;
год релиза: 2023;
Merge Requests (MRs): основные разработчики и сообщество.
ShadCN UI — хороший выбор для тех, кто хочет гибкость Tailwind CSS, удобство Radix UI и контроль над кодом компонентов. Это отличный вариант для кастомных дизайн-систем и современных React-приложений.
Еще о сильных сторонах
В ShadCN UI имеется целый набор готовых компонентов: Accordion (не музыкальный инструмент аккордеон, а вертикально расположенный набор интерактивных заголовков, в каждом из которых находится свой контент), Alert, Card, Chart, Checkbox, Data Table, Dialog, Drawer, Menubar и другие.
Чтобы создать Menubar, необходимо добавить импорты:
import {
Menubar,
MenubarContent,
MenubarItem,
MenubarMenu,
MenubarSeparator,
MenubarShortcut,
MenubarTrigger,
} from "@/components/ui/menubar"
И использовать такую структуру:
<Menubar>
<MenubarMenu>
<MenubarTrigger>File</MenubarTrigger>
<MenubarContent>
<MenubarItem>
New Tab <MenubarShortcut>⌘T</MenubarShortcut>
</MenubarItem>
<MenubarItem>New Window</MenubarItem>
<MenubarSeparator />
<MenubarItem>Share</MenubarItem>
<MenubarSeparator />
<MenubarItem>Print</MenubarItem>
</MenubarContent>
</MenubarMenu>
</Menubar>
Пример, как выглядит компонент Menubar:

На сегодня все. Сейчас думаю еще над парой подборок с хорошо зарекомендовавшими себя инструментами. Добавляйте в комментариях, чем пользуетесь вы, — это сделает пост еще полезнее!