React w 2026, czyli co się zmieniło i co z tego wynika
React pozostaje najczęściej używaną biblioteką do budowania interfejsów, natomiast sposób pisania w nim kodu zmienił się przez ostatnie dwa lata bardziej niż przez poprzednie pięć. Dwie zmiany są tu istotne i obie zdejmują pracę, którą wcześniej wykonywało się ręcznie.
Pierwsza to kompilator, który sam dba o to, żeby komponenty nie przeliczały się bez potrzeby. Druga to komponenty wykonujące się na serwerze, które nie wysyłają swojego kodu do przeglądarki.
Ten tekst dotyczy tego, co z tych zmian wynika dla kodu, który piszesz dzisiaj, oraz tego, co warto poprawić w kodzie napisanym wcześniej.
Stan wersji
Zacznijmy od uporządkowania numeracji, bo materiały w sieci mieszają tu kilka różnych rzeczy naraz.
Wersją stabilną jest dziś dziewiętnastka w linii 19.2, a poprawki wychodzą równolegle także do linii 19.1 i 19.0. Prace nad kolejnym wydaniem idą w wersjach zapowiadających oznaczonych jako 19.3, więc materiały opisujące dwudziestkę jako wydaną wyprzedzają fakty.
Kompilator to osobny projekt z własną numeracją i osiągnął wersję pierwszą jesienią 2025 roku. Działa razem z dziewiętnastką i jest gotowy do użycia produkcyjnego, potwierdzonego wdrożeniami na dużą skalę.
Komponenty serwerowe wymagają frameworka, który je obsłuży, bo sama biblioteka daje mechanizm, a nie gotowe rozwiązanie. W praktyce oznacza to Next.js albo inny framework wspierający ten model.
Kompilator, czyli koniec ręcznej optymalizacji
To zmiana, która najbardziej wpływa na codzienne pisanie kodu, i warto zrozumieć, co dokładnie zdejmuje z Twoich barków.
Przez lata standardem było opakowywanie wartości i funkcji w mechanizmy zapamiętujące wynik, żeby komponent podrzędny nie przeliczał się przy każdym renderowaniu rodzica. Kod pełen takich opakowań był brzydki, łatwo było się w nim pomylić, a pominięcie jednej zależności dawało błąd trudny do wytropienia.
const przefiltrowane = useMemo(
() => produkty.filter((p) => p.kategoria === kategoria),
[produkty, kategoria]
)
const obsluzKliknięcie = useCallback(
(id: string) => dodajDoKoszyka(id),
[dodajDoKoszyka]
)Kompilator analizuje kod przy budowaniu i dokłada te optymalizacje sam, na podstawie tego, co faktycznie się dzieje. Powyższy fragment staje się zwykłym kodem, bez opakowań.
const przefiltrowane = produkty.filter((p) => p.kategoria === kategoria)
function obsluzKliknięcie(id: string) {
dodajDoKoszyka(id)
}Warto postawić dwa zastrzeżenia. Pierwsze: kompilator działa poprawnie wyłącznie na kodzie przestrzegającym zasad biblioteki, więc komponent modyfikujący właściwości przekazane z zewnątrz albo zmieniający zmienną poza swoim zakresem zostanie pominięty. Dodatkowe reguły w narzędziu sprawdzającym kod wskażą takie miejsca.
Drugie: istniejący kod z ręcznymi optymalizacjami nadal działa i nie wymaga usunięcia. Kompilator sobie z nim radzi, więc sprzątanie warto potraktować jako pracę wykonywaną przy okazji, a nie jako osobne zadanie.
Wdrożenie w istniejącym projekcie warto prowadzić stopniowo, włączając kompilator najpierw dla wybranych katalogów. Pozwala to zauważyć miejsca łamiące zasady biblioteki bez zatrzymywania całej pracy.
Komponenty serwerowe
Druga duża zmiana dotyczy tego, gdzie komponent się wykonuje, i jest głębsza, niż wygląda na pierwszy rzut oka.
Komponent serwerowy wykonuje się wyłącznie na serwerze i wysyła do przeglądarki opis wyniku, a nie swój kod. To znaczy, że biblioteka użyta wewnątrz takiego komponentu, na przykład narzędzie do przetwarzania dat albo do renderowania tekstu, nie trafia do paczki wysyłanej użytkownikowi.
async function ListaArtykulow() {
const artykuly = await baza.artykul.findMany({ take: 20 })
return (
<ul>
{artykuly.map((a) => (
<li key={a.id}>{a.tytul}</li>
))}
</ul>
)
}Zwróć uwagę na zapytanie do bazy wykonane wprost w komponencie. To jest możliwe, bo kod nigdy nie opuszcza serwera, więc dane dostępowe pozostają bezpieczne.
Podział na komponenty serwerowe i klienckie wymaga decyzji przy każdym elemencie interfejsu, i tu najczęściej pojawia się nieporozumienie. Zasada jest prosta: komponent, który potrzebuje stanu, obsługi zdarzeń albo dostępu do obiektów przeglądarki, musi być kliencki. Reszta może zostać na serwerze.
W praktyce oznacza to układ, w którym warstwa serwerowa pobiera dane i buduje strukturę, a wyspy interaktywności są klienckie i możliwie małe. Przycisk otwierający menu jest kliencki, cała strona wokół niego nie musi być.
Najczęstszy błąd polega na oznaczeniu jako klienckiego komponentu wysoko w drzewie, bo wtedy wszystko poniżej również staje się klienckie i cały zysk z tego podziału znika bez śladu.
Akcje i formularze
Trzecia zmiana, mniej głośna od dwóch poprzednich, dotyczy obsługi formularzy i operacji zmieniających dane. Warto ją znać, bo usuwa sporo ręcznego kodu.
Wcześniej obsługa wysłania formularza wymagała własnego stanu na wartości pól, własnego stanu na informację o trwającym wysyłaniu, własnej obsługi błędu i własnego blokowania przycisku. Cztery rzeczy do napisania przy każdym formularzu i cztery miejsca, w których można coś pominąć.
Obecnie funkcję obsługującą wysłanie przekazujesz wprost do formularza, a biblioteka udostępnia informację o tym, czy operacja trwa. Blokowanie przycisku i pokazanie stanu oczekiwania stają się jedną linią zamiast osobnego stanu.
function PrzyciskWyslij() {
const { pending } = useFormStatus()
return (
<button type="submit" disabled={pending}>
{pending ? 'Zapisywanie' : 'Zapisz'}
</button>
)
}Osobnym mechanizmem jest natychmiastowa aktualizacja widoku przed potwierdzeniem z serwera. Dodany komentarz pojawia się na liście od razu, a jeśli zapis się nie powiedzie, biblioteka sama przywraca poprzedni stan. To wzorzec, który wcześniej pisało się ręcznie i który regularnie zostawiał interfejs w stanie niespójnym po błędzie.
Warto uważać na jedną rzecz. Natychmiastowa aktualizacja ma sens tam, gdzie operacja niemal zawsze się udaje, na przykład przy polubieniu albo dodaniu komentarza. Przy płatności albo rezerwacji lepiej poczekać na odpowiedź, bo pokazanie sukcesu, który się nie wydarzył, jest gorsze niż sekunda oczekiwania.
Co poprawić w istniejącym kodzie
Trzy wzorce warte zmiany niezależnie od tego, czy włączasz kompilator, bo wszystkie trzy są źródłem błędów.
Pierwszy to efekt uboczny wyliczający wartość ze stanu i właściwości. Jeśli wartość da się policzyć podczas renderowania, nie potrzebuje ani efektu, ani osobnego stanu. Ten wzorzec powoduje podwójne renderowanie i moment, w którym wyświetlana wartość jest nieaktualna.
Drugi to efekt uboczny synchronizujący stan z właściwościami. Zwykle oznacza, że stan jest w złym miejscu, a rozwiązaniem jest przeniesienie go wyżej albo wyliczenie na miejscu.
Trzeci to przekazywanie właściwości przez wiele poziomów w dół. Wartość podawana przez pięć komponentów, z których żaden jej nie używa, wskazuje na brakującą warstwę kontekstu albo na zły podział komponentów.
Wszystkie trzy wyłapuje narzędzie opisane w tekście o React Doctorze, które skanuje projekt i zwraca listę takich miejsc z konkretnymi lokalizacjami.
Stan aplikacji, czyli decyzja rozstrzygana raz
Zarządzanie stanem to obszar, w którym React celowo nie narzuca rozwiązania, więc decyzja należy do Ciebie i warto podjąć ją świadomie, a nie z rozpędu.
Podstawowe rozróżnienie dotyczy tego, skąd pochodzą dane. Stan serwerowy to wszystko, co przychodzi z zaplecza: lista produktów, dane użytkownika, wyniki wyszukiwania. Stan kliencki to rzeczy istniejące wyłącznie w przeglądarce: otwarte menu, zawartość niewysłanego formularza, wybrana zakładka.
To rozróżnienie jest ważniejsze, niż wygląda, bo oba rodzaje mają zupełnie inne potrzeby. Stan serwerowy wymaga pobierania, buforowania, odświeżania i obsługi błędów sieci. Stan kliencki nie wymaga niczego z tej listy i psuje się, gdy próbuje się go trzymać w narzędziu zaprojektowanym do pierwszego.
Najczęstszy błąd w projektach polega na wrzuceniu wszystkiego do jednego magazynu globalnego. Powstaje wtedy warstwa, która ręcznie odtwarza buforowanie i odświeżanie, czyli rzeczy, które biblioteka do stanu serwerowego robi sama i lepiej.
Rozsądny domyślny układ wygląda tak. Dane z serwera obsługuje biblioteka do tego przeznaczona, opisana w tekście o TanStacku. Stan wspólny dla całej aplikacji, na przykład motyw albo koszyk, idzie do lekkiego magazynu takiego jak Zustand. Wszystko pozostałe zostaje lokalnie w komponencie i nigdzie nie wychodzi.
Ta ostatnia część bywa pomijana, a jest najważniejsza. Stan otwarcia okna dialogowego nie musi być globalny i wyniesienie go poza komponent nie daje nic poza dodatkową rzeczą do utrzymania.
React kontra alternatywy
| Rozwiązanie | Mocna strona | Słabość | Kiedy wybrać |
|---|---|---|---|
| React | Największy ekosystem, dostępność ludzi, kompilator | Wymaga frameworka do pełnego wykorzystania | Projekt komercyjny, zespół do rozbudowy |
| Vue | Łagodniejsza krzywa wejścia, mniej decyzji | Mniejszy rynek pracy | Zespół zaczynający od zera |
| SvelteKit | Najmniej kodu wynikowego, brak warstwy pośredniej | Mniejszy ekosystem bibliotek | Projekt wrażliwy na rozmiar paczki |
| Solid | Drobnoziarnista reaktywność bez memoizacji, składnia zbliżona | Znacznie mniejsza społeczność | Interfejs o dużej liczbie aktualizacji |
| Qwik | Brak hydratacji, kod pobierany dopiero przy kliknięciu | Wersja 2 wciąż w becie, wąski ekosystem | Strona treściowa z nielicznymi interakcjami |
Pierwsze kryterium przy projekcie komercyjnym jest prozaiczne i decydujące: dostępność ludzi. Biblioteka, w której zna się większość rynku, oznacza łatwiejsze zatrudnianie i większą szansę, że ktoś podejmie projekt po Tobie.
Drugie to ekosystem. Przy typowych potrzebach, od formularzy przez tabele po wykresy i edytory tekstu, gotowe rozwiązania są tu liczniejsze i dojrzalsze niż gdziekolwiek indziej.
Trzecie, przemawiające przeciw, to złożoność. Pełne wykorzystanie dzisiejszych możliwości wymaga frameworka, zrozumienia podziału na serwer i klienta oraz świadomości, co robi kompilator. Przy prostej stronie to jest nadmiar.
Od czego zacząć naukę
Kolejność nauki zmieniła się razem z biblioteką i część materiałów w sieci uczy dziś rzeczy, które przestały być potrzebne.
Zacznij od komponentów i przekazywania właściwości, bo to fundament, który się nie zmienił. Potem stan lokalny i obsługa zdarzeń, czyli wszystko, czego potrzeba do zbudowania działającego interfejsu.
Efekty uboczne przesuń dalej, niż robi to większość kursów. To najczęściej nadużywany mechanizm w całej bibliotece, a znaczna część zastosowań, których uczą starsze materiały, ma dziś lepsze rozwiązania: pobieranie danych obsługuje osobna biblioteka albo komponent serwerowy, a wyliczanie wartości dzieje się podczas renderowania.
Ręczne optymalizacje możesz na początku pominąć całkowicie. Kompilator robi to sam, więc czas poświęcony na naukę mechanizmów zapamiętujących wynik lepiej przeznaczyć na zrozumienie, kiedy komponent się przerenderowuje i dlaczego.
Podział na serwer i klienta zostaw na moment, w którym sięgniesz po framework. Bez niego ten mechanizm i tak nie zadziała, a pojęcia wprowadzane zbyt wcześnie mieszają obraz bardziej, niż pomagają.
Przy wyborze materiałów sprawdzaj datę. Kurs sprzed trzech lat uczy poprawnie podstaw i nieaktualnie wszystkiego powyżej, a rozpoznanie tej granicy bez doświadczenia jest trudne.
Typowe błędy
Pierwszy to efekt uboczny tam, gdzie wystarczy wyliczenie podczas renderowania. Najczęstszy wzorzec w kodzie napisanym w Reakcie i źródło podwójnych renderowań.
Drugi to oznaczanie jako klienckiego komponentu wysoko w drzewie. Wszystko poniżej staje się wtedy klienckie i zysk z komponentów serwerowych znika.
Trzeci to dodawanie ręcznych optymalizacji przy włączonym kompilatorze. Nie szkodzi, natomiast jest zbędną pracą, którą kompilator wykonuje sam.
Czwarty to łamanie zasad biblioteki, na przykład modyfikowanie przekazanych właściwości. Kompilator pomija takie komponenty, więc tracisz optymalizacje dokładnie tam, gdzie kod jest najbardziej podejrzany.
Piąty to trzymanie w stanie wartości, którą da się wyliczyć z innych danych. Każdy dodatkowy stan to kolejne miejsce, w którym coś może się rozjechać z resztą.
Szósty to poleganie na materiałach opisujących wersję, która nie została wydana. Wersją stabilną jest dziś dziewiętnastka w linii 19.2, a kompilator prowadzi własną, niezależną numerację.
FAQ
Która wersja Reacta jest aktualna?
Stabilna jest dziewiętnastka w linii 19.2, a poprawki wychodzą też do 19.1 i 19.0. Kolejne prace idą w wersjach zapowiadających 19.3, więc dwudziestki nie ma. Kompilator to osobny projekt z własną numeracją, który osiągnął wersję pierwszą jesienią 2025 roku.
Czy kompilator zastępuje ręczne optymalizacje?
Tak, w nowym kodzie nie musisz już opakowywać wartości ani funkcji w mechanizmy zapamiętujące wynik. Istniejący kod z takimi opakowaniami nadal działa i nie wymaga usunięcia, więc sprzątanie warto robić przy okazji innych zmian.
Czy komponenty serwerowe wymagają frameworka?
Tak. Sama biblioteka dostarcza mechanizm, a jego obsługa należy do frameworka. W praktyce oznacza to Next.js albo inne rozwiązanie wspierające ten model renderowania.
Kiedy komponent musi być kliencki?
Gdy potrzebuje stanu, obsługi zdarzeń albo dostępu do obiektów przeglądarki. Reszta może zostać na serwerze. Warto oznaczać jako kliencki możliwie najniższy element drzewa, bo wszystko poniżej dziedziczy to oznaczenie.
Czy warto zaczynać nowy projekt od Reacta?
Przy projekcie komercyjnym zwykle tak, głównie ze względu na dostępność ludzi i dojrzałość ekosystemu. Przy prostej stronie informacyjnej albo przy projekcie wrażliwym na rozmiar paczki warto rozważyć lżejsze rozwiązania.
Dokumentacja stoi na stronie projektu, a historię wydań opisuje lista wersji.