В этом уроке мы подробно рассмотрели процесс создания сайта-приложения с использованием фреймворка Next.js. Мы начали с установки и настройки проекта, изучив структуру и базовые возможности Next.js, такие как роутинг и создание страниц. На протяжении урока мы охватывали важные аспекты разработки, включая создание главной страницы, добавление страницы «О нас», настройку навигации и работу с динамическими маршрутами для отображения индивидуальных страниц пользователей.
Также мы разобрали работу с изображениями, шрифтами и шаблонами для различных состояний приложения (загрузка, ошибки 404 и другие). Важным этапом стало использование API: мы научились делать запросы для получения данных, а также разобрали создание внутренних API и обработку POST-запросов. В финальной части урока мы показали, как собрать проект для продакшн-среды, а затем деплоить его на платформу Vercel, с настройкой домена и переменных окружения.
Урок является хорошим стартом для тех, кто хочет научиться работать с Next.js и создавать полноценные, оптимизированные сайты с динамическим контентом.
Видео
Материалы к видео
Код к уроку
Изображения

https://webcademy.ru/blog/wp-content/uploads/2025/09/dodge-200-200.jpg

https://webcademy.ru/blog/wp-content/uploads/2025/09/dodge-400-200.jpg

https://webcademy.ru/blog/wp-content/uploads/2023/07/Travel-X-Cover-870×400.jpg
Файл .editorconfig
# EditorConfig is awesome: https://EditorConfig.org # Основные настройки для всех файлов [*] charset = utf-8 end_of_line = lf insert_final_newline = true indent_style = tab indent_size = 4 trim_trailing_whitespace = true # Специфические настройки для файлов с определёнными расширениями (если необходимо) [*.md] trim_trailing_whitespace = false
Файл .prettierrc
{ "singleQuote": true, "trailingComma": "all", "semi": true, "printWidth": 80, "tabWidth": 4, "endOfLine": "lf" }
CSS стили. Файл globals.css
/* ============================================= Глобальные стили (globals.css) - Переменные темы и базовые стили - Компоненты интерфейса и утилиты ============================================= */ /* Тема: корневые CSS‑переменные */ :root { --bg: #070f14; --bg-2: #0b1620; --card: #0f1f2a; --text: #e6f1ff; --muted: #9fb2c7; --accent: #2fe39a; --accent-2: #38bdf8; --ring: rgba(47, 227, 154, 0.3); } /* База: box-sizing и высота документа */ * { box-sizing: border-box; } html, body { height: 100%; } body { margin: 0; background-color: #0e1b25; color: var(--text); font: 16px/1.6 ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, 'Apple Color Emoji', 'Segoe UI Emoji'; overflow-x: hidden; font-family: var(--font-inter); } /* Типографика: заголовки */ h1, h2, h3, h4, h5, h6 { color: #fff; } /* Блоки кода */ code pre { text-align: left; width: max-content; margin: 0 auto; border-radius: 12px; background-color: rgba(5, 20, 30, 0.7); backdrop-filter: blur(5px); padding: 20px; font-size: 14px; color: #16a34a; color: #58a0d8; } /* Медиа: изображения по умолчанию */ .image { object-fit: cover; height: auto; } /* Контейнер макета */ .container { width: min(1100px, 92%); margin: 0 auto; } /* Фон: тонкая точечная сетка */ .bg-grid { position: fixed; inset: 0; pointer-events: none; z-index: -1; background-image: radial-gradient( rgba(120, 200, 255, 0.2) 1px, transparent 1px ); background-size: 18px 18px; mask-image: radial-gradient( circle at 50% 10%, rgba(0, 0, 0, 0.99), transparent 40% ); } /* Навигация: верхняя панель */ .nav { position: sticky; top: 0; z-index: 20; /* backdrop-filter: saturate(130%) blur(10px); */ } .nav-inner { display: flex; align-items: center; justify-content: center; gap: 1rem; padding: 14px 0; border-bottom: 1px solid rgba(255, 255, 255, 0.05); } /* Навигация: плавающий блок ссылок */ .navigation { position: sticky; top: 20px; left: 50%; transform: translateX(-50%); z-index: 20; display: flex; gap: 0.6rem; align-items: center; width: max-content; border-radius: 999px; padding: 10px 16px; border: 1px solid rgba(255, 255, 255, 0.08); background: rgba(255, 255, 255, 0.04); color: var(--text); backdrop-filter: saturate(130%) blur(10px); } /* Ссылки навигации */ .nav-link { color: var(--muted); text-decoration: none; padding: 8px 12px; border-radius: 999px; transition: 150ms ease; } .nav-link:hover { color: var(--text); background: rgba(255, 255, 255, 0.04); } /* Кнопки */ .buttons-wrapper { display: flex; gap: 10px; justify-content: center; } .btn { display: inline-flex; align-items: center; gap: 0.5rem; font-weight: 600; text-decoration: none; border-radius: 999px; padding: 10px 16px; border: 1px solid rgba(255, 255, 255, 0.08); cursor: pointer; } .btn-ghost { background: rgba(255, 255, 255, 0.04); color: var(--text); } .btn-ghost:hover { background: rgba(255, 255, 255, 0.07); } .btn-primary { color: #072617; background: linear-gradient(180deg, #43f3ad, #16a34a); border: none; box-shadow: 0 12px 30px rgba(67, 243, 173, 0.25), inset 0 0 0 1px rgba(255, 255, 255, 0.15); } .btn-primary:hover { filter: saturate(1.1) brightness(1.02); transform: translateY(-1px); } /* Разметка страницы */ .page { padding: 48px 0 64px; } /* Раздел Hero / контент */ .content { text-align: center; padding: 56px 0 36px; } .badge { display: inline-flex; align-items: center; gap: 0.4rem; border: 1px solid rgba(255, 255, 255, 0.08); padding: 8px 12px; border-radius: 999px; color: var(--muted); background: rgba(10, 20, 28, 0.5); } .content h1 { margin: 1em 0 0.5em; font-size: clamp(38px, 7.5vw, 68px); line-height: 1.05; letter-spacing: -0.02em; } .content h2 { margin: 1em 0 0.5em; font-size: clamp(28px, 6.5vw, 52px); line-height: 1.05; letter-spacing: -0.02em; } .content > h1:first-child, .content > h2:first-child { margin-top: 0; } .content p { color: var(--muted); max-width: 760px; margin: 0 auto 24px; } /* Сетки и списки */ .grid { display: grid; grid-template-columns: repeat(1, minmax(0, 1fr)); gap: 12px; } /* Список пользователей */ .users-list { max-width: 600px; margin: 0 auto; text-align: left; } .users-list .item { border-radius: 0px; border-bottom: 0px; backdrop-filter: blur(5px); } .users-list .item:first-child { border-top-right-radius: 12px; border-top-left-radius: 12px; } .users-list .item:last-child { border-bottom-left-radius: 12px; border-bottom-right-radius: 12px; } @media (min-width: 700px) { .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } } @media (min-width: 1000px) { .grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } } /* Карточки */ .card { background: linear-gradient( 180deg, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0.03) ); border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 14px; padding: 16px; transition: 150ms ease; } .card:hover { border-color: rgba(47, 227, 154, 0.35); box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35), 0 0 0 6px var(--ring); } .card--user-details { display: grid; grid-template-columns: 140px 1fr; row-gap: 8; } /* Элемент списка/карточки */ .item { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 14px 16px 15px; border-radius: 12px; background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(255, 255, 255, 0.07); text-decoration: none; color: var(--text); } .item:hover { background: rgba(255, 255, 255, 0.05); border-color: rgba(47, 227, 154, 0.35); border-bottom-width: 1px; border-bottom-style: solid; padding-bottom: 14px; } .item-title { font-weight: 600; } .item-sub { color: var(--muted); font-size: 14px; } /* Заголовок раздела */ .section-title { margin: 36px 0 12px; font-size: 24px; letter-spacing: 0.2px; color: #dbeafe; } /* Подвал (footer) */ .footer { padding: 20px 0 40px; color: var(--muted); border-top: 1px solid rgba(255, 255, 255, 0.06); text-align: center; font-size: 14px; } /* Утилиты */ .stack { display: flex; gap: 12px; justify-content: center; } /* p, */ .muted { color: var(--muted); } /* Страницы и обёртки с рамкой */ .user-page { border: 1px solid #ffffff3c; padding: 30px; } .bordered-wrapper { border: 1px solid #ffffff3c; padding: 30px; } /* Индикатор загрузки */ .loader-wrapper { width: 100%; height: 200px; display: flex; justify-content: center; align-items: center; } .loader { width: 48px; height: 48px; border: 5px solid #fff; border-bottom-color: #ff3d00; border-radius: 50%; display: inline-block; box-sizing: border-box; animation: rotation 1s linear infinite; } /* Анимации */ @keyframes rotation { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }