У клієнтській продуктивності вебзастосунків типографіка та сторонній JavaScript являють собою найбільшу загрозу стабільності інтерфейсу та часу реакції системи.
При традиційному підключенні зовнішніх шрифтів (наприклад, через <link href="https://fonts.googleapis.com/css2...">) браузер змушений виконувати багатокроковий мережевий каскад: запросити HTML, виявити CSS, встановити окреме TLS-з'єднання з сервером Google Fonts, розпарсити декларацію @font-face і лише потім завантажити двійкові файли .woff2. У цей період виникають деструктивні візуальні артефакти:
Сторонні скрипти (аналітика, рекламні трекери, віджети чатів підтримки) створюють іншу небезпеку: вони блокують головний потік виконання (Main Thread). Синхронний парсинг і компіляція сотень кілобайт зовнішнього коду заморожують введення користувача, деградуючи метрику INP (Interaction to Next Paint).
Next.js вирішує обидві проблеми на рівні архітектури завдяки модулям next/font (Zero-RTT білд-тайм локалізація ресурсів із синтезом метрик fallback-шрифта) та next/script (гранулярні стратегії пріоритизації виконання).
next/font#Модуль next/font переносить отримання зовнішніх шрифтів із рантайму браузера на етап статичної збірки (next build):
.next/static. Браузер користувача більше не робить жодного мережевого запиту до сторонніх серверів.next/script#Компонент <Script> інкапсулює розширене керування життєвим циклом тега <script>, узгоджуючи його виконання з процесом гідратації React-дерева.
Під час збірки Next.js завантажує двійкові файли WOFF2 локально в директорію проєкту. У рантаймі повністю відсутні зовнішні запити до серверів Google Fonts.
Генеруються правила @font-face з дескрипторами size-adjust та ascent-override. Текст резервує ідеальний геометричний простір у DOM.
У head кореневого layout додаються скрипти, необхідні до початку гідратації React (безпека, поліфіли ядра).
React активує віртуальний DOM. Одразу після цього запускаються скрипти аналітики та моніторингу помилок (Sentry, GTM).
Браузер завершує первинний рендеринг. Під час простою процесора (requestIdleCallback) підвантажуються чати та важкі SDK соцмереж.
Next.js підтримує як Variable Fonts, так і статичні накреслення.
import { Inter, JetBrains_Mono } from 'next/font/google';
import localFont from 'next/font/local';
// 1. Варіативний шрифт Google Fonts (містить усі значення ваги за замовчуванням)
export const inter = Inter({
subsets: ['latin', 'cyrillic'],
display: 'swap',
variable: '--font-inter',
});
// 2. Моноширинний шрифт із явним зазначенням значень ваги
export const jetbrainsMono = JetBrains_Mono({
subsets: ['latin'],
display: 'swap',
variable: '--font-jetbrains-mono',
});
// 3. Локальний кастомний шрифт із файлової системи
export const customBrandFont = localFont({
src: [
{
path: '../public/fonts/BrandFont-Regular.woff2',
weight: '400',
style: 'normal',
},
{
path: '../public/fonts/BrandFont-Bold.woff2',
weight: '700',
style: 'normal',
},
],
variable: '--font-brand',
display: 'swap',
});Шрифти ініціалізуються суворо на рівні кореневого шару та підключаються до <html> або <body> через CSS-змінні.
import type { Metadata } from 'next';
import { inter, jetbrainsMono } from '@/app/fonts';
import Script from 'next/script';
import '@/app/globals.css';
export const metadata: Metadata = {
title: 'Fullstack Agentic Lab - Оптимізована платформа',
description: 'Приклад високопродуктивного Next.js застосунку',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html
lang="uk"
className={`${inter.variable} ${jetbrainsMono.variable} antialiased`}
>
<head>
{/* beforeInteractive дозволений СУВОРО всередині Root Layout */}
<Script
id="security-headers-init"
strategy="beforeInteractive"
src="https://cdn.agenticlab.dev/security-shield.js"
/>
</head>
<body className="min-h-screen bg-bg-base text-text-primary font-sans">
{children}
</body>
</html>
);
}У Tailwind CSS v4 конфігураційний файл tailwind.config.ts не використовується. Прив'язка змінних next/font здійснюється декларативно в globals.css через директиву @theme inline.
@import "tailwindcss";
@theme inline {
/* Прив'язуємо CSS-змінні шрифтів до стандартних утиліт font-sans та font-mono */
--font-sans: var(--font-inter), ui-sans-serif, system-ui, -apple-system, sans-serif;
--font-mono: var(--font-jetbrains-mono), ui-monospace, SFMono-Regular, monospace;
--font-heading: var(--font-inter);
}next/script та інлайн-скрипти#Для впровадження аналітики, трекерів та віджетів використовується компонент <Script>.
'use client';
import Script from 'next/script';
export function AnalyticsScripts() {
return (
<>
{/* 1. afterInteractive: Зовнішній скрипт аналітики одразу після гідратації */}
<Script
src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"
strategy="afterInteractive"
/>
{/* 2. Інлайн-скрипт: Атрибут 'id' О Б О В ' Я З К О В И Й для дедуплікації */}
<Script id="google-analytics-setup" strategy="afterInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX', { page_path: window.location.pathname });
`}
</Script>
{/* 3. lazyOnload: Некритичний віджет чату підтримки під час простою */}
<Script
src="https://cdn.support-chat.com/widget.js"
strategy="lazyOnload"
onLoad={() => console.log('Support chat widget loaded during idle.')}
/>
</>
);
}Розробник додав тег <Script src='/payment-sdk.js' strategy='beforeInteractive' /> всередині сторінки app/checkout/page.tsx. Чому компілятор Next.js зупинить білд із фатальною помилкою?
@next/third-parties#У сучасних версіях Next.js ручне підключення Google Tag Manager, Google Analytics та YouTube через <Script> замінюється офіційним пакетом @next/third-parties. Він оптимізований інженерами Chrome Aurora для захисту метрик TBT та LCP.
import { GoogleTagManager, GoogleAnalytics } from '@next/third-parties/google';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="uk">
<body>
{children}
{/* Автоматично оптимізований запуск GTM */}
<GoogleTagManager gtmId="GTM-XYZ1234" />
<GoogleAnalytics gaId="G-XXXXXXXXXX" />
</body>
</html>
);
}display: 'optional'#Властивість font-display зі значенням 'optional' — найпотужніший інструмент для досягнення ідеального мобільного LCP:
export const inter = Inter({
subsets: ['latin', 'cyrillic'],
display: 'optional', // Ідеально для максимального балу Core Web Vitals на мобільних мережах
variable: '--font-inter',
});Яким чином next/font забезпечує нульовий Cumulative Layout Shift (CLS = 0) при заміні системного шрифта на кастомний вебшрифт?
Спроба використати стратегію strategy="beforeInteractive" всередині вкладених сторінок (page.tsx), клієнтських компонентів або дочірніх layout призводить до помилки збірки:
Error: next/script's beforeInteractive strategy is only allowed in the root layout (app/layout.js).
Рішення: Стратегія beforeInteractive дозволена суворо в кореневому app/layout.tsx. Для будь-яких вкладених сторінок використовуйте strategy="afterInteractive" або lazyOnload.
При передачі вбудованого коду всередину компонента <Script> без властивості id Next.js викидає помилку: next/script: id is required for inline scripts. Ця властивість необхідна фреймворку для дедуплікації скриптів під час клієнтської навігації між маршрутами.
Рішення: Завжди задавайте унікальний рядковий id: <Script id="analytics-init">{...}</Script>.
У Tailwind CSS v4 файл tailwind.config.ts застарів і ігнорується рушієм стилів. Спроба налаштувати theme.extend.fontFamily у TypeScript-файлі не дасть жодного результату.
Рішення: Оголошуйте відповідність змінних шрифтів через директиву @theme inline у файлі app/globals.css.
Підключення неваріативного шрифта (наприклад, Roboto) без зазначення конкретних значень ваги призводить до помилки збірки, а перерахування всіх велик (['100', '300', '400', '500', '700', '900']) змушує браузер завантажувати сотні кілобайт файлів.
Рішення: Завжди віддавайте перевагу варіативним шрифтам (Variable Fonts: Inter, Geist), які містять увесь спектр накреслень в одному легкому файлі WOFF2. Для неваріативних шрифтів підключайте виключно використовувані значення ваги (наприклад, тільки weight: ['400', '700']).
Підключення Google Tag Manager або Facebook Pixel через стратегію beforeInteractive або afterInteractive з десятками внутрішніх тегів призводить до того, що під час початкового завантаження головний потік браузера зависає на 500–1500 мс, викликаючи провал метрики INP.
Рішення: Використовуйте оптимізований пакет @next/third-parties/google (компоненти <GoogleTagManager> та <GoogleAnalytics>) або переносьте скрипти у веб-воркер через strategy="worker" із бібліотекою Partytown.
Ініціалізація шрифта (const inter = Inter({...})) всередині ізольованого компонента кнопки або картки призводить до того, що стилі шрифта підвантажуються в DOM тільки в момент першого рендеру цього компонента.
Рішення: Завжди виносьте ініціалізацію шрифтів у централізований модуль (наприклад, app/fonts.ts) та підключайте CSS-змінні в кореневому app/layout.tsx.