Стилізація в Next.js
Next.js стилізація — студія дизайнера з тканинами палітрами кольорів та манекеном
Ми вже вміємо створювати сторінки, отримувати дані та будувати API. Тепер зробимо наш додаток гарним. Next.js підтримує кілька підходів до стилізації -- від класичного CSS до сучасного Tailwind. А ще має вбудовані інструменти для оптимізації шрифтів та зображень.
Глобальні стилі
Глобальні CSS-стилі імпортуються в кореневому layout.tsx:
/* src/app/globals.css */
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: system-ui, -apple-system, sans-serif;
line-height: 1.6;
color: #333;
}
a {
color: #0070f3;
text-decoration: none;
}
// src/app/layout.tsx
import "./globals.css";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="uk">
<body>{children}</body>
</html>
);
}
Глобальні стилі можна імпортувати тільки в layout.tsx або page.tsx -- не в звичайних компонентах. Для компонентів використовуй CSS Modules.
CSS Modules
CSS Modules -- це CSS-файли, де класи автоматично стають локальними (унікальними). Жодних конфліктів імен!
/* src/components/Card.module.css */
.card {
border: 1px solid #eee;
border-radius: 8px;
padding: 1.5rem;
transition: box-shadow 0.2s;
}
.card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.title {
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 0.5rem;
}
.description {
color: #666;
font-size: 0.9rem;
}
// src/components/Card.tsx
import styles from "./Card.module.css";
interface CardProps {
title: string;
description: string;
}
export default function Card({ title, description }: CardProps) {
return (
<div className={styles.card}>
<h3 className={styles.title}>{title}</h3>
<p className={styles.description}>{description}</p>
</div>
);
}
У браузері класи будуть виглядати як Card_card__x7f2k -- автоматично унікальні.
Комбінування класів
import styles from "./Button.module.css";
interface ButtonProps {
variant: "primary" | "secondary";
children: React.ReactNode;
}
export default function Button({ variant, children }: ButtonProps) {
return (
<button className={`${styles.button} ${styles[variant]}`}>
{children}
</button>
);
}
Tailwind CSS
Tailwind CSS -- утилітарний CSS-фреймворк, який дозволяє стилізувати компоненти прямо в JSX через класи. При створенні проекту через create-next-app Tailwind вже налаштований.
Базові утиліти
export default function HeroSection() {
return (
<section className="bg-blue-600 text-white py-20 px-4">
<div className="max-w-4xl mx-auto text-center">
<h1 className="text-4xl font-bold mb-4">
Вивчай веб-розробку
</h1>
<p className="text-xl text-blue-100 mb-8">
Від HTML до Next.js -- повний курс для початківців
</p>
<button className="bg-white text-blue-600 px-6 py-3 rounded-lg font-semibold hover:bg-blue-50 transition-colors">
Почати навчання
</button>
</div>
</section>
);
}
Responsive design
Tailwind використовує mobile-first підхід з брейкпоінтами:
export default function Grid() {
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div className="p-4 bg-gray-100 rounded">Картка 1</div>
<div className="p-4 bg-gray-100 rounded">Картка 2</div>
<div className="p-4 bg-gray-100 rounded">Картка 3</div>
</div>
);
}
grid-cols-1-- 1 колонка на мобільнихmd:grid-cols-2-- 2 колонки від 768pxlg:grid-cols-3-- 3 колонки від 1024px
Dark mode
export default function ThemeCard() {
return (
<div className="bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 p-6 rounded-lg shadow">
<h3 className="text-lg font-semibold">Картка</h3>
<p className="text-gray-600 dark:text-gray-400">
Автоматично адаптується до теми.
</p>
</div>
);
}
Tailwind генерує тільки ті класи, які ти використовуєш. Фінальний CSS-файл зазвичай менше 10 КБ -- набагато менше, ніж типовий CSS-фреймворк.
next/font -- оптимізація шрифтів
Next.js має вбудовану оптимізацію шрифтів -- без зовнішніх запитів та без зсуву контенту (CLS):
Google Fonts
// src/app/layout.tsx
import { Inter, Roboto_Mono } from "next/font/google";
const inter = Inter({
subsets: ["latin", "cyrillic"],
variable: "--font-inter",
});
const robotoMono = Roboto_Mono({
subsets: ["latin"],
variable: "--font-roboto-mono",
});
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="uk" className={`${inter.variable} ${robotoMono.variable}`}>
<body className={inter.className}>{children}</body>
</html>
);
}
Використання з Tailwind:
// tailwind.config.ts
import type { Config } from "tailwindcss";
const config: Config = {
theme: {
extend: {
fontFamily: {
sans: ["var(--font-inter)", "system-ui", "sans-serif"],
mono: ["var(--font-roboto-mono)", "monospace"],
},
},
},
};
export default config;
<h1 className="font-sans text-2xl">Заголовок шрифтом Inter</h1>
<code className="font-mono">Код шрифтом Roboto Mono</code>
Next.js завантажує шрифти під час збірки і хостить їх локально. Браузер не робить запитів до Google Fonts -- це покращує приватність і швидкість.
Локальні шрифти
import localFont from "next/font/local";
const myFont = localFont({
src: "./fonts/MyFont.woff2",
variable: "--font-my",
});
next/image -- оптимізація зображень
Компонент Image автоматично оптимізує зображення:
import Image from "next/image";
export default function Avatar() {
return (
<Image
src="/avatar.jpg"
alt="Фото профілю"
width={150}
height={150}
className="rounded-full"
/>
);
}
Що робить Image:
- Автоматично обирає формат (WebP, AVIF)
- Lazy loading (завантажує, коли зображення наближається до viewport)
- Запобігає зсуву контенту (CLS) -- резервує місце
- Адаптивні розміри для різних екранів
Зображення на всю ширину
import Image from "next/image";
export default function HeroBanner() {
return (
<div className="relative w-full h-[400px]">
<Image
src="/banner.jpg"
alt="Банер"
fill
className="object-cover"
priority // Завантажити одразу (для above-the-fold)
/>
</div>
);
}
Зовнішні зображення
Для зовнішніх URL потрібно додати домен в конфігурацію:
// next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "api.example.com",
},
],
},
};
export default nextConfig;
<Image
src="https://api.example.com/photos/1.jpg"
alt="Зовнішнє зображення"
width={600}
height={400}
/>
Metadata API
Next.js має вбудований API для SEO-метаданих:
Статичні metadata
// src/app/layout.tsx
import type { Metadata } from "next";
export const metadata: Metadata = {
title: {
default: "Мій сайт",
template: "%s | Мій сайт", // Для дочірніх сторінок
},
description: "Навчальний проект на Next.js",
openGraph: {
title: "Мій сайт",
description: "Навчальний проект на Next.js",
locale: "uk_UA",
type: "website",
},
};
// src/app/about/page.tsx
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Про нас", // Стане "Про нас | Мій сайт" (через template)
description: "Дізнайтеся більше про нас",
};
Динамічні metadata
// src/app/blog/[slug]/page.tsx
import type { Metadata } from "next";
export async function generateMetadata({
params,
}: {
params: Promise<{ slug: string }>;
}): Promise<Metadata> {
const { slug } = await params;
const post = await getPostBySlug(slug);
return {
title: post.title,
description: post.description,
openGraph: {
title: post.title,
images: [post.coverImage],
},
};
}
Підсумок
- Глобальні стилі імпортуються в
layout.tsx - CSS Modules (
.module.css) -- локальні стилі без конфліктів - Tailwind CSS -- утилітарні класи прямо в JSX, mobile-first, dark mode
- next/font -- оптимізація шрифтів (Google Fonts, локальні), без CLS
- next/image -- оптимізація зображень (формати, lazy loading, адаптивність)
- Metadata API -- SEO метадані (статичні та динамічні)
Що далі?
У наступному, фінальному уроці блоку вивчимо deployment -- як збілдити і задеплоїти Next.js додаток на Vercel або Netlify.
Корисні посилання:
- Next.js: CSS -- стилізація в Next.js
- Tailwind CSS Documentation -- повна документація Tailwind
- Next.js: Image Optimization -- оптимізація зображень
- Next.js: Font Optimization -- оптимізація шрифтів