Домашнє завдання #24: Довідник твого продукту
Побудуй статичний сайт на Next.js App Router: статті лежать як MDX-файли, сторінки генеруються під час білду (SSG), а маршрути народжуються з файлової структури. Це рівно та архітектура, на якій працює сайт цього курсу.
Але писати «5 абстрактних статей про Flexbox» — нудно. Тому обери, про що буде твій контент:
- Довідник твого продукту (з ДЗ #5): для фільмотеки — «Гід по жанрах», «Як обрати, що дивитися ввечері», «5 режисерів, з яких варто почати»; для рецептів — гіди по кухнях; для ігор — по жанрах і платформах
- Щоденник курсу: 5 чесних нотаток «що я вивчив» — про перший git push, про день, коли клацнув Flexbox, про баг, який ти шукав годину
Пиши по-справжньому. Ці сторінки переїдуть у твоє портфоліо в ДЗ #26 — їх читатимуть живі люди, можливо навіть твій майбутній роботодавець.
Підготовка
npx create-next-app@latest my-guide --typescript --tailwind --app
cd my-guide
npm install gray-matter
Структура контенту (назви файлів — під твої теми):
content/
posts/
gid-po-zhanrah.mdx
shcho-dyvytysia-vvecheri.mdx
...
Frontmatter кожного файлу:
---
title: "Гід по жанрах: з чого почати"
date: "2026-02-01"
description: "Короткий опис для списку та SEO"
tags: ["гід", "жанри"]
---
Завдання
1. Утиліти для контенту
Файл src/lib/posts.ts з функціями (читання файлів — fs, парсинг frontmatter — gray-matter):
getAllPosts()— масив усіх постів (meta + slug), нові першимиgetPostBySlug(slug)— один пост (meta + content)getAllTags()— масив унікальних тегів
2. Список статей (/blog)
- Server Component: усі статті з заголовком, датою, описом і тегами
- Кожна — посилання на свою сторінку
3. Сторінка статті (/blog/[slug])
- Повний контент, заголовок, дата, теги
generateStaticParams— Next.js має згенерувати всі сторінки статично під час білду- Посилання «Назад до всіх статей»
- Неіснуючий slug → 404 (
notFound())
4. Фільтрація за тегами (/blog/tag/[tag])
- Сторінка зі статтями конкретного тега, заголовок «Статті з тегом: ...»
generateStaticParamsна основіgetAllTags()- Теги у списках і статтях — клікабельні посилання сюди
5. Layout та навігація
- Кореневий
layout.tsx: header (назва-бренд + навігація) і footer - Навігація: Головна, Статті, Про автора
/about— сторінка про тебе (2-3 абзаци, хто ти і що це за сайт)
6. SEO metadata
- Статичний
metadataдля головної, списку та about generateMetadataдля сторінок статей: title і description з frontmatter- Open Graph поля (
openGraph.title,openGraph.description)
Вимоги до контенту
Мінімум 5 статей, кожна — від 3-5 абзаців власного тексту. Не lorem ipsum і не згенерована вода: конкретика, приклади, твоя думка. У статей мають бути перехресні теги (хоча б один тег зустрічається у 2+ статтях — інакше сторінки тегів не мають сенсу).
Як здати
- Це — окремий публічний репозиторій
README.md: про що сайт, як запустити (npm install,npm run dev,npm run build); env-змінних тут немає — так і напиши- Мінімум 5 комітів — наприклад: setup, утиліти, список і сторінка статті, теги, metadata + контент
npm run buildпроходить без помилок — це головний тест SSG- Скріншоти в
screenshots/:desktop.png(список статей),mobile.png,interaction.png(відкрита стаття або сторінка тега) - Здай посиланням на репозиторій
Критерії оцінювання
| Критерій | Тип | Бали |
|---|---|---|
lib/posts.ts: getAllPosts із сортуванням за датою, getPostBySlug, getAllTags | [код] | 15 |
| Список статей: Server Component, заголовок/дата/опис/теги, робочі посилання | [поведінка] | 15 |
Сторінка статті: generateStaticParams, повний контент, 404 для неіснуючого slug | [код] | 20 |
Сторінки тегів: свій generateStaticParams, клікабельні теги ведуть сюди | [поведінка] | 10 |
Layout: header з навігацією, footer, сторінка /about | [код] | 10 |
SEO: статичний metadata + generateMetadata для статей + Open Graph | [код] | 10 |
| 5+ справжніх статей по 3-5 абзаців, перехресні теги | [код] | 5 |
| Скріншоти: список, читабельна сторінка статті, mobile | [скрін] | 10 |
| Якість коду | [код] | 5 |
| Разом | 100 | |
| Бонус: час читання для кожної статті (порахований зі слів) | [код] | +5 |
| Бонус: навігація «попередня / наступна стаття» на сторінці посту | [поведінка] | +5 |
Бонус: пошук за заголовком (Client Component) або RSS public/rss.xml | [поведінка] | +10 |
Підказки
Рівень 1: напрямок
Усе крутиться навколо lib/posts.ts — напиши і перевір його першим (можна тимчасово console.log у page.tsx). Сторінки в App Router — серверні за замовчуванням, тому вони можуть просто викликати твої функції з fs напряму, без жодного API. Динамічний маршрут — папка у квадратних дужках: app/blog/[slug]/page.tsx.
Рівень 2: які інструменти
fs.readdirSync + fs.readFileSync + matter(fileContent) дає { data, content }. Slug — ім'я файлу без .mdx. У сторінці [slug]: export async function generateStaticParams() повертає [{ slug: '...' }, ...]; сам компонент отримує params (в нових версіях Next це Promise — await params). generateMetadata — експортована функція поруч з компонентом, теж отримує params. Не знайшли пост — notFound() з next/navigation. Рендер markdown-тексту: почни з простого (розбий content на абзаци) або підключи next-mdx-remote.
Рівень 3: скелет сторінки статті
app/blog/[slug]/page.tsx:generateStaticParams→getAllPosts().map(p => ({ slug: p.slug }))generateMetadata({ params })→await params, взяти пост →{ title, description, openGraph: {...} }- Компонент:
const { slug } = await params; const post = getPostBySlug(slug); if (!post) notFound(); - Розмітка:
h1з title, дата (toLocaleDateString('uk-UA')), теги якLinkна/blog/tag/..., контент - Сторінка тега аналогічна:
generateStaticParamsзgetAllTags(), контент —getAllPosts().filter(p => p.tags.includes(tag)) - Фінальна перевірка:
npm run build— у виводі побачиш●(SSG) навпроти своїх маршрутів
Що далі
Не видаляй ці статті! У ДЗ #26 весь цей розділ переїде у твоє портфоліо як живий блог — а перед тим, у ДЗ #25, твій каталог отримає власний бекенд на API routes.