Как я раскладываю React-проект под реальную боевую задачу

Учебные туториалы по React часто заканчиваются на src/App.jsx и куче компонентов в одной папке. В бою такое быстро превращается в кашу. Расскажу, как я обычно собираю структуру фронта, чтобы через полгода не хотелось всё выкинуть.

Базовая структура проекта

Обычно стартую с чего-то такого:

src/ ├ app/ │ ├ App.tsx │ ├ providers/ │ └ routes/ ├ entities/ ├ features/ ├ widgets/ ├ pages/ └ shared/ ├ ui/ ├ lib/ ├ config/ └ api/

Коротко: • app/ — точка входа, роутинг, провайдеры (Router, Store, i18n и т.п.). • pages/ — страницы как композиция виджетов/фич. • widgets/ — крупные блоки страницы (шапка, боковое меню, лента товаров). • features/ — законченные пользовательские действия (логин, смена пароля, фильтр каталога). • entities/ — бизнес-сущности (User, Product, Order) + их отображение. • shared/ — переиспользуемые части без бизнес-логики.

Такой подход близок к Feature-Sliced, но без фанатизма: можно адаптировать под размер проекта.

Как я описываю сущности

Пример для товара:

entities/product/ model/ types.ts hooks.ts ui/ ProductCard/ ProductPrice/

• в model/ лежат типы, хуки, селекторы; • в ui/ — dumb-компоненты, которые можно переиспользовать.

Важно: сущность не знает про конкретные страницы; наоборот, страницы собирают сущности.

Фичи: «маленькие законченные истории»

Примеры фич: • add-to-cart • change-password • products-filter • edit-profile

Структура:

features/add-to-cart/ model/ ui/

Внутри: • хук useAddToCart, • кнопка AddToCartButton, • возможно, модалка подтверждения.

Если нужно переехать на другое API — меняю model, UI почти не трогаю.

Что я кладу в sharedshared/ui — кнопки, инпуты, модалки, типовые таблицы. • shared/lib — утилиты (formatDate, debounce, validators). • shared/api — базовый клиент (axiosInstance), интерсепторы. • shared/config — константы, feature-флаги, env-настройки.

Правило: shared не должен знать о бизнес-сущностях типа Order или Product.

Организация роутов

Пример:

// app/routes/index.tsx import { MainPage } from "@/pages/main"; import { ProductPage } from "@/pages/product";

export const routes = [ { path: "/", element: }, { path: "/product/:id", element: }, ];

Внутри страницы:

// pages/product/ui/ProductPage.tsx export const ProductPage = () => (

);

Роуты знают только о pages, а не о конкретных фичах.

Типичные ошибки, которые я вижу • Все компоненты в одной папке components/. • Смешение API-вызовов, верстки и бизнес-логики в одном файле. • Глобальный store с десятками несвязанных сущностей. • Шеринг всего подряд через shared/, который превращается в свалку.

Чек-лист при старте React-проекта • Структура разложена по слоям: app / pages / widgets / features / entities / shared. • Бизнес-сущности выделены в entities с model и ui. • Пользовательские действия собраны в features. • Роуты завязаны на pages, а не на отдельные компоненты. • В shared лежат только переиспользуемые вещи без бизнес-логики. • Есть понятные правила импорта (например, только «вверх» по слоям).

Итог

Сама по себе структура не ускорит сайт и не решит все баги, но она задаёт рамки: куда класть код, как переиспользовать сущности и фичи, как не утонуть в хаосе через полгода.

Как я раскладываю React-проект под реальную боевую задачу | Сетка — социальная сеть от hh.ru