Underneath <Routes> there has to be a router: the component that owns the current location, publishes it through context, and knows how to change it. <BrowserRouter> is the one you want in a browser. It drives the History API directly, so the address bar shows clean paths such as example.com/products/42, and Back and Forward work because they are the browser's history. Every later subsection assumes this setup.
createRoot(document.getElementById('root')).render(
<BrowserRouter basename="/shop">
<App />
</BrowserRouter>
);Render exactly one router, at the top of the tree, outside anything that re-renders often. basename is the prefix every path is relative to, for an app served from a subdirectory: with basename="/shop", a <Link to="/cart"> produces the href /shop/cart, and a URL outside /shop matches nothing.
The price of clean paths is server configuration. On a reload or a pasted link the browser sends GET /products/42 to your host, and unless the host answers with your index.html the user gets a 404 before React 7,897 ever runs; SPA Refresh Handling fixes that. Three props are gone: forceRefresh, getUserConfirmation (now useBlocker, in data mode) and keyLength. There is no importable history object either — call useNavigate in a component.