VibeSite: как я сделал AI-генератор сайтов на Next.js за время хакатона
Зачем ещё один AI-генератор сайтов
Конструкторы сайтов вроде Tilda или Wix решают задачу, но у них есть ограничения: шаблоны выглядят одинаково, кастомизация ограничена, а для нестандартного дизайна нужен разработчик.
VibeSite подходит к проблеме с другой стороны: пользователь описывает идею на естественном языке, а AI генерирует уникальный дизайн с нуля. Не шаблон, а именно сгенерированную под конкретный запрос верстку. (нужно еще дорабатывать)
Что умеет приложение
Генерация по текстовому описанию. Пользователь пишет запрос вроде "лендинг для кофейни в скандинавском стиле" и получает готовый сайт за 10-20 секунд.
Генерация по изображению. Можно загрузить фото, и AI извлечёт из него доминирующие цвета, настроение, текстуры и подберёт дизайн, который выглядит как естественное продолжение изображения.
Поиск изображений. Встроенный поиск по Pexels, Pixabay и LoremFlickr с автоматическим фолбэком.
Undo/Redo с историей в localStorage. Шесть готовых шаблонов для быстрого старта: кофейня, SaaS, портфолио, барбершоп, ресторан, курс.
Технологический стек
Next.js 14.1.0 с App Router и standalone-сборкой. React 18, TypeScript, Tailwind CSS. OpenAI GPT-4o для генерации. Framer Motion для анимаций. Lucide React для иконок.
Выбор Next.js был очевиден: App Router даёт удобный способ описать API-роуты рядом с фронтендом, а standalone-сборка упрощает деплой в Docker.
Архитектура
Структура проекта:
src/app/page.tsx — главная страница с чат-интерфейсом src/app/api/generate/route.ts — API для генерации сайтов src/app/api/search-images/route.ts — API для поиска изображений src/components/SiteRenderer.tsx — рендерер сгенерированного сайта
Всё построено вокруг одной идеи: AI возвращает структурированный JSON, а React-компонент интерпретирует его и отрисовывает сайт. Это разделение важно: модель не пишет HTML, она описывает структуру, а рендеринг занимается фронтенд.
Как работает генерация
API-роут /api/generate принимает три параметра: prompt (текстовое описание), image (изображение в base64) и current (текущее состояние сайта для режима редактирования).
Системный промпт — это самая интересная часть. Я не просто прошу модель "сделай сайт". Промпт описывает AI как эксперта, который совмещает навыки senior product designer и frontend инженера. Он включает несколько блоков.
Идентичность. Модель должна анализировать намерение пользователя, читать между строк, принимать дизайнерские решения на основе лучших практик, понимать психологию бренда, теорию цвета, типографику и UX-паттерны.
Компьютерное зрение. Когда пользователь загружает изображение, промпт переключается в режим визуального анализатора. Модель должна извлечь 3-5 точных доминирующих цветов в HEX, определить настроение, описать визуальные элементы (субъект, объекты, текстуры, материалы, освещение, композиция), определить тип бренда и целевую аудиторию. И главное: сайт должен выглядеть как естественное продолжение изображения.
Правила дизайн-системы. В промпте зашита таблица соответствия типа бренда и стиля. Например, для Tech/SaaS — glassmorphism с тёмной теммой и цветами Indigo и Blue. Для Luxury/Fashion — минимализм с чёрным и золотым. Для Food/Coffee — aurora с коричневым и кремовым. Отдельный блок про психологию цвета: доверие и финансы — navy и teal, энергия и действие — оранжевый и красный, роскошь — чёрный и золотой.
Состав секций. В зависимости от типа сайта модель выбирает 5-7 секций. Для SaaS это hero, stats, grid, testimonials, pricing, faq, cta. Для портфолио — hero, gallery, grid, cta. Для ресторана — hero, gallery, grid (меню), testimonials, cta.
Схемы секций. Каждая секция имеет чёткую структуру. Hero: title, text, cta, img. Stats: items с value и label. Grid: items с title, text, icon. Gallery: items с title и img. Testimonials: items с title, text, role. Pricing: items с title, price, period, features, cta, featured. FAQ: items с title и text. CTA: title, text, cta.
Иконки. Промпт явно указывает, что иконки должны быть из lucide-react в PascalCase, и даёт списки по категориям: бизнес (Rocket, Zap, Shield, Award), креатив (Palette, Camera, Sparkles), еда (Coffee, UtensilsCrossed, Wine), услуги (Scissors, Clock, MapPin), технологии (Code, Database, Cloud).
Выходной формат. Модель возвращает только валидный JSON без markdown и объяснений. Структура: title, tagline, imageInsight (если было изображение), designSystem (style, theme, colors, fontPair) и layout (массив секций).
Вызов модели выглядит так: модель openai/gpt-4o, response_format json_object, max_tokens 4000, temperature 0.85. Температура повыше, чтобы дизайн был креативнее.
Валидация ответа
Модель иногда возвращает неполный JSON, поэтому после парсинга стоит несколько слоёв защиты. Если JSON.parse падает, я ищу первый JSON-объект регуляркой. Если designSystem отсутствует, создаю пустой объект. Если цветов нет или они невалидные (не проходят проверку регуляркой HEX), подставляю дефолтные: primary #6366f1, secondary #818cf8, accent #a855f7, bg #ffffff, text #0f172a. Если fontPair отсутствует, ставлю Inter для заголовков и тела. Если style отсутствует — glassmorphism. Если layout не массив — пустой массив.
Рендерер сайтов
SiteRenderer — это React-компонент, который принимает JSON от модели и отрисовывает полноценный сайт. Здесь несколько интересных решений.
Динамические иконки. Компонент DynamicIcon принимает имя иконки строкой, достаёт её из lucide-react по имени и рендерит. Если иконка не найдена, fallback на Zap. Это позволяет модели указывать иконки по имени в JSON, не заботясь об импортах.
Стили по типу дизайна. Заранее описаны стили для glassmorphism, brutalism, minimalism и aurora. Каждый определяет классы для карточек, скруглений и кнопок. Например, brutalism использует толстые чёрные рамки и жёсткие тени без скруглений, а glassmorphism — backdrop-blur и полупрозрачные фоны.
Шрифты. Из JSON достаётся пара шрифтов (heading и body), формируется CSS-импорт с Google Fonts и применяются через классы vb-h и vb-b. Это позволяет модели выбирать любые шрифты из Google Fonts.
Aurora-фон. Для стиля aurora генерируется составной radial-gradient с тремя точками цвета, создающий эффект северного сияния.
Анимации. Framer Motion используется для появления секций при скролле (initial opacity 0 и y 50, whileInView opacity 1 и y 0) и для hover-эффектов на карточках и галерее.
Семь типов секций. Hero с заголовком, подзаголовком, CTA и изображением. Stats с сеткой чисел и подписей. Gallery с сеткой квадратных изображений. Grid с карточками фич и иконками. Pricing с тремя тарифами и выделенным "популярным". Testimonials с цитатами и аватарами. FAQ с раскрывающимися вопросами. CTA-баннер с градиентом.
Поиск изображений
API-роут /api/search-images реализует каскадный фолбэк. Сначала пробуется Pexels (если есть API-ключ). Если пусто или ключа нет — Pixabay. Если тоже пусто — LoremFlickr, который работает без ключа и генерирует изображения по ключевому слову. Если и это не сработало — Picsum с случайными изображениями. Таким образом поиск всегда возвращает результат, даже без единого API-ключа.
Деплой
Проект настроен для production. В next.config.js указан output: standalone, что создаёт минимальный автономный бандл. Dockerfile с многоэтапной сборкой: сначала установка зависимостей и сборка, потом копирование standalone-бандла и public-директории в финальный образ.
Требования к окружению: Node.js 18+, обязательно OPENAI_API_KEY, опционально PEXELS_API_KEY и PIXABAY_API_KEY.
Валидация прошла успешно: npm ci установил 416 пакетов, npm run build собрался без ошибок, npm run start запустил сервер, healthcheck вернул HTTP 200.
Что было сложно
Системный промпт. Первые версии промпта давали непредсказуемые результаты: модель могла вернуть HTML вместо JSON, придумать несуществующую иконку или выбрать шрифт не из Google Fonts. Пришлось явно прописать схему выходных данных, список допустимых иконок и формат цветов. Температура 0.85 оказалась балансом между креативностью и предсказуемостью.
Валидация. Модель — вероятностная система, и полагаться на то, что она всегда вернёт валидный JSON, нельзя. Слои валидации с дефолтными значениями спасают от падения рендера. Регулярка для извлечения JSON из текста — на случай, если модель обернёт ответ в markdown.
Изображения. Без API-ключей Pexels и Pixabay поиск изображений вырождается в LoremFlickr и Picsum, что приемлемо для прототипа, но не для production. Каскадный фолбэк гарантирует, что сайт всегда будет с изображениями.
Планы
Интеграция с DALL-E для генерации уникальных изображений вместо стоковых. Больше стилей дизайна: neomorphism, retro, 3D. Экспорт в React/Vue компоненты. Интеграция с CMS. Мультиязычность. Аналитика и SEO-оптимизация.
Заключение
VibeSite — пример того, как можно построить полноценный AI-продукт на Next.js, используя структурированный промпт-инжиниринг и разделение между генерацией структуры и рендерингом. Ключевая идея: модель не пишет код, она описывает структуру в JSON, а фронтенд интерпретирует её в готовый сайт. Это даёт предсказуемость, валидируемость и возможность дорабатывать дизайн итеративно.
GITHUB: