Jak rozwiązać konflikt wersji React 18 i React Router 7
- Wprowadzenie do konfliktu React 18 i React Router 7
- Dlaczego dochodzi do konfliktu React 18 i React Router 7?
- Typowe objawy konfliktu w konsoli i aplikacji
- Krok po kroku – jak rozwiązać konflikt React 18 i React Router 7
- Aktualizacja kluczowych pakietów w projekcie
- Przejście na nowe API createRoot w React 18
- Poprawne użycie komponentów React Router 6/7
- Kontrola peerDependencies za pomocą resolutions i overrides
- Rola React.StrictMode w React 18 i diagnostyka problemów
- Dobre praktyki przy aktualizacjach Reacta i React Routera
- Podsumowanie i dalsze kroki
Wprowadzenie do konfliktu React 18 i React Router 7
Witaj w świecie front-endu, gdzie aktualizacje technologii to chleb powszedni, ale czasem też… prawdziwa droga przez mękę. Jeśli czytasz ten wpis, prawdopodobnie stajesz przed wyzwaniem, które spędza sen z powiek wielu deweloperom – próbujesz połączyć siły Reacta 18 z React Routerem 7 i napotykasz na opór. To absolutnie normalne i da się to opanować.
W tym artykule zobaczysz, jak rozwiązać konflikt wersji React 18 i React Router 7 w jednym projekcie, krok po kroku, bez zbędnego stresu. Przejdziemy przez najczęstsze przyczyny problemów, objawy w konsoli oraz konkretne zmiany w kodzie, które trzeba wprowadzić. Dzięki temu Twoja aplikacja odzyska stabilność.
Masz świetny pomysł na aplikację, piszesz kod w React, dodajesz nawigację, a nagle… wszystko się sypie. Konsola krzyczy błędami, komponenty przestają działać, a Ty zastanawiasz się, co poszło nie tak. W wielu przypadkach przyczyną jest niekompatybilność wersji lub niewłaściwe podejście do aktualizacji pakietów.
React 18 wprowadził ważne zmiany, zwłaszcza w sposobie renderowania aplikacji, co bezpośrednio wpływa na inne biblioteki, takie jak React Router. Z kolei React Router w wersji 6, a co za tym idzie w najnowszej 7, również przeszedł spore zmiany – uproszczono API, ale jednocześnie wymuszono dostosowanie kodu. Stąd właśnie biorą się konflikty.
React Router 7 opiera się na fundamentach React Routera 6, ale zakłada korzystanie z nowego sposobu renderowania w React 18. Jeśli w projekcie masz stare wzorce, takie jak ReactDOM.render() albo przestarzałe definicje tras, to bardzo łatwo o błędy. W kolejnych sekcjach przejdziemy po kolei przez wszystko, co musisz poprawić, aby projekt działał poprawnie.

Dlaczego dochodzi do konfliktu React 18 i React Router 7?
Głównym winowajcą konfliktu jest zmiana w API renderowania w React 18. Wcześniejsze wersje Reacta wykorzystywały funkcję ReactDOM.render(), podczas gdy React 18 wprowadził nowe API – ReactDOM.createRoot(). Ten nowy mechanizm umożliwia korzystanie między innymi z Concurrent Features, ale wymaga pełnej kompatybilności całej aplikacji.
Jeśli w aplikacji korzystasz jednocześnie z nowego Reacta 18 i starych wzorców renderowania, to biblioteki takie jak React Router 7 mogą mieć problem z poprawnym zainicjowaniem kontekstu. W efekcie pojawiają się komunikaty o błędach, a trasy przestają działać zgodnie z oczekiwaniami. Brak spójności między wersjami jest tu kluczowym źródłem problemów.
Drugim istotnym aspektem są tzw. peerDependencies. To zależności, których pakiet oczekuje w Twoim projekcie w określonej wersji. Jeśli w pliku package.json masz starszą wersję Reacta, a React Router 7 wymaga Reacta 18, npm lub yarn zgłoszą konflikt. To trochę jak próba połączenia nowoczesnej płyty głównej z bardzo starym procesorem – teoretycznie mogą pasować, ale w praktyce po prostu nie zadziałają.
Wyobraź sobie projekt, w którym jedna biblioteka wymaga Reacta 17, inna 18, a Ty na siłę próbujesz je połączyć. Po instalacji pojawiają się ostrzeżenia o niespełnionych peerDependencies, które często są ignorowane. Właśnie wtedy rodzą się trudne do zdiagnozowania błędy, szczególnie w obszarze routingu i renderowania komponentów.
Dodatkowo, część bibliotek może mieć głęboko zagnieżdżone zależności, które nadal odwołują się do starszych wersji Reacta. Nawet jeśli na poziomie głównych zależności masz React 18, moduły zależne mogą próbować używać innej wersji. Dlatego tak ważne jest, aby pilnować spójności i w razie potrzeby wymuszać właściwe wersje Reacta i React DOM w całym drzewie zależności.
Typowe objawy konfliktu w konsoli i aplikacji
Zanim przejdziesz do rozwiązywania problemu, warto przyjrzeć się typowym objawom konfliktu między React 18 a React Router 7. Pierwszym, najbardziej oczywistym sygnałem jest komunikat w konsoli w stylu:
ReactDOM.render is no longer supported in React 18. Use createRoot instead.
Taki komunikat wprost informuje, że aplikacja nadal korzysta ze starego API renderowania. Nawet jeśli reszta kodu wygląda poprawnie, dopóki nie przejdziesz na ReactDOM.createRoot(), kompatybilność z Reactem 18 będzie ograniczona. To wpływa także na działanie routera, który polega na poprawnym uruchomieniu całej aplikacji.
Kolejnym częstym błędem są komunikaty związane z kontekstem React Routera, na przykład:
Error: Could not find "router" context. Ensure the component is wrapped in a <BrowserRouter>.
Ten błąd sugeruje, że komponenty wykorzystujące hooki routera lub nawigację nie znajdują się wewnątrz poprawnie zainicjowanego routera. Czasem to wina samej struktury komponentów, a czasem skutki konfliktu wersji.
Inne symptomy, które mogą wskazywać na niekompatybilność:
- Aplikacja się uruchamia, ale nawigacja nie reaguje lub zachowuje się nieprzewidywalnie.
- Po przejściu na konkretną trasę komponenty nie renderują się w ogóle.
- W konsoli pojawiają się ostrzeżenia o „nieobsługiwanych” lub „przestarzałych” funkcjach związanych z React Routerem.
Często zdarza się też, że aplikacja działa poprawnie w jednej części, ale po przejściu na inną trasę pojawiają się błędy lub ostrzeżenia. Może to oznaczać, że część tras została już zaktualizowana do nowych wzorców z React Router 6/7, a reszta pozostała w starym stylu. Taka mieszanka jest szczególnie niebezpieczna i wymaga uporządkowania.
Jeśli którykolwiek z powyższych objawów brzmi znajomo, oznacza to, że prawdopodobnie masz do czynienia z konfliktem wersji React 18 i React Router 7. W następnej sekcji znajdziesz konkretne kroki, które pomogą uporządkować zależności, zaktualizować kod i przywrócić stabilne działanie aplikacji.
Krok po kroku – jak rozwiązać konflikt React 18 i React Router 7
Rozwiązywanie konfliktu między React 18 a React Router 7 można porównać do składania komputera z części kupionych w różnych sklepach. Musisz dopilnować, aby wszystkie elementy – płyta główna, procesor i pamięć RAM – idealnie do siebie pasowały. W świecie front-endu tymi elementami są wersje Reacta, React DOM i React Router DOM.
Poniżej znajdziesz konkretny plan działania, który pozwoli Ci ustabilizować projekt:
- Zaktualizuj wszystkie kluczowe pakiety do właściwych wersji.
- Przejdź na nowe API renderowania
createRootw React 18. - Upewnij się, że używasz odpowiednich komponentów i wzorców z React Router 6/7.
- Skorzystaj z
resolutionsluboverrides, aby wymusić spójne wersje zależności. - Sprawdź zachowanie aplikacji z i bez
React.StrictMode.
Ważne jest, aby wykonywać te kroki po kolei i za każdym razem upewniać się, że projekt się kompiluje oraz że nie pojawiają się nowe błędy w konsoli. Dzięki temu łatwiej będzie ustalić, w którym miejscu coś poszło nie tak. W kolejnych podsekcjach każdy krok rozpisany jest dokładniej, wraz z przykładami gotowych fragmentów kodu.
Pamiętaj też, że po dużych zmianach w infrastrukturze projektu, takich jak aktualizacja głównych zależności, warto wykonać „clean install”. Usunięcie katalogu node_modules oraz plików blokady, a następnie ponowna instalacja pakietów, często rozwiązuje problemy wynikające z poprzednich, nieudanych aktualizacji.
Aktualizacja kluczowych pakietów w projekcie
Pierwszym i absolutnie podstawowym krokiem jest aktualizacja kluczowych pakietów: React, React DOM oraz React Router DOM. Upewnij się, że korzystasz z najnowszych stabilnych wersji, które są ze sobą kompatybilne. Dzięki temu zminimalizujesz ryzyko konfliktów wynikających z przestarzałych zależności.
Otwórz terminal w katalogu swojego projektu i wykonaj poniższe polecenia, w zależności od używanego menedżera pakietów:
# Dla npm
npm install react@^18.2.0 react-dom@^18.2.0 react-router-dom@^6.22.0
# Dla yarn
yarn add react@^18.2.0 react-dom@^18.2.0 react-router-dom@^6.22.0
Po zaktualizowaniu zależności warto wykonać pełne odświeżenie instalacji. Usuń katalog node_modules oraz odpowiedni plik blokady (package-lock.json lub yarn.lock), a następnie zainstaluj wszystko od nowa. Pozwoli to upewnić się, że menedżer pakietów nie trzyma się starych, niekompatybilnych wersji.
# Dla npm
rm -rf node_modules && rm package-lock.json && npm install
# Dla yarn
rm -rf node_modules && rm yarn.lock && yarn install
Takie „czyszczenie” jest szczególnie ważne, gdy wcześniej próbowałeś różnych kombinacji wersji pakietów. W przeciwnym razie część zależności może pozostać w niepożądanej wersji, co prowadzi do trudnych do zdiagnozowania błędów. Po tej operacji masz pewność, że projekt bazuje na spójnym zestawie wersji.
Po zakończeniu instalacji warto też uruchomić aplikację lokalnie i sprawdzić, czy już na tym etapie nie pojawiają się błędy kompilacji. Jeśli wszystko się buduje, ale w konsoli widzisz ostrzeżenia związane z React 18 lub React Routerem 7, przejdź do kolejnego kroku – zmiany sposobu renderowania aplikacji na nowe API createRoot.

Przejście na nowe API createRoot w React 18
Kluczową zmianą w React 18 jest wprowadzenie nowego API renderowania – ReactDOM.createRoot(). Stare wywołanie ReactDOM.render() nie jest już wspierane w React 18, dlatego właśnie widzisz komunikaty w stylu „ReactDOM.render is no longer supported in React 18”. Musisz zaktualizować plik wejściowy aplikacji, najczęściej src/index.js lub src/main.jsx.
Poniżej przykład starego sposobu renderowania (przed React 18):
// src/index.js (stary sposób)
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
W React 18 należy przejść na nowy sposób renderowania z użyciem createRoot. Zwróć szczególną uwagę na zmianę importu z react-dom na react-dom/client. Dzięki temu React Router 7 będzie działał poprawnie z nowym mechanizmem renderowania i z funkcjami takimi jak Concurrent Features.
// src/index.js (nowy sposób)
import React from 'react';
import ReactDOM from 'react-dom/client'; // Zwróć uwagę na '/client'
import App from './App';
// Pobieramy element DOM, do którego będziemy renderować aplikację
const container = document.getElementById('root');
// Tworzymy "root" za pomocą nowego API createRoot
const root = ReactDOM.createRoot(container);
// Renderujemy naszą aplikację
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
Bez tej zmiany React Router 7 może mieć problemy z prawidłowym zainicjowaniem swojego kontekstu, co objawia się błędami przy korzystaniu z nawigacji lub hooków routera. Dlatego upewnij się, że w Twoim projekcie nie ma już żadnych odniesień do ReactDOM.render().
Po wprowadzeniu nowego API warto ponownie uruchomić aplikację i sprawdzić, czy komunikaty o przestarzałym renderowaniu zniknęły. Jeśli nadal widzisz błędy związane z routerem lub trasami, przejdź do kolejnej sekcji, w której omówimy poprawne użycie komponentów React Router 6/7.
Poprawne użycie komponentów React Router 6/7
React Router 6, a tym samym React Router 7, wprowadził istotne zmiany w sposobie definiowania tras. Najważniejsze różnice to zastąpienie komponentu Switch komponentem Routes oraz zmiana sposobu przekazywania komponentu do Route – z component na element. Jeśli w projekcie pozostały stare wzorce, musisz je zaktualizować.
Poniżej przykład starego stylu (React Router < 6):
// App.js (stary sposób)
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import Home from './components/Home';
import About from './components/About';
function App() {
return (
<Router>
<Switch>
<Route path="/" component={Home} exact />
<Route path="/about" component={About} />
</Switch>
</Router>
);
}
export default App;
W React Router 6/7 ten kod powinien zostać przepisany na nowy, uproszczony styl. Zamiast Switch używasz Routes, a w Route przekazujesz JSX w propsie element. Dzięki temu definicje tras są bardziej jednoznaczne i lepiej integrują się z mechanizmem renderowania Reacta 18.
// App.js (nowy sposób)
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Home from './components/Home';
import About from './components/About';
import Contact from './components/Contact';
function App() {
return (
<Router>
<Routes> {/* Zamiast Switch, używamy Routes */}
<Route path="/" element={<Home />} /> {/* Zamiast component, używamy element */}
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
{/* Możesz też zdefiniować trasę dla 404 */}
<Route path="*" element={<div>404 - Nie znaleziono strony</div>} />
</Routes>
</Router>
);
}
export default App;
Pamiętaj, że komponent BrowserRouter (lub HashRouter, jeśli go używasz) powinien otaczać całą Twoją aplikację lub przynajmniej tę część, w której korzystasz z tras. Dzięki temu wszystkie komponenty wewnątrz mają dostęp do kontekstu routera, a hooki takie jak useNavigate czy useParams działają poprawnie.
Jeśli nadal widzisz błąd „Could not find "router" context”, upewnij się, że nie renderujesz komponentów zależnych od routera poza drzewem <Router>. Często zdarza się, że jakiś komponent został przypadkowo przeniesiony wyżej w strukturze i utracił dostęp do kontekstu. Po uporządkowaniu struktury oraz definicji tras większość problemów z React Routerem 7 powinna zniknąć.
Kontrola peerDependencies za pomocą resolutions i overrides
Nawet po zaktualizowaniu głównych zależności może się okazać, że inne biblioteki w projekcie mają głęboko zagnieżdżone zależności wskazujące na starsze wersje Reacta. Takie sytuacje prowadzą do subtelnych błędów i ostrzeżeń, które trudno powiązać z konkretnym pakietem. W takich przypadkach możesz skorzystać z mechanizmów resolutions (Yarn) lub overrides (npm).
Jeśli używasz Yarn, dodaj sekcję resolutions do pliku package.json, aby wymusić konkretne wersje Reacta i React DOM w całym drzewie zależności:
// package.json (dla Yarn)
{
"name": "moj-projekt",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.22.0"
},
"resolutions": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
}
Dla użytkowników npm (w wersjach >= 8.3.0) odpowiednikiem jest sekcja overrides. Działa ona podobnie – pozwala wymusić określone wersje wybranych pakietów, niezależnie od wymagań zgłaszanych przez inne biblioteki w projekcie:
// package.json (dla npm)
{
"name": "moj-projekt",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.22.0"
},
"overrides": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
}
Po dodaniu resolutions lub overrides koniecznie uruchom ponownie yarn install lub npm install. Dzięki temu menedżer pakietów zastosuje nowe reguły i ujednolici wersje Reacta oraz React DOM we wszystkich zależnościach. To szczególnie ważne, gdy w projekcie używasz wielu dodatkowych bibliotek, które mogą mieć różne wymagania.
Pamiętaj jednak, że takie wymuszanie wersji to rozwiązanie awaryjne. Zawsze lepiej jest zaktualizować wszystkie zależności do oficjalnie kompatybilnych wersji, jeśli to tylko możliwe. resolutions i overrides przydają się głównie wtedy, gdy jakaś biblioteka nie nadąża z aktualizacjami, a Ty potrzebujesz stabilnego środowiska opartego na React 18 i React Router 7.
Rola React.StrictMode w React 18 i diagnostyka problemów
React 18 w połączeniu z React.StrictMode jest bardziej rygorystyczny niż wcześniejsze wersje. Tryb ten ujawnia potencjalne problemy w aplikacji, które wcześniej mogły być ignorowane. W praktyce oznacza to, że po aktualizacji do React 18 możesz zauważyć nowe ostrzeżenia lub nieoczekiwane zachowania, szczególnie w fazie rozwoju.
Jeśli po przejściu na React 18 oraz zaktualizowaniu React Routera 7 napotykasz trudne do wyjaśnienia błędy, jednym z kroków diagnostycznych może być tymczasowe usunięcie <React.StrictMode> z pliku index.js. W ten sposób sprawdzisz, czy problemy wynikają z dodatkowych kontroli wprowadzonych przez ten tryb.
Jeżeli po wyłączeniu StrictMode problem znika, oznacza to, że tryb ten ujawnił błąd w Twoim kodzie lub w jednej z używanych bibliotek. Nie traktuj tego jako zachęty do stałego wyłączania StrictMode, ale jako wskazówkę, że warto przyjrzeć się logice komponentów, efektom ubocznym i używanym hookom. To właśnie one najczęściej zachowują się inaczej w tym trybie.
Warto pamiętać, że React.StrictMode działa tylko w środowisku deweloperskim. W produkcji nie wprowadza dodatkowych wywołań ani ostrzeżeń, więc nie wpływa na wydajność końcowej aplikacji. Jego rolą jest pomoc w znalezieniu potencjalnych błędów na wczesnym etapie prac, zanim trafią do użytkowników końcowych.
Podczas diagnozowania problemów z React 18 i React Router 7 możesz więc stosować następującą strategię:
- Tymczasowo wyłącz
StrictModei sprawdź, czy problem ustępuje. - Zbadaj komunikaty w konsoli i ostrzeżenia generowane w trybie ścisłym.
- Popraw potencjalne błędy w kodzie lub zaktualizuj problematyczne biblioteki.
- Po naprawieniu przyczyn przywróć
React.StrictMode, aby nadal korzystać z jego zalet.
Dobre praktyki przy aktualizacjach Reacta i React Routera
Przechodzenie na nowe wersje technologii front-endowych, takich jak React 18 i React Router 7, to wyzwanie, ale również świetna okazja do uporządkowania projektu. Stosowanie kilku prostych zasad pozwoli w przyszłości unikać wielu konfliktów wersji i problemów z kompatybilnością.
Po pierwsze, warto regularnie czytać dokumentację i release notes nowych wydań. Gdy pojawia się duża aktualizacja, dobrze jest poświęcić chwilę na zapoznanie się z listą zmian i zaleceniami dotyczącymi migracji. Oficjalne materiały często zawierają przykłady kodu oraz opisy typowych pułapek, których możesz uniknąć.
Po drugie, staraj się aktualizować zależności regularnie, ale z umiarem. Zamiast latami trzymać się Reacta 15 i nagle przeskakiwać do Reacta 18, lepiej wykonywać mniejsze aktualizacje częściej. Dzięki temu różnice między wersjami są mniejsze, a proces migracji mniej bolesny. Podobną strategię warto stosować wobec React Routera i innych kluczowych bibliotek.
Kolejną dobrą praktyką jest korzystanie z narzędzi takich jak npm outdated czy yarn upgrade-interactive. Pomagają one w prosty sposób sprawdzić, które pakiety są przestarzałe, oraz zarządzać ich aktualizacją. Pozwala to zachować większą kontrolę nad wersjami i unikać sytuacji, w których jedna biblioteka pozostaje daleko w tyle.
Ważne jest też, aby duże aktualizacje wykonywać w osobnym branchu. Nigdy nie aktualizuj kluczowych zależności bezpośrednio na głównej gałęzi projektu. Zamiast tego utwórz nową gałąź, zaktualizuj pakiety, dostosuj kod i dopiero po dokładnym przetestowaniu zmerguj zmiany. Taki proces znacznie zmniejsza ryzyko wprowadzenia krytycznych błędów na produkcję.
Na koniec pamiętaj o „clean install” w sytuacji, gdy pojawiają się trudne do wyjaśnienia problemy z zależnościami. Usunięcie node_modules oraz plików blokady (package-lock.json lub yarn.lock) i ponowna instalacja pakietów często rozwiązuje zagadkowe konflikty wersji. To prosta, ale bardzo skuteczna technika ratunkowa.
Podsumowanie i dalsze kroki
Konflikt między Reactem 18 a React Routerem 7 może na początku wydawać się skomplikowany, ale po rozbiciu go na mniejsze kroki staje się w pełni do opanowania. Kluczem jest zrozumienie, że głównym źródłem problemów są zmiany w API renderowania, peerDependencies oraz przejście na nowe wzorce definiowania tras w React Router 6/7.
Przypomnijmy najważniejsze działania: zaktualizuj React, React DOM i React Router DOM do kompatybilnych wersji, przejdź na ReactDOM.createRoot(), popraw definicje tras z wykorzystaniem Routes i element, a w razie potrzeby skorzystaj z resolutions lub overrides. Nie zapomnij też o roli React.StrictMode w diagnozowaniu potencjalnych problemów w Twojej aplikacji.
Świat programowania jest dynamiczny – aktualizacje, nowe wersje i okazjonalne niekompatybilności są naturalną częścią tej podróży. Dzięki cierpliwości, umiejętności diagnozowania błędów i konsekwentnemu stosowaniu dobrych praktyk możesz jednak sprawić, że przejścia między kolejnymi wersjami technologii będą znacznie spokojniejsze.
Teraz, gdy Twój router działa płynnie z Reactem 18, możesz skupić się na tym, co najważniejsze – tworzeniu świetnych aplikacji. Ucz się, eksperymentuj z nowymi funkcjami i rozwijaj swoje umiejętności, budując kolejne projekty w oparciu o nowoczesny stos technologiczny.