StriveLab
Strony internetowe
Usługi
RealizacjeO mnieBlogPorozmawiajmy
PL
EN

Astro

Ultraszybkie projekty, łączące lekkość ze skalowalnością.

Next.js

Elastyczne i wydajne narzędzia dla biznesu, które dotrzymają kroku Twojemu rozwojowi.

React

Połączenie intuicyjności z wydajnością, które zapewnia bezproblemową skalowalność kodu.

SEO & Performance

Audyt techniczny i optymalizacja pod kątem SEO i GEO.

Automatyzacja AI

Bezpieczne automatyzacje procesów i agenci AI w n8n, Make i Claude.

QA & Automation

Testy automatyczne komponentów i E2E w Cypress.

Konsultacje

Połączenie perspektywy produktu, developera i marketingu w jednym miejscu

StriveLab
Strony internetowe
Usługi
RealizacjeO mnieBlogPorozmawiajmy
PL
EN

Astro

Ultraszybkie projekty, łączące lekkość ze skalowalnością.

Next.js

Elastyczne i wydajne narzędzia dla biznesu, które dotrzymają kroku Twojemu rozwojowi.

React

Połączenie intuicyjności z wydajnością, które zapewnia bezproblemową skalowalność kodu.

SEO & Performance

Audyt techniczny i optymalizacja pod kątem SEO i GEO.

Automatyzacja AI

Bezpieczne automatyzacje procesów i agenci AI w n8n, Make i Claude.

QA & Automation

Testy automatyczne komponentów i E2E w Cypress.

Konsultacje

Połączenie perspektywy produktu, developera i marketingu w jednym miejscu

Astro

Ultraszybkie projekty, łączące lekkość ze skalowalnością.

Next.js

Elastyczne i wydajne narzędzia dla biznesu, które dotrzymają kroku Twojemu rozwojowi.

React

Połączenie intuicyjności z wydajnością, które zapewnia bezproblemową skalowalność kodu.

SEO & Performance

Audyt techniczny i optymalizacja pod kątem SEO i GEO.

Automatyzacja AI

Bezpieczne automatyzacje procesów i agenci AI w n8n, Make i Claude.

QA & Automation

Testy automatyczne komponentów i E2E w Cypress.

Konsultacje

Połączenie perspektywy produktu, developera i marketingu w jednym miejscu

RealizacjeO mnieBlog
Porozmawiajmy
PL
EN

Nowoczesne strony internetowe dla firm, które myślą odważnie.

Przewiń do góry

Nazwa

StriveLab Maciej Sala

NIP

6772218995

REGON

524008527

E-mail

contact@strivelab.pl

Usługi główne
  • Tworzenie stron internetowych
  • Strony internetowe Next.js
  • Strony internetowe Astro
  • Strony internetowe React
Inne usługi
  • Usługi
  • Audyt SEO i Performance
  • Testy automatyczne i QA
  • Konsultacje Produktowe
  • Automatyzacja Procesów AI
  • Aplikacje webowe Next.js
  • Współpraca ciągła
Strony
  • O mnie
  • Usługi
  • Realizacje
  • Blog

© 2026 StriveLab.pl

Polityka prywatności
Next.jsSEOWydajność

Pagespeed 100/100 w Next.js — case study optymalizacji strony usługowej

100/100 w PageSpeed dla strony Next.js — jak to wygląda w realnym projekcie, nie na prostej demo. Metryki, decyzje i co naprawdę dało wynik.

OpublikujLinkedInFacebookWyślij
Autor
Maciej Sala
Opublikowano
10 kwietnia 2026 14:40
Czytanie
4 min czytania
Aktualizacja
25 maja 2026 10:55

Mamy tu typową stronę usługową zbudowaną w Next.js, wyposażoną w Google Analytics, Google Tag Manager, prosty formularz kontaktowy, galerię zdjęć i animacje – czyli standardowy zestaw. Na początku testy PageSpeed Insights pokazały wyniki: 67/100 na urządzeniach mobilnych i 89/100 na komputerach stacjonarnych.

Artykuł w skrócie

  • Fonty przez next/font — eliminacja layout shift fontów i blokowania renderowania to często pierwsze 5–10 punktów w PageSpeed Insights.
  • next/image z priority i sizes — hero image z priority ładuje się poza kolejką lazy loading; poprawny sizes redukuje rozmiar pliku.
  • Third-party scripts leniwie — GTM i inne skrypty analityczne ładuj przez next/script ze strategy="lazyOnload" lub strategy="afterInteractive".
  • Client Components do minimum — każdy "use client" powiększa bundle; izoluj interaktywność w małych komponentach zamiast opakowywać duże sekcje.
  • Testuj na produkcji — Lighthouse lokalnie nie mierzy TTFB serwera; wynik 100 w dev nie gwarantuje tego samego na deployowanej stronie.

Punkt wyjścia: typowa strona usługowa

Lighthouse wskazało główne problemy: duże zasoby JavaScript (GTM) blokujące renderowanie, niezoptymalizowane obrazy, brak odpowiednich wskazówek dla przeglądarki (resource hints) oraz nadmierne przesunięcia układu strony (layout shift) związane z ładowaniem czcionek.

Naszym celem było poprawienie rzeczywistych wskaźników Core Web Vitals to zestaw metryk Google oceniających realne doświadczenie użytkownika: LCP (szybkość ładowania), INP (responsywność) i CLS (stabilność wizualna). Wpływają na ranking i konwersję., a przy okazji osiągnięcie jak najwyższego wyniku w testach laboratoryjnych Lighthouse. Wynik 100/100 zarówno na urządzeniach mobilnych, jak i desktopowych jest możliwy do uzyskania, ale nie powinien być celem za wszelką cenę. Generalnie uzyskanie powyżej 90/100 w zupełności wystarcza.

PageSpeed Mobile po optymalizacji
67 → 100PageSpeed Mobile po optymalizacji
LCP po uporządkowaniu obrazów i fontów
3.2 s → 1.1 sLCP po uporządkowaniu obrazów i fontów
JavaScript bundle po ograniczeniu Client Components
210 KB → 85 KBJavaScript bundle po ograniczeniu Client Components
Uwaga

Wynik 100/100 jest dobrym dowodem technicznej dyscypliny, ale nie jest celem biznesowym. Ważniejsze jest to, czy realni użytkownicy mają szybki LCP, stabilny layout i responsywny formularz na produkcji.

Krok 1: Fonty — eliminacja layout shift

Problem stanowiły Google Fonts ładowane z CDN, czyli Content Delivery Network, to rozproszona sieć serwerów dostarczająca zasoby z węzła najbliższego użytkownikowi; CDN do obrazów dodatkowo transformuje je w locie. powodowały FOUT i CLS (Cumulative Layout Shift) to Core Web Vital mierzący nieoczekiwane przesunięcia elementów podczas ładowania i działania strony. Animacje psują go wtedy, gdy ruszają właściwości layoutowe albo gdy pojawiający się element nie ma zarezerwowanego miejsca..

Rozwiązaniem było użycie next/font z samodzielnym hostowaniem i size-adjust:

Code
import { Inter } from 'next/font/google'
 
const inter = Inter({
  subsets: ['latin', 'latin-ext'],
  display: 'swap',
  variable: '--font-inter',
})

W wyniku tych działań wyeliminowaliśmy zewnętrzny request do fonts.googleapis.com i uzyskaliśmy spadek CLS z fontów z 0.12 do 0.00.

Krok 2: Obrazy — next/image z priority i sizes

Problem stanowił obraz hero image (LCP, czyli Largest Contentful Paint, mierzy czas do wyrenderowania największego widocznego elementu — oznaczenie go preload przyspiesza jego załadowanie. element) ładował się z opóźnieniem, brak sizes powodował pobieranie zbyt dużych wariantów.

Code
<Image
  src={heroImage}
  alt="Strona internetowa Next.js"
  priority // Najwyższy priorytet dla LCP image
  placeholder="blur"
  sizes="100vw"
  className="h-auto w-full"
/>

Rozwiązaniem jest konwersja wszystkich obrazów na WebP/AVIF automatycznie przez next/image, dodanie sizes do każdego <Image> z precyzyjnymi breakpointami.

W wyniku tych działań LCP spadł z 3.2 s do 1.4 s (a po komplecie optymalizacji z kolejnych kroków zszedł do 1.1 s).

Krok 3: Third-party scripts — lazy loading GTM

Problemem okazał się GTM, czyli Google Tag Manager, pozwala zarządzać tagami i skryptami marketingowymi bez każdej zmiany w kodzie aplikacji. (130 KB), który blokował rendering. GTM ładowany w <head> opóźniał FCP (First Contentful Paint) to czas do wyrenderowania pierwszego fragmentu treści strony — pierwszy sygnał dla użytkownika, że coś się ładuje. o 800 ms.

Rozwiązanie: @next/third-parties z opóźnionym ładowaniem:

Code
// app/layout.tsx
import { GoogleTagManager } from '@next/third-parties/google'
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="pl">
      <body>
        {children}
        <GoogleTagManager gtmId="GTM-XXXXX" />
      </body>
    </html>
  )
}

@next/third-parties automatycznie opóźnia ładowanie GTM i skrypt nie blokuje renderingu.

Alternatywnie, jeśli nie korzystasz z @next/third-parties, ten sam efekt osiągniesz ręcznie przez next/script ze strategy="lazyOnload" — GTM ładuje się dopiero po onload:

Code
'use client'
 
import Script from 'next/script'
 
export function LazyGTM() {
  return (
    <Script
      id="gtm"
      strategy="lazyOnload" // Ładuj po onload
      src={`https://www.googletagmanager.com/gtm.js?id=GTM-XXXXX`}
    />
  )
}

W wyniku tych działań TBT, czyli Total Blocking Time, to łączny czas, w którym główny wątek jest zablokowany i nie reaguje na interakcje użytkownika podczas ładowania. spadł z 420 ms do 80 ms (po minimalizacji Client Components w kroku 4 zszedł finalnie do 60 ms).

Krok 4: JavaScript bundle — minimalizacja Client Components

Zauważyliśmy, że w projekcie było zbyt wiele Client Components. Przykładowo, sekcja hero, nawigacja i stopka były oznaczone jako 'use client', choć w rzeczywistości nie wymagały interaktywności po stronie klienta.

Rozwiązaniem było przekształcenie tych elementów na Server Components wszędzie tam, gdzie było to możliwe. Client Components zostały zachowane wyłącznie dla funkcjonalności wymagających faktycznej interakcji, takich jak przełącznik menu mobilnego, formularz kontaktowy czy animacje związane ze scrollowaniem.

Code
/* PRZED — cały hero jako Client Component
'use client'; // ← niepotrzebne
export function HeroSection() { ... } */
 
/* PO — Server Component (zero JS w bundle) */
export function HeroSection() { ... }

W wyniku tych optymalizacji JavaScript bundle zmalał z 210 KB do 85 KB (gzipped).

Krok 5: Preconnect i DNS prefetch

Metadata API nie służy do dowolnego generowania tagów preconnect i dns-prefetch, więc najprościej dodać je bezpośrednio w <head>:

Code
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="pl">
      <head>
        <link rel="preconnect" href="https://www.googletagmanager.com" />
        <link rel="dns-prefetch" href="https://www.google-analytics.com" />
      </head>
      <body>{children}</body>
    </html>
  )
}

Krok 6: CSS — eliminacja nieużywanych klas

Tailwind CSS z konfiguracją content automatycznie tree-shakuje nieużywane klasy. Upewnij się, że ścieżki są poprawne:

Code
// tailwind.config.ts
const config = {
  content: [
    './app/**/*.{ts,tsx}',
    './components/**/*.{ts,tsx}',
    // NIE: './node_modules/**' — to zaciągnie ogromne CSS
  ],
}

Wynikowy CSS: ~12 KB (gzipped) zamiast pełnych ~300 KB Tailwind.

Krok 7: Animacje bez blokowania

Framer Motion ładowane lazy, ale tylko na stronach z animacjami:

Code
import dynamic from 'next/dynamic'
 
const AnimatedSection = dynamic(() => import('./animated-section'), {
  ssr: false,
  loading: () => <div className="h-96" />,
})

Micro-interactions (hover, tap) zastąpione czystym CSS:

Code
.btn-hover {
  transition: transform 0.15s ease;
}
.btn-hover:hover {
  transform: scale(1.02);
}
.btn-hover:active {
  transform: scale(0.98);
}

Wynik końcowy

Podsumowując wszystkie działania optymalizacyjne dobiliśmy do poniższych wyników:

MetrykaPrzedPo
PageSpeed Mobile67100
PageSpeed Desktop89100
LCP3.2 s1.1 s
INP180 ms45 ms
CLS0.120.00
TBT420 ms60 ms
JS bundle210 KB85 KB
CSS45 KB12 KB

Checklist optymalizacji

Przed każdym deploymentem, warto skorzystać z tej krótkiej checklisty:

  • next/font dla wszystkich fontów (z latin-ext dla polskiego),
  • next/image z priority na LCP element i sizes na każdym obrazie,
  • Third-party scripts z strategy="lazyOnload" lub @next/third-parties,
  • Minimum Client Components — Server Components jako domyślne,
  • Preconnect do zewnętrznych domen,
  • Tailwind z poprawnymi content ścieżkami,
  • Animacje CSS zamiast JS dla micro-interactions,
  • headers() w next.config.ts z cache-control dla statycznych assets.

Werdykt Labu

100/100 w PageSpeed da się osiągnąć w warunkach laboratoryjnych, ale większe znaczenie mają stabilnie, szybkie i dobre Core Web Vitals na produkcji. Największe zyski w Next.js dają: fonty hostowane samodzielnie (CLS → 0), atrybut priority na LCP image, lazy loading third-party scripts i minimalizacja Client Components.

Nie musisz rezygnować z funkcjonalności, ale musisz ładować je w sposób logiczny i przemyślany. Pamiętaj, by sprawdzać CWV po każdej większej zmianie treści, aktualizacji czy dodaniu nowego modułu na stronie.

Audyt techniczny i optymalizacja pod kątem SEO i GEO.
SEO & Performance
  • Punkt wyjścia: typowa strona usługowa1 min
  • Krok 1: Fonty — eliminacja layout shift1 min
  • Krok 2: Obrazy — next/image z priority i sizes1 min
  • Krok 3: Third-party scripts — lazy loading GTM1 min
  • Krok 4: JavaScript bundle — minimalizacja Client Components1 min
  • Krok 5: Preconnect i DNS prefetch1 min
  • Krok 6: CSS — eliminacja nieużywanych klas1 min
  • Krok 7: Animacje bez blokowania1 min
  • Wynik końcowy1 min
  • Checklist optymalizacji1 min
  • Werdykt Labu1 min

Często zadawane pytania

Źródła i data weryfikacjiZweryfikowano: 20 maja 2026

Progi Core Web Vitals, wytyczne dotyczące metryk oraz dokumentację narzędzi optymalizacyjnych zweryfikowano na podstawie oficjalnych źródeł Google i Next.js:

web.dev — Core Web Vitals, web.dev — LCP, web.dev — INP, web.dev — CLS, PageSpeed Insights, Next.js — next/image, Next.js — next/font, Next.js — next/script.

Seria

Wydajność stron w praktyce
Część 2 / 2
  1. 1Core Web Vitals — jak przyspieszyć stronę i poprawić pozycję w Google
  2. Pagespeed 100/100 w Next.js — case study optymalizacji strony usługowej
Maciej Sala

O autorze

Maciej Sala

Maciej Sala — Product Manager i Frontend Developer z bogatym doświadczeniem w marketingu internetowym oraz SEO. Na co dzień pracuje z Reactem, Next.js i TypeScriptem, a ostatnio także z Astro i narzędziami do automatyzacji procesów AI. Sprawnie łączy perspektywę produktową z praktycznym podejściem do kodu. Przez kilka lat był związany z branżą gier wideo jako project manager i game designer. Absolwent historii na Uniwersytecie Jagiellońskim oraz studiów podyplomowych z marketingu internetowego na AGH w Krakowie. Po godzinach trenuje na siłowni, maluje figurki i rozwija własne projekty side-projecty.

Moje artykułyWięcej o mnie

Pomagam przekładać takie tematy na konkretne wdrożenia w frontendzie, SEO, analityce i procesie produktowym.

Umów konsultację

Biblioteka wiedzy

Czytaj dalej

Zobacz więcej wpisów
Lighthouse 100/100 w Astro — case study optymalizacji strony usługowej
Lighthouse 100/100 w Astro — case study optymalizacji strony usługowej

Jak realna strona usługowa uzyskała 100/100 w Lighthouse? Konkretne metryki, konkretne poprawki w Astro — bez handwavingu.

Maciej Sala

Maciej Sala

Founder Strivelab

29 maja 2026
Core Web Vitals — jak przyspieszyć stronę i poprawić pozycję w Google
Core Web Vitals — jak przyspieszyć stronę i poprawić pozycję w Google

LCP, INP i CLS — co każda metryka mierzy, jak ją poprawić i co naprawdę wpływa na pozycje w Google. Bez ogólnych rad, konkretne techniki.

Maciej Sala

Maciej Sala

Founder Strivelab

14 listopada 2025
Google Tag Manager w Next.js — dataLayer, custom triggers i debugowanie jak pro
Google Tag Manager w Next.js — dataLayer, custom triggers i debugowanie jak pro

Jak wdrożyć Google Tag Manager w Next.js App Router bez chaosu w dataLayer: page_view, custom events, ecommerce, consent mode i debugowanie.

Maciej Sala

Maciej Sala

Founder Strivelab

25 września 2025
Poprzedni wpisNext.js Sitemap i robots.txt — automatyczna generacja z App RouteraSitemap i robots.txt w Next.js App Router bez zewnętrznych paczek. Dynamiczne sitemaps, lastmod i jak nie wyeksponować staging przez przypadek.
Maciej Sala

Maciej Sala

Founder Strivelab

10 kwietnia 2026
Następny wpisHreflang i canonical w Next.js — SEO wielojęzycznych stron bez duplikacjiBłędny hreflang lub canonical w Next.js generuje tysiące zduplikowanych stron. Jak uniknąć tego w App Router przy wielojęzycznej strukturze?
Maciej Sala

Maciej Sala

Founder Strivelab

10 kwietnia 2026