Next.js 15 — не просто «React с SSR». Это Server Components, Server Actions, partial prerendering и три уровня кэша. Claude Code помогает всё это собрать правильно — если знает, что App Router работает иначе, чем Pages Router.
Канал с гайдами и контентом по claude code, выкладываем новости (когда режут лимиты в 10 раз) и какие инструменты через claude реализуем для проектов, канал: https://t.me/claudedevolper
CLAUDE.md под Next.js 15
## Стек - Next.js 15 (App Router), React 19 - TypeScript strict - Prisma + PostgreSQL - Tailwind + shadcn/ui - Auth.js v5 (NextAuth) ## Правила - Это Next.js 15 App Router. НЕ Pages Router. - По умолчанию компонент — Server Component. "use client" только когда нужен браузерный API/state - Мутации — через Server Actions, не через API-роуты - Fetch делает кэш сам — не оборачивай вручную - next/image обязателен для картинок ## Запреты - Никаких `getServerSideProps`, `getStaticProps` — это Pages Router - Никаких `pages/api/*` для мутаций — только Server Actions - Не тащи `useEffect` в Server Components
Server Component с fetch
// app/products/page.tsx
import { Product } from "@/components/Product";
export default async function ProductsPage() {
const res = await fetch("https://api.example.com/products", {
next: { revalidate: 60 },
});
const products = await res.json();
return (
<div className="grid grid-cols-3 gap-4">
{products.map((p) => (
<Product key={p.id} product={p} />
))}
</div>
);
}next.revalidate: 60 — ISR: через минуту страница пересоберётся в фоне.
Server Action для мутаций
// app/actions/product.ts
"use server";
import { revalidatePath } from "next/cache";
import { z } from "zod";
import { prisma } from "@/lib/db";
import { auth } from "@/lib/auth";
const schema = z.object({
name: z.string().min(1),
priceCents: z.number().int().positive(),
});
export async function createProduct(formData: FormData) {
const session = await auth();
if (!session) throw new Error("Unauthorized");
const parsed = schema.safeParse({
name: formData.get("name"),
priceCents: Number(formData.get("priceCents")),
});
if (!parsed.success) {
return { error: parsed.error.flatten() };
}
await prisma.product.create({
data: { ...parsed.data, ownerId: session.user.id },
});
revalidatePath("/products");
return { ok: true };
}Использование в форме — без JS:
// app/products/new/page.tsx
import { createProduct } from "@/app/actions/product";
export default function NewProduct() {
return (
<form action={createProduct} className="space-y-4">
<input name="name" required className="border p-2" />
<input name="priceCents" type="number" required className="border p-2" />
<button type="submit">Создать</button>
</form>
);
}Работает даже с выключенным JS.
Streaming через Suspense
// app/dashboard/page.tsx
import { Suspense } from "react";
import { Stats } from "./Stats";
import { Activity } from "./Activity";
export default function Dashboard() {
return (
<div>
<h1>Дашборд</h1>
<Suspense fallback={<p>Статистика загружается...</p>}>
<Stats />
</Suspense>
<Suspense fallback={<p>Активность...</p>}>
<Activity />
</Suspense>
</div>
);
}Клиент получает shell → по мере готовности подтягивает блоки. Ощутимый прирост perceived performance, без конфигурации.
Три уровня кэша
Next.js 15 кэширует:
- Request Memoization — один
fetch()в одном рендере не делает два запроса - Data Cache — между рендерами, инвалидируется по времени или тегу
- Full Route Cache — статический HTML страниц
Ручное управление:
// Инвалидация по пути
import { revalidatePath } from "next/cache";
revalidatePath("/products");
// Инвалидация по тегу
import { revalidateTag } from "next/cache";
revalidateTag("products");
// Фетч с тегом
fetch(url, { next: { tags: ["products"] } });
// Отключить кэш
fetch(url, { cache: "no-store" });Подводные камни
- Client-only библиотеки в Server Components.
window is not defined— переноси в клиентский компонент. cookies()иheaders()— async в Next.js 15.const c = await cookies()— безawaitбудет warning.- Server Actions и FormData. Все значения — строки. Конвертируй вручную.
- Claude путает Pages и App Router. Прибивай в CLAUDE.md жёстко.
Как попробовать
1. npx create-next-app@latest → выбери App Router, TypeScript 2. CLAUDE.md из сниппета выше 3. Попроси: «Server Action для создания продукта через Prisma» 4. Проверь, что модель не скатилась в pages/api/*
Канал с гайдами и контентом по claude code, выкладываем новости (когда режут лимиты в 10 раз) и какие инструменты через claude реализуем для проектов, канал: https://t.me/claudedevolper
