
На старте проекта обычно встает вопрос о выборе готовой ui-библиотеки для решения шаблонных задач, таких как создание форм, инпутов, кнопок и других компонентов. Количество готовых ui-библиотек для React так стремительно растет, что уже сложно остановить свой выбор на какой либо из них. Зато в таком разнообразии каждый может найти библиотеку, подходящую под его задачи. В этой статье хочется рассказать о фреймворке Steroids, который разработан и поддерживается в нашей компании.
Изначально мы не планировали создавать фреймворк, а просто собирали удачные решения рутинных задач. Получился набор полезных утилит и мини-библиотек, который позволял нам работать быстрее. Мы постепенно добавляли в него новые элементы, он рос и видоизменялся, и в итоге вырос в полноценный фреймворк Steroids.
Что же такое Steroids и для каких задач он может подойти?
Steroids — это фреймворк, который задает архитектуру для всего проекта, а также содержит набор как простых, так и комплексных ui-компонентов, инструменты для взаимодействия с бэкендом и работой с формами, компоненты для работы с хранилищем браузера, websocket и другими технологиями.
Фреймворк Steroids подходит для создания полноценного SPA приложения с продуманной архитектурой, которое содержит в себе:
роутинг и управление состоянием, в том числе с использованием Redux, React Context;
взаимодействие с сервером — отправка запросов, обработка ошибок;
работу с формами — валидация, отправка данных на сервер, сохранение данных полей формы в state manager;
загрузку и отображение данных в виде списка или таблицы, сортировка, пагинация, фильтрация данных;
локализацию всего приложения;
использование серверного рендеринга приложения (SSR).
Основные фичи и особенности фреймворка
У фреймворка Steroids много особенностей и фич, о которых стоит рассказать. Всё это точно не влезет в одну статью. Поэтому здесь пойдет речь про некоторые из них:
фреймворк задаёт определенную структуру проекта;
все ui-компоненты разделены на
core
иview
части;во фреймворк входят не только ui-компоненты, но и логические компоненты, которые «под капотом» используют другие инструменты или библиотеки.
Структура проекта
Фреймворк Steroids задаёт определенную структуру папок и файлов проекта. Эта структура предполагает отдельные директории для переиспользуемых компонентов, редьюсеров и экшенов, роутов приложения, стилей, модальных окон и так далее. Ниже приведен пример такой структуры:
├── node_modules
├── public
├── src
│ ├── actions
│ ├── reducers
│ ├── modals
│ ├── routes
│ │ └── IndexPage
│ │ └── views
│ │ ├── MyBox.ts
│ │ └── MyBox.scss
│ │ ├── IndexPage.ts
│ │ ├── IndexPage.scss
│ │ └── index.ts
│ ├── shared
│ │ └── Layout
│ │ ├── Layout.ts
│ │ ├── Layout.scss
│ │ └── index.ts
│ ├── ui
│ │ └── form
│ │ ├── Button
│ │ │ ├── ButtonView.tsx
│ │ │ └── ButtonView.scss
│ │ └── CustomField
│ │ ├── CustomField.ts
│ │ ├── CustomFieldView.tsx
│ │ └── CustomFieldView.scss
│ ├── style
│ │ ├── index.scss
│ │ └── variables.scss
│ ├── Application.tsx
│ └── index.tsx
├── .eslintrc
├── index.d.ts
├── package.json
├── tsconfig.json
├── webpack.js
└── yarn.lock
Созданное на основе Boilerplate приложение имеет нужную структуру, в нём созданы все необходимые папки и файлы. Оно представляет собой тривиальное SPA-приложение с единственной страницей, и сразу после развертывания готово к запуску. Это приложение можно наполнить своими страницами и компонентами, располагая их в соответствующих структуре папках.
Разделение логики и отображения компонентов
Чтобы логика работы компонента была отделена от его отображения, каждый ui-компонент разделён на core и view часть.
Core — ядро компонента. Оно содержит всю бизнес-логику компонента, принимает и обрабатывает полученные данные из props, находит или получает нужную «вьюшку» (view-часть компонента) и прокидывает в неё подготовленные данные. Core-компонент практические не содержит jsx кода и обращений напрямую к DOM.
View — отображение компонента. Это часть, которая отвечает за непосредственную отрисовку компонента. Как правило она содержит только jsx код и scss стили компонента, без бизнес-логики. При кастомизации компонента его View.tsx
и/или View.scss
файлы при необходимости могут полностью копироваться из фреймворка Steroids и изменяться под дизайн конкретного проекта.
Если при разработке достаточно стандартного отображения компонента, то его можно просто импортировать из core-части фреймворка Steroids (npm-пакет @steroids/core
) и использовать в нужном месте. Если же есть потребность как-то кастомизировать компонент под дизайн проекта, то можно воспользоваться несколькими способами, один из которых работает как раз благодаря разделению компонента на core- и view-части.
Вот три основных способа кастомизации:
Передача через пропсы css-классов или объекта
style
.Переопределение css-переменных.
Переопределение view-части компонента — его стилей или tsx-кода.
Как использовать первые два способа, думаю, понятно. Они есть в большинстве готовых ui-библиотек. Поэтому перейдем к примерам того, как можно переопределить view часть.
Первый и самый простой способ — передать кастомное отображение через проп view
:
const CustomInputFieldView = (props: IInputFieldViewProps) => (
<div>
<span>Кастомное отображение инпута</span>
<input
{...props.inputProps}
placeholder={props.placeholder}
disabled={props.disabled}
required={props.required}
/>
</div>
);
const MyForm = () => (
<Form>
{/* инпут со стандартным отображением */}
<InputField attribute='name' />
{/* инпут с кастомным отображением */}
<InputField
attribute='surname'
view={CustomInputFieldView}
/>
</Form>
);
Этот способ подойдет, если нужно переопределить какой то конкретный компонент, например инпут для поиска в шапке сайта. Если же нужно добавить кастомное отображение для всех инпутов в проекте, то лучше воспользоваться другим способом.
Второй способ заключается в полной замене стандартных view-частей компонентов на кастомные. В этом случае «вьюшки» применяются ко всем компонента сразу, например ко всем Button или ко всем Form.
Файлы со стилями компонентов подключаются в src/style/index.scss
через конструкцию @use
, например:
@use '~@steroidsjs/bootstrap/form/InputField/InputFieldView';
А файлы с tsx-кодом компонента в src/ui/bootstrap.ts
, например:
'form.InputFieldView': {
lazy: () => require('@steroidsjs/bootstrap/form/InputField/InputFieldView').default
},
Чтобы переопределить view-часть, достаточно изменить пути к файлам — к scss-файлу или tsx-файлу, смотря что нужно кастомизировать.
Логические компоненты
Одна из особенностей Steroids состоит в том, что «из коробки» доступны не только ui-компоненты, но и логические компоненты или, по другому, компоненты-обертки над другими инструментами и библиотеками. Каждый компонент представляет из себя класс с набором свойств и методов.
Инициализируются эти компоненты в проекте в файле src/Application.tsx
в константе config
в поле components
:
export const config = {
reducers: require('./reducers').default,
routes: () => require('routes').default,
layoutView: () => require('shared/Layout').default,
screen: {},
theme: {},
components: {
locale: LocaleComponent,
http: HttpComponent,
resource: ResourceComponent,
},
}
В этом же конфиге можно задать свойства для компонентов, например:
components: {
http: {
className: HttpComponent,
apiUrl: 'https://steroids.dev/',
},
},
Используются такие компоненты внутри обычных react компонентов, а получить их можно из хука useComponents
. Пример:
export default function Header() {
const bem = useBem('Header');
const {http} = useComponents();
const [user, setUser] = React.useState(null);
const getUser = () => http.get('api/v1/user').then(
response => setUser(response.data)
);
return (<>...</>)
}
В Steroids реализовано более десяти таких компонентов. Вот основные из них:
HttpComponent
— используется для отправки запросов на бекенд, обработки ошибок, авторизации. Поддерживает авторизацию через токен. «Под капотом» использует библиотеку axios.
JwtHttpComponent
— расширяет HttpComponent
, добавляя функционал обновления токена авторизации.
LocaleComponent
— локализует приложение, поддерживает конфигурацию языка и временной зоны.
ClientStorageComponent
— работает с хранением данных в браузере (cookie, local/session storage).
WebSocketComponent
— создаёт websocket-соединения и управляет ими
UI-Kit, простые и комплексные компоненты
В Steroids ui-компоненты условно делятся на простые и комплексные.
Простые компоненты — компоненты интерфейса, которые можно использовать независимо от других компонентов, например кнопка, форма, список.
Комплексные компоненты — состоят из простых компонентов, логически связанных между собой, и реализуют какой либо сложный функционал. Примеры таких компонентов — канбан, таблица, чат и др.
Все компоненты в UI-Kit фреймворка Steroids разделены на несколько групп:
content;
form;
layout;
list;
nav;
typography.
В каждой группе собраны компоненты, отвечающие за определенный функционал — отображение контента, формы и списки, навигацию и так далее.
Вот примеры простых ui-компонентов из Steroids, которые наиболее часто используются в проектах:
Accordion
— компонент-аккордеон позволяет создавать интерактивные списки или контейнеры, где содержимое может быть развернуто или свернуто по требованию пользователя.
DropDown
— компонент, представляющий меню с элементами, которые могут быть выбраны или нажаты. Компонент позволяет отображать и скрывать содержимое меню, а также управлять его позиционированием.
Button
— кнопка или ссылка. Используется в интерфейсе для выполнения какого-либо действия по клику.
InputField
— поле ввода текста.
NumberField
— числовое поле ввода.
FieldList
— список из сгруппированных полей формы.
CheckboxField
— одиночный чекбокс, использующийся в формах для отметки булевого значения.
Form
— компонент для создания формы. Предоставляет управление и синхронизацию состояния формы, а также позволяет выполнять отправку данных формы на сервер с возможностью валидации и обработки результатов.
Notifications
— компонент, представляющий собой контейнер для отображения всплывающих уведомлений.
Tooltip
— компонент, предоставляющий всплывающую подсказку для дочерних элементов.
Grid
— представление данных коллекции в виде таблицы.
List
— компонент для представления коллекции в виде списка.
Nav
— компонент навигации позволяет переключаться между группами связанного контента.
Комплексные компоненты используются реже, так как реализуют функционал, заточенный под узкий круг сложных задач.
Вот примеры некоторых компонентов:
CalendarSystem
— полноценная система календаря, которая поддерживает создание событий и добавление их групп, разделение событий по пользователям, отображение сетки событий в трёх вариациях — день, неделя и месяц.

Kanban
— компонент для работы со списком карточек, сгруппированных в списки. Поддерживает функционал создания и редактирования карточек, добавления к ним тегов и описания, открытия карточек в детальном виде — в модальном окне.

Chat
— реализует функционал чата, поддерживает загрузку данных при отправке сообщений, просмотр истории сообщений, разделение сообщений по датам, прикрепление файлов к сообщениям.

Остальные простые и комплексные компоненты UI-Kit Steroids можно посмотреть по ссылке — https://steroids.dev/ru/docs/ui.
Сравнение с другими библиотеками
Мы решили сравнить функционал разных ui-библиотек для React, которые пользуются популярностью. Для сравнения выбрали несколько пунктов, которые считаем важными при выборе библиотеки:
наличие UI-Kit;
готовая архитектура для проекта;
подключенные «под капотом» базовые зависимости для создания SPA приложения;
работа с формами;
работа со списками (в том числе пагинация и фильтрация);
работа с роутингом;
работа с SSR;
возможность кастомизировать компоненты;
наличие активного сообщества.
Для сравнения мы выбрали несколько библиотек:
Material UI;
Ant Design;
Mantine;
Semantic UI React;
Steroids.
Вот что у нас получилось:

У всех выбранных библиотек есть Ui-Kit и возможность разными способами кастомизировать компоненты. А вот с остальными критериями не все так гладко.
Готовая архитектура проекта, настроенный роутинг и подключенные «под капотом» зависимости есть только у библиотек Material UI, Mantine и Steroids. При чем у Material UI такие функции доступны только в платных шаблонах.
Работа с SSR есть только у двух библиотек — Mantine и Steroids. У первой «под капотом» используется Next.js, а у второй написана собственная реализация SSR, которая подключается через npm-пакет @steroids/ssr
.
Посмотрим как обстоят дела с компонентами. В каждой из приведенных библиотек есть компоненты для полей формы, но только в двух из них (все те же Mantine и Steroids) «под капотом» реализована логика работы с формами, а именно валидация значений, обработчик onSubmit
, режимы контролируемой и неконтролируемой формы.
Вот пример использования форм, взятый из документации библиотеки Mantine:
import { Button, Checkbox, Group, TextInput } from '@mantine/core';
import { useForm } from '@mantine/form';
function Demo() {
const form = useForm({
mode: 'uncontrolled',
initialValues: {
email: '',
termsOfService: false,
},
validate: {
email: (value) => (/^\S+@\S+$/.test(value) ? null : 'Invalid email'),
},
});
return (
<form onSubmit={form.onSubmit((values) => console.log(values))}>
<TextInput
withAsterisk
label="Email"
placeholder="your@email.com"
key={form.key('email')}
{...form.getInputProps('email')}
/>
<Checkbox
mt="md"
label="I agree to sell my privacy"
key={form.key('termsOfService')}
{...form.getInputProps('termsOfService', { type: 'checkbox' })}
/>
<Group justify="flex-end" mt="md">
<Button type="submit">Submit</Button>
</Group>
</form>
);
}
И вот пример, как формы используются в Steroids:
import {login} from '@steroidsjs/core/actions/auth';
import {ROUTE_USERS} from 'routes';
const FORM_ID = 'LoginFrom';
export default function LoginFrom() {
return (
<div className={bem.block()}>
<Form
formId={FORM_ID}
fields={formFields}
action='/api/v1/auth/login'
actionMethod='POST'
useRedux
onComplete={(values, data) => {
if (data.accessToken) {
dispatch(login(data.accessToken, ROUTE_USERS));
}
}}
submitLabel='Войти'
/>
</div>
);
}
В этом примере реализована даже логика отправки данных формы на конкретный API endpoint. У Form
также есть такие пропсы, как onBeforeSubmit
, onAfterSubmit
, onComplete
, которые вызывают функции соответственно до отправки формы, после отправки и в случае успешного выполнения запроса. Еще есть пропсы для сохранения данных формы в редаксе (useRedux
), в адресной строке (addressBar
) и в local storage (autoSave
). Вообще компонент Form
в Steroids довольно функциональный. О нем будет одна из следующих статей.
В работе со списками данных похожая ситуация. Во всех фреймворках есть компоненты List
и Pagination
, но в большинстве случае это только визуальная составляющая, для работы которой нужно дописывать логику уже в проекте. А в Steroids такая логика написана «под капотом».
Вот пример, как используется List
c пагинацией:
<List
listId='FlightList'
items={flightItems}
itemView={ListCard}
itemProps={{
bem,
}}
contentClassName={bem.element('list')}
paginationSize={{
defaultValue: 2,
sizes: [1, 2, 3],
}}
pagination={{
defaultValue: 3,
}}
/>
Теперь давайте сравним степень кастомизации компонентов в разных библиотеках. Везде поддерживается кастомизация через различные пропсы для стилей (в каждой библиотеке это будут свои) и через переопределение css-переменных. Библиотека Mantine также предоставляет возможность использовать ее как headless ui-библиотеку, что значит использовать только логику (core-часть), а стили добавлять свои. В Steroids есть возможность переопределять полностью view-часть компонента. Это ещё более глубокая степень кастомизации. Подобный функционал мы встречали только в одной библиотеке — Korus UI от Сбера. Там есть возможность передать view-часть через пропсы, но нет возможности переопределить ее для всех компонентов в проекте (например, для всех InputField
).
И напоследок сравним библиотеки по наличию активного сообщества. Этот критерий тоже имеет вес при выборе ui-библиотеки для проекта. У трех из пяти библиотек есть сообщество разработчиков, которое занимается поддержкой, развитием и фиксом багов. А именно у Material UI, Mantine и Steroids.
Какую библиотеку выбрать, конечно, стоит решать в каждом проекте отдельно, так как везде требуется разный функционал. Мы сравнили всего лишь несколько библиотек из тех, что пользуются популярностью у разработчиков, но их существует ещё довольно много.
И в заключение
Изначально фреймворк Steroids не создавался как покоритель всего, но за 8 лет вырос в полноценный продукт.
В 2022 году мы выиграли грант от faise на его развитие. В рамках работ по гранту было реализовано много новых ui-компонентов, а также несколько комплексных компонентов. Мы провели рефакторинг кода, покрыли фреймворк тестами на 70%, написали подробную документацию и запустили сайт с большим набором демоверсий компонентов.
Если вас заинтересовал фреймворк Steroids и вы хотите узнать о нем больше, то вот ссылка на документацию и быстрый старт.
Спасибо за внимание)
Комментарии (7)
markelov69
15.07.2025 06:58Фреймворк Steroids подходит для создания полноценного SPA приложения с продуманной архитектурой
управление состоянием, в том числе с использованием Redux, React Context;
Вы сделали 11 ошибок в слове плохой. Вместо SPA приложения с плохой архитектурой, вы по ошибке написали с продуманной.
Очнитесь, все адекватные люди последние 9 лет используют MobX в связке с React. В противном случае это просто очередной говнокод.
React нужно использовать по назначению, рендер html и жизненный цикл компонента, а управление состоянием, как глобальным, так и локальным - MobX.gunGarave
15.07.2025 06:58Осильте уже что-то сложнее MobX. Внезапно будет и Redux не плох, если применять его к месту и руками.
isumix
15.07.2025 06:58вы прямо Фьюзор описали
рендер html и жизненный цикл компонента, а управление состоянием, как глобальным, так и локальным - MobX
только в нем можно любой стейт менеджер подключить на пропсу
{ mount }
ну и раз нет состояния - то становится не нужен ре-рендеринг и хуки, жизненный цикл компонента сжимется в 4 состояния:
create, mount, update, unmount
xadd
15.07.2025 06:58Опять тут навязывают всякие структуры, чтобы говнокод случайно не написали, сделайте просто, чтобы собиралось без геморроя и без всяких топорных /app & /pages
affka
15.07.2025 06:58Структуры никто не навязывает, но есть описание и примеры как можно делать (и которые мы у себя используем как правила). Но привязки к директориям (app & /pages ...) здесь нет
upd: и да, если не дать примера как делать — будут делать говнокод :) Либо прогеры будут крутые, но каждый напишет по своему.. и опять получится говнокод)
nihil-pro
Шо, опять?