Vite + React без хаоса: минимальная структура проекта
Vite + React. Границы папок важнее модного паттерна - через месяц без структуры components/ превращается в свалку всего проекта.
Vite быстрый, архитектуру не продаёт
create-vite поднимает проект за минуту. Через две недели в `components` лежит всё: кнопка, страница оплаты, запросы к API и модалка удаления.
Мелочь? Нет.
Новый экран - и вы уже боитесь что-то трогать.
Минимальная структура, которую я использую
src/
app/ # роутинг, провайдеры
pages/ # экраны
widgets/ # крупные блоки страницы
features/ # действия пользователя
shared/
api/ # запросы
ui/ # кнопки, инпуты
lib/ # утилиты
Это не религия FSD. Это границы: shared не знает про бизнес, features держит сценарии, pages собирают экран из виджетов.
Алиасы - меньше ../../../
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'node:path'
export default defineConfig({
plugins: [react()],
resolve: {
alias: { '@': path.resolve(__dirname, './src') }
}
})
Импорт `@/shared/api/orders` читается лучше, чем цепочка из точек.
Сроки плывут отсюда.
TypeScript paths настраиваю синхронно с Vite.
API не живёт в JSX
Компонент вызывает `loadOrders()`, а не собирает fetch с headers внутри разметки. Ошибки, повторы, типы - в одном месте.
Да, так бывает.
Так проще тестировать и менять бэкенд.
| Слой | Что хранить |
|---|---|
| shared/api | fetch-обёртки, типы ответов |
| features | хуки и действия «отправить форму» |
| pages | сборка экрана, минимум логики |
| widgets | header, footer, сложные секции |
Хорошая структура не усложняет проект. Она снижает шанс, что новая кнопка сломает соседний экран.
Когда не усложнять
Лендинг на три экрана не обязан быть микросервисом на фронте. Если файлов мало - достаточно pages + shared. Папки features и widgets добавляю, когда появляется второй похожий сценарий, а не «на вырост».
Если проект заказной - фиксирую структуру в README на полстраницы: куда класть новый API, где лежат UI-кит и env. Это дешевле, чем объяснять голосом каждому новому человеку. Вопросы по стеку до старта - в материале до предоплаты.
Env, конфиг и деплой
Ключи API - только в .env на сервере, не в репозитории. В Vite публичные переменные с префиксом VITE_, секреты - через backend. .env.example в git, реальные .env - в .gitignore. Банально, но на каждом втором аудите нахожу ключ в клиенте.
Скрипты в package.json: dev, build, preview, lint. Husky или CI на lint - по желанию, но build должен проходить чисто перед деплоем. README с командой деплоя экономит нервы через полгода.
Когда вырастать из минимальной структуры
Появился второй тип пользователя, общие фильтры, модалки на пол экрана - пора делить features/widgets. До этого не создавайте пустые папки «на будущее». Структура должна отражать реальные границы, не wishlist.
Тесты и типы - по необходимости
На MVP лендинга достаточно TypeScript strict и eslint. Unit-тесты на утилиты и API-обёртки - да; snapshot всего UI - редко окупается. e2e на «форма отправилась» - один сценарий перед релизом, если есть staging.
Компоненты UI документирую Storybook только если их больше десятка и команда >1. Иначе - overkill.
Роутинг и code splitting
React Router или аналог: страницы lazy через React.lazy и Suspense для админки и редких экранов. Главная и лендинг - в основном bundle, если LCP критичен. Не режьте всё подряд - смотрите bundle analyzer.
Общие зависимости: date-fns вместо moment, lodash-es точечный import. Каждый «просто подключим ещё одну lib» - минус на mobile.
Error boundary на уровне page - пользователь видит «что-то сломалось, обновите», а не белый экран. Логируете в Sentry или console на staging минимум.
Состояние и формы
React Hook Form или аналог для форм - меньше ре-рендеров. Глобальный state (Zustand/Context) только для того, что реально глобально: user, theme, cart. Локальный useState на странице - норм.
Не тащите Redux на лендинг. Не тащите GraphQL если один REST endpoint формы.
Стили: CSS modules, Tailwind или styled - один подход на проект. Смешение трёх систем - боль поддержки.
Деплой: static на CDN/VPS, env на сервере, CI build on push. Preview deploy на каждый PR - кайф для заказчика, видит прогресс.
Git и code review на маленьком проекте
Даже лендинг на React я веду в git с понятными коммитами: «feat: contact form», «fix: mobile menu». Ветка main - прод, develop или feature-ветки - staging. Заказчик получает ссылку на staging, не на «у меня локально работает».
Pull request на изменения больше пятидесяти строк - сам себе review: что сломается на мобиле, не утекли ли ключи. Husky optional, но npm run build перед merge - обязательно.
Конфиг ESLint и Prettier
Один стиль форматирования с первого дня. Споры «таб или пробелы» не в чате с заказчиком, а в .prettierrc. TypeScript strict: noImplicitAny ловит половину багов до деплоя.
// tsconfig.json - минимум для заказного фронта
{
"compilerOptions": {
"strict": true,
"noUnusedLocals": true,
"paths": { "@/*": ["./src/*"] }
}
}
Документация для «через полгода»
README на полэкрана: как поднять dev, как собрать, куда деплоить, где env. Список env-переменных в .env.example. Если проект передаётся другому разработчику - он не должен звонить вам ночью.
Я добавляю ADR только когда решение неочевидное: «почему Zustand, а не Redux» - один абзац в docs/adr-001-state.md. Не encyclopedia, а якоря памяти.
Структура папок - карта. README - легенда. Без легенды карта бесполезна через три месяца.
Если заказчик спрашивает «зачем React на лендинге» - честный ответ в материале React vs HTML. Вопросы по стеку до старта - до предоплаты. Оценка рефакторинга - на странице цен.
Миграция без остановки продакшена
Рефакторинг структуры делаю по слоям: сначала выношу API в shared/api, потом UI в shared/ui, страницы не трогаю. Каждый шаг - отдельный deploy, форма и заявки работают. Big bang «перепишем всё за выходные» на заказном проекте - red flag.
Что сделать сначала
- Разделите api, ui и pages хотя бы на три папки.
- Настройте алиас @ в vite.config и tsconfig.
- Вынесите повторяющиеся кнопки и инпуты в shared/ui.
- Новый экран начинайте с page, не с монолита в App.tsx.
- Добавляйте features/widgets только когда появился второй похожий кейс.
- Опишите структуру в README для себя и подрядчиков.
Промахи на старте
- Класть fetch и бизнес-логику прямо в компоненты страниц.
- Делать одну папку components для всего проекта.
- Вводить сложную архитектуру на лендинг из трёх экранов.
- Не настроить алиасы и плодить длинные относительные импорты.
- Дублировать один и тот же UI вместо shared/ui.
Когда звать на помощь
Имеет смысл звать разработчика, когда рефакторинг структуры мешает добавлять функции - он разложит проект без большой миграции и без остановки продакшена. Ориентиры по срокам и бюджету - на странице цен, услуги - в разделе услуг.
Что почитать ещё
- Вопросы до предоплаты - про стек
- Услуги - фронтенд под ключ
- Цены - рефакторинг
- Контакты - разбор вашего репо
Хотите обсудить похожую задачу? Могу быстро привести фронтенд к понятной структуре без большой миграции.
Обсудить проект →