SEO12 мин5 июля 2026 г.

Технический SEO на Next.js: Core Web Vitals, Schema и hreflang в 2026

Технический SEO на Next.js: Core Web Vitals, Schema и hreflang в 2026

Общее SEO — ключевые слова и контент — не спасёт сайт, если Google видит LCP 5 секунд, INP 400 мс и пустой HTML до гидрации React. В 2026 году технический SEO для JavaScript-фреймворков — это не «опция для гиков», а базовая гигиена: Core Web Vitals с INP вместо FID, JSON-LD Schema.org, корректный hreflang для ru/en/uz/zh и правильная стратегия рендеринга в Next.js 16 App Router. Разбираем на уровне кода и production-решений для рынка Узбекистана — без пересказа документации.

01Core Web Vitals 2026: LCP, INP и CLS — что измеряет Google

Core Web Vitals (CWV) — три метрики реального пользовательского опыта, которые Google использует в ранжировании и в отчётах Search Console. Это не синтетический балл PageSpeed, а данные с поля (field data) из Chrome UX Report (CrUX) — 75-й перцентиль за 28 дней. Google делит результат на три корзины: Good, Needs Improvement, Poor — и именно field data определяет, попадёт ли URL в «зелёную» зону. LCP (Largest Contentful Paint) — время до отрисовки крупнейшего видимого элемента (hero-картинка, заголовок, видео-постер). Порог «хорошо»: < 2.5 сек (4.0 с — «нужно улучшить», выше — «плохо»). Медленный LCP на 4G в Ташкенте — типичная причина отказов с рекламы; подробнее про бизнес-эффект в статье про скорость сайта. TTFB + загрузка CSS/шрифтов + декодирование hero-изображения складываются в LCP — оптимизировать нужно всю цепочку, а не только картинку. INP (Interaction to Next Paint) с марта 2024 заменил FID (First Input Delay) как полноценный CWV. FID измерял только задержку первого клика и игнорировал последующие; INP фиксирует худший отклик на любое взаимодействие за сессию (тапы, клики, ввод). Порог «хорошо»: < 200 мс (500 мс — «нужно улучшить»). Тяжёлые обработчики событий, блокировка main thread React-гидрацией, Long Tasks > 50 мс и сторонние виджеты (Jivo, Intercom, reCAPTCHA) — главные виновники INP на SPA. CLS (Cumulative Layout Shift) — суммарный сдвиг вёрстки без действия пользователя. Порог: < 0.1. Баннер cookie без резервирования высоты, web-шрифты без `size-adjust`, lazy-картинки без `width`/`height`, динамически подгружаемые рекламные блоки — классика. CLS особенно болезнен на мобильном: пользователь промахивается по кнопке «Заказать» — конверсия падает напрямую. Как измерять: 1. PageSpeed Insights — lab (Lighthouse) + field (CrUX), если накоплены данные по origin/URL. 2. Search Console → Core Web Vitals — группы URL с проблемами, удобно для приоритизации. 3. web-vitals npm-библиотека — `onLCP`, `onINP`, `onCLS` с отправкой в GA4 или свой endpoint. 4. Real User Monitoring (RUM) — Vercel Speed Insights, Datadog, Sentry Performance; обязателен для сайтов с трафиком из UZ, где CrUX может не иметь URL-level данных. Lab-данные полезны при разработке и CI; для SEO решает field data. Страница с PSI 95, но CrUX «Needs improvement» — реальная проблема. Связывайте CWV с аналитикой: сегментируйте конверсию по LCP-бuckets и смотрите, где теряете заявки.

02Оптимизация Core Web Vitals в Next.js 16: инструменты App Router

Next.js 15/16 с React 19 и Turbopack (dev-сборка по умолчанию) даёт встроенные рычаги — используйте их, а не подключайте jQuery-плагины поверх React. Наша позиция для production: Server Component по умолчанию, `'use client'` только там, где нужны hooks и browser API. Изображения — `next/image`: • `priority` на LCP-элементе above-the-fold — отключает lazy-load, добавляет `<link rel="preload">`. • `sizes` с медиа-условиями — браузер выбирает правильный srcset, не тянет 2000px на мобильный. • `placeholder="blur"` с `blurDataURL` — резервирует место, снижает CLS. • AVIF/WebP — автоматически через Image Optimization API; на Vercel не нужен отдельный CDN для ресайза. Шрифты — `next/font`: self-hosting Inter/Roboto с `display: 'swap'` и `adjustFontFallback: true` — без лишних запросов к Google Fonts и с минимальным CLS. Для uz/ru контента подключайте `cyrillic` и `latin` subsets сразу. Скрипты — `next/script`: • `strategy="afterInteractive"` — GA4, Meta Pixel после гидрации. • `strategy="lazyOnload"` — чаты, виджеты, карты на странице контактов. • Partytown — если нужен third-party JS в Web Worker (продвинутый кейс). Code splitting — `next/dynamic`: тяжёлые клиентские блоки (графики, слайдеры, калькуляторы) грузите с `{ ssr: false, loading: () => <Skeleton /> }` — не блокируют INP на первом экране. React Server Components (RSC): HTML на сервере без отправки component JS — меньше bundle, быстрее LCP. Оборачивайте медленные блоки (отзывы, рекомендации) в `<Suspense fallback={<Skeleton />}>` — streaming отдаёт shell раньше. `loading.tsx` на уровне route — UX-паттерн App Router для perceived performance. Гидрация: несовпадение server/client HTML (`typeof window`, `Math.random()`, `Date.now()` в render) вызывает re-render и бьёт по INP. Extension-блокировщики рекламы тоже ломают гидрацию — тестируйте в incognito. Turbopack ускоряет dev-итерации — быстрее ловите регрессии CWV до деплоя. `@next/bundle-analyzer` — смотрите, какой client chunk раздувает main thread. На production Vercel edge-кеш + CDN закрывают TTFB для UZ-трафика; для лендингов под рекламу это критично.

03Schema.org и JSON-LD в App Router: разметка для бизнеса в Узбекистане

Structured data помогает Google и Яндексу показывать rich results: звёзды, FAQ-аккордеон, breadcrumbs, карточки товаров. В App Router JSON-LD вставляется в Server Component — поисковик получает разметку в первом HTML-ответе, без ожидания JavaScript. Microdata в HTML-атрибутах (`itemscope`) в React-разметке неудобна — JSON-LD в `<script>` проще поддерживать и версионировать. Базовый паттерн в layout или page.tsx: ```tsx <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }} /> ``` Вынесите schema в `lib/schema.ts` — типизируйте через TypeScript, переиспользуйте между ru/en/uz/zh версиями с локализованным `name` и `description`. Какие схемы ставить на типичный UZ-сайт:`Organization` — root `layout.tsx`: название, logo (ImageObject с width/height), sameAs (Telegram, Instagram, LinkedIn), url, foundingDate. • `LocalBusiness` — `/contact`: адрес в Ташкенте, geo (latitude/longitude), openingHoursSpecification, telephone (+998…), priceRange. • `Article` + `BreadcrumbList` — каждый пост блога: headline, datePublished, dateModified, author (Person), publisher (Organization с logo). • `FAQPage` — страницы услуг с блоком Q&A — шанс на FAQ-rich snippet. • `Product` + `Offer` + `AggregateRating`интернет-магазин: priceCurrency UZS, availability InStock/OutOfStock. • `Service` — `/uslugi/*`: serviceType, areaServed (Tashkent, Uzbekistan), provider. Пример LocalBusiness для Ташкента (компактный, валидный): ```json { "@context": "https://schema.org", "@type": "LocalBusiness", "name": "UZNEO", "address": { "@type": "PostalAddress", "streetAddress": "ул. Примерная, 1", "addressLocality": "Ташкент", "addressCountry": "UZ" }, "telephone": "+998901234567", "url": "https://uzneo.uz" } ``` Production-правила: не смешивайте противоречивые `@type` на одной странице; телефон в Schema = телефон в footer. Не размечайте несуществующие `AggregateRating` — Google penalizes fake reviews markup. Валидируйте в Rich Results Test и Search Console → Enhancements. Schema не заменяет контент, но повышает CTR на 10–20% в нишах с FAQ/Product snippets. Связка с общим SEO обязательна.

04hreflang для мультиязычных сайтов: ru, en, uz, zh без ошибок

Сайт на четырёх языках — стандарт для экспорта и локального бизнеса в Узбекистане. hreflang говорит Google, какая URL-версия для какой аудитории. Ошибки здесь = дубли в индексе, каннибализация ключей, потеря en/uz/zh сегментов. На UZNEO-проектах структура `/`, `/en/`, `/uz/`, `/zh/` — path-based i18n, не subdomain (проще SSL, один origin для CrUX). Коды языков (BCP 47): • Русский: `ru-UZ` (рекомендуем) или `ru` • English: `en-UZ` или `en` • O'zbek (Latin): `uz-Latn-UZ` — не путать с `uz-UZ` (кириллица/legacy) • 中文: `zh-Hans` (упрощённый) или `zh-CN` `x-default` — fallback для пользователей без совпадения локали. Обычно русская версия главной — основной трафик UZ-сайтов. Next.js `generateMetadata` в `[lang]/blog/[slug]/page.tsx`: ```tsx alternates: { canonical: `https://uzneo.uz/${lang}/blog/${slug}`, languages: { 'ru-UZ': `https://uzneo.uz/blog/${slug}`, 'en-UZ': `https://uzneo.uz/en/blog/${slug}`, 'uz-Latn-UZ': `https://uzneo.uz/uz/blog/${slug}`, 'zh-Hans': `https://uzneo.uz/zh/blog/${slug}`, 'x-default': `https://uzneo.uz/blog/${slug}`, }, } ``` Next генерирует `<link rel="alternate" hreflang="…">` в `<head>` автоматически — не дублируйте вручную. Типичные баги: 1. Missing return links — каждая языковая версия должна ссылаться на все остальные, включая себя (self-referencing). 2. Canonical на другой язык — canonical всегда на текущую локализованную URL. 3. hreflang между непереведёнными страницами — только полные переводы, не «похожие» услуги. 4. Trailing slash mismatch — `/en/blog/slug` vs `/en/blog/slug/` — Google видит как разные URL. 5. HTTP vs HTTPS, www vs non-www — alternates только на канонический хост. Валидация: Search Console → Pages → hreflang errors; Merkle hreflang tag testing tool; XML sitemap с `xmlns:xhtml` и `xhtml:link`. Для продвижения бизнеса online мультиязычность без hreflang — половина бюджета впустую. UZNEO держит единую таблицу `slug → locale[]` в коде — при добавлении пятого языка не ломаются return links.

05sitemap.xml, robots.txt и MetadataRoute в App Router

В Next.js 16 статический `public/sitemap.xml` уступает место динамическим route handlers типа `MetadataRoute` — sitemap всегда актуален после деплоя новой статьи или услуги. Генерируйте URL из БД или CMS, не хардкодьте список. `app/sitemap.ts` — минимальный рабочий пример: ```tsx import type { MetadataRoute } from 'next' import { getAllPosts } from '@/lib/blog' export default async function sitemap(): Promise<MetadataRoute.Sitemap> { const posts = await getAllPosts() return posts.flatMap((p) => [ { url: `https://uzneo.uz/blog/${p.slug}`, lastModified: p.updated, priority: 0.8, alternates: { languages: { en: `https://uzneo.uz/en/blog/${p.slug}` } } }, ]) } ``` `app/robots.ts`: ```tsx export default function robots(): MetadataRoute.Robots { return { rules: [{ userAgent: '*', allow: '/', disallow: ['/admin/', '/api/', '/tracker/'] }], sitemap: 'https://uzneo.uz/sitemap.xml', } } ``` Best practices:`lastModified` — дата реального изменения из CMS (`updated` field), не `new Date()` на каждый запрос. • Sitemap index — для 1000+ URL: `sitemap-blog.xml`, `sitemap-services.xml`, `sitemap-products.xml` + index. • Не включайте: `/api/*`, preview deployments, URL с UTM, faceted filters (`?sort=price`). • Лимит Google — 50 000 URL или 50 MB на файл; при превышении — index. • `changeFrequency` и `priority` — Google largely ignores, но полезны для Yandex. Куда отправить: 1. Google Search Console → Sitemaps — добавьте URL, следите за «Couldn't fetch». 2. Yandex.Webmaster — критично для UZ/CIS; Яндекс читает sitemap и robots иначе, чем Google. 3. Bing Webmaster — питает Bing, DuckDuckGo, Copilot Search. Robots vs meta robots: `robots.txt` блокирует crawl, но не garantирует de-index; для закрытия — `noindex` в metadata. Staging на `*.vercel.app` — `Disallow: /` + `X-Robots-Tag: noindex` в headers. После миграции с WordPress/Tilda обновите sitemap и запросите переобход — см. выбор CMS. Ping sitemap после крупных обновлений каталога.

06SSR, SSG, ISR и PPR: стратегия рендеринга для SEO в 2026

Google рендерит JavaScript, но с задержкой (hours–days) и менее надёжно, чем готовый HTML в первом ответе. Yandex historically ещё чувствительнее к CSR — чистый client-side React SPA (Vite/CRA) без SSR часто теряет индексацию страниц глубже главной. Vue/Nuxt и SvelteKit решают это SSR/SSG из коробки — принципы те же, что в Next.js. SSR (Server-Side Rendering): HTML на каждый запрос через `fetch` с `cache: 'no-store'` или dynamic route. Плюс: всегда свежие данные (остатки, курс UZS/USD). Минус: TTFB выше, нагрузка на сервер/edge. Подходит: личный кабинет, поиск с фильтрами, realtime-данные. SSG (Static Site Generation): HTML на build time — `generateStaticParams` для всех slug. Максимальная скорость, идеальный LCP, минимальный crawl budget. Подходит: маркетинг, блог, лендинги80% бизнес-сайтов UZNEO. ISR (Incremental Static Regeneration): `export const revalidate = 3600` — статика + фоновое обновление раз в час/день. Золотая середина для каталога товаров, прайсов в UZS, курсов валют. Первый visitor после expiry получает stale HTML, следующие — fresh (stale-while-revalidate). On-demand revalidation: `revalidatePath('/blog/[slug]')` или `revalidateTag('products')` после webhook из CMS — мгновенное обновление без full rebuild. Обязательно для контент-команды, которая публикует ежедневно. PPR (Partial Prerendering, Next.js 15+): статическая оболочка (header, footer, hero) + streaming динамических «дыр» через `<Suspense>` (персонализация, корзина, «последние заказы»). В 2026 production-ready на Vercel — рекомендуем для главной с блоком акций, который меняется каждый час. Рекомендации UZNEO: 1. Маркетинг + блог + услуги → SSG/ISR `revalidate: 86400`. 2. E-commerce → ISR на product pages, SSG на categories/brands. 3. Admin, tracker, dashboard → `'use client'` + SSR auth, noindex в metadata. 4. Не SPA на Vite для SEO-критичных страниц — см. WordPress vs Tilda vs Next.js. `unstable_cache` / `fetch` cache в Server Components — кешируйте DB-запросы между ISR-regenerations. Полностью client-rendered React — архитектурный антипаттерн для SEO в 2026.

Итог

Технический SEO в 2026 — это измеримые CWV (LCP < 2.5s, INP < 200ms, CLS < 0.1), валидный JSON-LD, симметричный hreflang и sitemap с alternates. Next.js 16 App Router закрывает 90% задач из коробки — если не превращать каждую страницу в client component. UZNEO проектирует сайты и интернет-магазины с этими практиками с первого дня: Schema, мультиязычность ru/en/uz/zh, ISR и мониторинг Web Vitals. Избегайте типичных ошибок при создании сайта — начните с архитектуры, а не с плагинов. Обсудить проект — в Telegram.

Нужна помощь с проектом?

Расскажите о задаче — посмотрим, чем сможем помочь