Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds
Powrót do kolekcji
Przewodnik11 min czytania

Nuxt, framework fullstack dla Vue

Nuxt 4 w praktyce, tryby renderowania, pobieranie danych, warstwa serwerowa Nitro, moduły, koniec wsparcia Nuxt 3 i przygotowanie na wersję piątą.

Nuxt, czyli Vue z całą resztą w komplecie

Projekt na Vue zaczyna się od routera, potem dochodzi renderowanie po stronie serwera, potem warstwa danych, potem obsługa metadanych i mapa strony. Po miesiącu okazuje się, że połowa pracy poszła na sklejanie rzeczy, które w innych ekosystemach są w zestawie.

Nuxt zamyka tę lukę. Dokłada do Vue trasowanie z układu plików, renderowanie serwerowe w kilku trybach, warstwę backendową, automatyczne importy i system modułów. To odpowiednik tego, czym Next.js jest dla Reacta, przy czym Nuxt idzie dalej w stronę pełnego stosu, bo warstwa serwerowa jest tu osobnym, samodzielnym silnikiem.

Stan wersji w połowie 2026 roku

Zacznijmy od czegoś, co przy tym frameworku ma teraz największe znaczenie praktyczne.

Nuxt 3 zakończył wsparcie 31 lipca 2026 roku. To nie jest odległa zapowiedź, tylko fakt sprzed kilku dni, więc projekt stojący na trójce nie dostaje już poprawek bezpieczeństwa z głównego repozytorium. Dostępne są komercyjne przedłużenia wsparcia od firm trzecich, ale to rozwiązanie pomostowe, nie docelowe.

Nuxt 4 jest bieżącą wersją stabilną i rozwija się przyrostowo. Wersja 4.5 domyka etap, po którym uwaga zespołu przechodzi na przygotowanie piątki oraz narzędzi ułatwiających przejście.

Nuxt 5 pozostaje w rozwoju i nie ma konkretnej daty wydania, natomiast oficjalna mapa drogowa szacuje je na czwarty kwartał 2026 roku. Piątka ma przynieść silnik serwerowy Nitro w wersji trzeciej oraz dalsze zmiany łamiące zgodność, rozdzielone na dwa wydania właśnie po to, żeby ekosystem zdążył się dostosować. Wniosek dla planujących pracę jest prosty: dziś celem migracji jest czwórka, a nie oczekiwanie na piątkę.

Migracja z trójki na czwórkę jest łagodna z założenia, bo większość zmian dało się włączyć wcześniej flagą zgodności. Największa widoczna różnica to układ katalogów, w którym kod aplikacji przenosi się do osobnego katalogu, żeby oddzielić go od plików serwerowych i konfiguracji.

Pierwszy projekt

Code
Bash
npx nuxi@latest init moja-aplikacja
cd moja-aplikacja
npm install
npm run dev

Struktura po instalacji jest przewidywalna i to jedna z mocniejszych stron tego frameworka. Katalog stron odpowiada trasom, katalog komponentów jest importowany automatycznie, a katalog serwerowy trzyma punkty końcowe API.

Code
TEXT
app/
  pages/
    index.vue
    blog/[slug].vue
  components/
    ArticleCard.vue
  layouts/
    default.vue
server/
  api/
    articles.get.ts
nuxt.config.ts

Automatyczne importy dotyczą komponentów, funkcji kompozycyjnych i pomocników. Komponent z katalogu komponentów używasz w szablonie bez linii importu, podobnie jak funkcje wbudowane frameworka. To wygodne i bywa mylące na początku, bo trudno odgadnąć, skąd wzięła się dana nazwa. Rozwiązaniem jest przejście do definicji z poziomu edytora, które działa poprawnie dzięki typom generowanym przy starcie.

Tryby renderowania

Najważniejsza decyzja architektoniczna dotyczy tego, gdzie i kiedy powstaje HTML, a Nuxt pozwala rozstrzygnąć ją osobno dla każdej ścieżki.

Renderowanie po stronie serwera generuje stronę przy każdym żądaniu. Treść jest zawsze aktualna, koszt to praca serwera i czas do pierwszego bajtu zależny od źródła danych.

Generowanie statyczne buduje strony raz, przy wdrożeniu. Wynik jest plikiem, więc odpowiedź jest natychmiastowa i tania, kosztem świeżości.

Renderowanie tylko po stronie klienta wysyła pustą powłokę i buduje widok w przeglądarce. Sensowne dla paneli za logowaniem, bezsensowne dla treści, które mają się indeksować.

Regeneracja przyrostowa łączy dwa pierwsze: strona jest statyczna, ale odświeża się po zadanym czasie.

Code
TypeScript
export default defineNuxtConfig({
  routeRules: {
    '/': { prerender: true },
    '/blog/**': { isr: 3600 },
    '/panel/**': { ssr: false },
    '/api/**': { cors: true }
  }
})

Ta konfiguracja to jedna z najlepiej zaprojektowanych rzeczy w tym frameworku. Zamiast wybierać tryb dla całej aplikacji, opisujesz zachowanie per ścieżka w jednym miejscu, a strona marketingowa, blog i panel użytkownika mogą działać w trzech różnych trybach w ramach jednego wdrożenia.

Warto natomiast wiedzieć, że nie każdy tryb działa wszędzie tak samo. Regeneracja przyrostowa wymaga wsparcia po stronie hostingu, więc przed wyborem dostawcy sprawdź, czy Twój tryb jest tam obsługiwany, a nie tylko akceptowany w konfiguracji.

Pobieranie danych

To obszar, w którym framework najbardziej odbiega od zwykłego Vue, i zarazem najczęstsze źródło nieporozumień.

Code
VUE
<script setup lang="ts">
const { data: artykuly, status, error } = await useFetch('/api/articles', {
  query: { limit: 10 }
})
</script>

<template>
  <div v-if="status === 'pending'">Wczytywanie</div>
  <ArticleCard v-for="a in artykuly" :key="a.id" :article="a" />
</template>

Kluczowa własność jest taka, że zapytanie wykonuje się na serwerze przy pierwszym renderowaniu, a wynik trafia do przeglądarki razem z HTML. Klient nie powtarza tego zapytania po uruchomieniu, bo dane są już obecne w ładunku strony.

Stąd bierze się najczęstszy błąd: użycie zwykłego wywołania sieciowego w miejsce funkcji frameworka. Zadziała, ale wykona się dwa razy, raz na serwerze i raz w przeglądarce, co podwaja ruch do API i powoduje miganie treści.

Druga pułapka to klucze buforowania. Wynik jest buforowany pod kluczem wyliczonym z adresu i parametrów, więc dwa różne zapytania o tym samym adresie i różnych parametrach dynamicznych wymagają jawnie podanego klucza, inaczej jedno nadpisze drugie.

Trzecia to niepotrzebne blokowanie renderowania. Dane poboczne, na przykład lista powiązanych artykułów, nie muszą wstrzymywać pierwszej odpowiedzi. Opcja pobierania leniwego pozwala wysłać stronę od razu, a resztę dociągnąć po stronie klienta.

Warstwa serwerowa

Katalog serwerowy zamienia projekt frontendowy w aplikację pełnego stosu i to jest różnica względem samego Vue z routerem.

Code
TypeScript
export default defineEventHandler(async (event) => {
  const { limit } = getQuery(event)
  const artykuly = await db.article.findMany({ take: Number(limit) || 10 })
  return artykuly
})

Nazwa pliku wyznacza trasę i metodę, a treść funkcji wykonuje się po stronie serwera, więc klucze API i połączenie z bazą nie wychodzą do przeglądarki. Sprawdza się to dobrze z warstwą dostępu do danych opisaną w tekście o Prismie.

Silnik serwerowy jest niezależny od frameworka i buduje wynik dostosowany do środowiska docelowego: klasycznego serwera Node, funkcji bezserwerowej albo środowiska brzegowego. To znaczy, że wybór hostingu jest w dużej mierze odwracalny, bo zmienia się ustawienie kompilacji, a nie kod aplikacji.

Środowisko brzegowe ma jednak swoje ograniczenia i warto o nich wiedzieć przed wdrożeniem. Nie ma tam pełnego zestawu wbudowanych modułów Node, więc biblioteka sięgająca po system plików albo po moduły kryptograficzne w wersji natywnej po prostu się nie uruchomi. Ta sama bariera dotyczy sterowników baz danych otwierających gniazda TCP, dlatego na brzegu sięga się po usługi wystawione po zwykłym HTTP, na przykład Upstash. Cena jest taka, że każde polecenie niesie narzut żądania HTTP, więc pętla pięćdziesięciu operacji kosztuje tam wyraźnie więcej niż po TCP. Sprawdzenie tego na wczesnym etapie oszczędza przepisywania warstwy danych na dzień przed wdrożeniem.

Moduły

Ekosystem rozszerzeń jest tu mocniejszy niż w większości frameworków tej klasy i często decyduje o wyborze.

Moduł obrazów optymalizuje grafikę i generuje warianty rozmiarów. Moduł metadanych i mapy strony obsługuje warstwę pozycjonowania. Moduł zarządzania stanem wpina magazyn danych z automatycznymi importami. Istnieją też moduły do treści z plików Markdown, do wielojęzyczności i do warstwy uwierzytelniania.

Code
TypeScript
export default defineNuxtConfig({
  modules: [
    '@nuxt/image',
    '@pinia/nuxt',
    '@nuxtjs/i18n',
    '@nuxtjs/tailwindcss'
  ]
})

Wartość modułu polega na tym, że wpina się nie tylko w czasie działania aplikacji, ale też w proces budowania. Moduł potrafi dodać trasy, rozszerzyć konfigurację i wygenerować typy, więc integracja bywa głębsza niż zwykła instalacja biblioteki.

Odwrotna strona jest przewidywalna: każdy moduł to zależność, która musi nadążać za wersjami frameworka. Przy migracji między głównymi wydaniami to zwykle moduły, a nie kod aplikacji, decydują o tym, kiedy przejście jest możliwe. Przed rozpoczęciem migracji warto sprawdzić zgodność wszystkich używanych modułów, bo jeden nieaktualizowany dodatek potrafi zablokować całość.

Warto też ograniczyć ich liczbę do rzeczywiście potrzebnych. Moduł dodany po to, żeby zaoszczędzić dwadzieścia linii kodu, zostaje w projekcie na lata i pojawia się przy każdej aktualizacji.

Metadane i widoczność w wyszukiwarce

Renderowanie serwerowe samo w sobie nie wystarczy, żeby strona dobrze się indeksowała, a to nieporozumienie zdarza się często.

Tytuł i opis ustawia się per strona, a nie raz dla całej aplikacji. Framework daje do tego funkcję kompozycyjną, którą wywołujesz wewnątrz komponentu strony, więc opis może zależeć od pobranych danych.

Code
TypeScript
const { data: artykul } = await useFetch(`/api/articles/${slug}`)

useSeoMeta({
  title: () => artykul.value?.tytul,
  description: () => artykul.value?.opis,
  ogImage: () => artykul.value?.okladka
})

Zapis w postaci funkcji ma tu znaczenie, bo wartości zmieniają się razem z danymi. Podanie zwykłych wartości przy jeszcze niezaładowanym artykule zapisze puste znaczniki, które nigdy się nie zaktualizują.

Adres kanoniczny jest równie ważny i bywa pomijany. Ta sama treść dostępna pod kilkoma adresami, na przykład z parametrem śledzenia albo z ukośnikiem na końcu i bez, dzieli sygnały między warianty. Jawne wskazanie adresu głównego rozwiązuje to raz na zawsze.

Mapa strony i plik reguł dla robotów pochodzą z modułów i warto włączyć je od początku projektu, a nie po wdrożeniu. Mapa generowana automatycznie z tras jest zawsze aktualna, a ręcznie utrzymywana przestaje być aktualna po dwóch tygodniach.

Warstwy w większych projektach

Mechanizm warstw to rozwiązanie dla organizacji utrzymujących kilka aplikacji o wspólnym trzonie i najsłabiej znana część frameworka.

Warstwa to zwykły projekt, z którego inny projekt dziedziczy konfigurację, komponenty, funkcje kompozycyjne, a nawet strony. Firma z pięcioma serwisami może trzymać wspólny motyw, nagłówek i warstwę uwierzytelniania w jednym miejscu, a w poszczególnych aplikacjach nadpisywać tylko to, co się różni.

Code
TypeScript
export default defineNuxtConfig({
  extends: ['@firma/warstwa-podstawowa']
})

Nadpisywanie działa po nazwie pliku, więc komponent o tej samej nazwie w projekcie potomnym zastępuje ten z warstwy. To wygodne i wymaga ostrożności, bo przypadkowa zbieżność nazw daje podmianę, której nikt nie planował.

Warstwę publikuje się jako zwykły pakiet albo wskazuje repozytorium wprost, co pozwala zacząć bez budowania procesu wydawniczego. Przy kilku aplikacjach warto natomiast szybko przejść na wersjonowany pakiet, bo inaczej zmiana we wspólnym trzonie trafia do wszystkich projektów naraz, bez możliwości wdrożenia jej pojedynczo i sprawdzenia skutków.

Nuxt kontra alternatywy

RozwiązanieMocna stronaSłabośćKiedy wybrać
NuxtKomplet w zestawie, moduły, warstwa serwerowaSporo magii, dużo konwencjiProjekt na Vue wymagający pozycjonowania
Next.jsNajwiększy ekosystem, dojrzałe wdrożeniaPowiązanie z ReactemZespół pracujący w Reakcie
Vite plus VuePełna kontrola, żadnej magiiWszystko składasz samAplikacja za logowaniem, bez wymogu indeksowania
AstroMinimum JavaScriptu na wyjściuSłabszy przy dużej interaktywnościStrony z przewagą treści

Rozstrzygnięcie sprowadza się do dwóch pytań. Pierwsze: czy zespół pracuje w Vue, czy w Reakcie, bo to decyduje o pierwszych dwóch wierszach. Drugie: czy treść musi się indeksować, bo aplikacja w całości za logowaniem nie potrzebuje renderowania serwerowego i wtedy prostszy zestaw wygrywa.

Typowe błędy

Pierwszy to pozostawanie na Nuxt 3 po zakończeniu wsparcia. Brak poprawek bezpieczeństwa to nie jest kwestia wygody, tylko realne ryzyko, a migracja na czwórkę jest z założenia łagodna.

Drugi to zwykłe wywołania sieciowe zamiast funkcji frameworka. Skutkiem jest podwójne pobieranie danych i miganie treści przy uruchomieniu strony.

Trzeci to sięganie po obiekty przeglądarki podczas renderowania. Kod wykonuje się najpierw na serwerze, gdzie tych obiektów nie ma, więc dostęp do nich należy do funkcji uruchamianej po zamontowaniu komponentu.

Czwarty to trzymanie kluczy API w zmiennych dostępnych po stronie klienta. Konfiguracja rozróżnia wartości prywatne i publiczne, a wartość publiczna trafia do pakietu wysyłanego do przeglądarki.

Piąty to jeden tryb renderowania dla całej aplikacji. Panel za logowaniem nie musi się indeksować, a strona główna nie musi być generowana przy każdym żądaniu.

Szósty to dobór hostingu bez sprawdzenia wsparcia dla wybranego trybu. Regeneracja przyrostowa działa nie wszędzie, a odkrycie tego przy wdrożeniu jest bolesne.

FAQ

Czy Nuxt 3 jest jeszcze wspierany?

Nie, wsparcie zakończyło się 31 lipca 2026 roku. Projekty stojące na tej wersji nie dostają już poprawek bezpieczeństwa z głównego repozytorium. Dostępne są komercyjne przedłużenia wsparcia, natomiast właściwym kierunkiem jest migracja na Nuxt 4.

Czy warto czekać na Nuxt 5?

Nie. Mapa drogowa szacuje wydanie piątki na czwarty kwartał 2026 roku, bez podania konkretnej daty, a czwórka jest wersją stabilną i rozwijaną. Migracja z trójki na czwórkę jest łagodna, więc czekanie oznacza tylko dłuższy czas bez poprawek bezpieczeństwa.

Czym różni się Nuxt od Next.js?

Warstwą widoku i filozofią. Next.js stoi na Reakcie, Nuxt na Vue. Nuxt daje więcej w zestawie, w tym samodzielny silnik serwerowy i rozbudowany system modułów, kosztem większej liczby konwencji, których trzeba się nauczyć.

Czy potrzebuję serwera do wdrożenia?

Nie zawsze. Aplikację można zbudować jako komplet plików statycznych i wystawić na dowolnym hostingu plików. Serwer jest potrzebny dopiero wtedy, gdy używasz renderowania przy żądaniu, regeneracji przyrostowej albo punktów końcowych API.

Czy Nuxt nadaje się do dużych aplikacji?

Tak, o ile zespół zaakceptuje jego konwencje. Automatyczne importy i trasowanie z układu plików skracają kod, ale wymagają dyscypliny w organizacji katalogów. Przy rozbudowanych projektach pomaga mechanizm warstw, który pozwala dzielić konfigurację i komponenty między aplikacjami.

Dokumentacja stoi na stronie projektu, a plan rozwoju opisuje oficjalna mapa drogowa.