i want to use that Hashrouter, but when i try, i got this error:
<Router basename="/admin"> is not able to match the URL "/" because it does not start with the basename, so the <Router> won't render anything.
i put "Homepage": "./admin" in packedjson
but when i use BrowserRouter, its render normaly, can anyone explain why, please?
The code i'm using to try to understand router v6:
import "./styles.css";
import {
BrowserRouter,
Routes,
Route,
Navigate,
Outlet,
Link,
HashRouter,
} from "react-router-dom";
const ProtectedRoutes = () => <Outlet />;
const Configuration = () => <h1>Configuration</h1>;
const SummaryPage = () => <h1>SummaryPage</h1>;
const Dashboard = () => <h1>Dashboard</h1>;
const Appointments = () => <h1>Appointments</h1>;
const NotFound = () => <h1>NotFound</h1>;
export default function App() {
return (
<HashRouter basename="/admin">
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Link to="/dashboard" className="link">
Home
</Link>
</div>
<Routes>
<Route path="/configuration/configure" element={<Configuration />} />
<Route path="/configuration" element={<SummaryPage />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/appointments" element={<Appointments />} />
<Route path="/" element={<Navigate replace to="/configuration" />} />
<Route path="*" element={<NotFound />} />
</Routes>
</HashRouter>
);
}
There mostly seems to be a misunderstanding with how the basename prop is applied in the router, specifically the HashRouter. With the HashRouter the basename prop is a value that is applied against the paths the app is handling, not against the domain path where the app is served/running.
Example:
<HashRouter basename="/admin">
<Link to="/dashboard" className="link"> // renders <a href="#/admin/dashboard">
Dashboard
</Link>
...
<Routes>
<Route path="/configuration">
<Route path="configure" element={<Configuration />} />
<Route index element={<SummaryPage />} />
</Route>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/appointments" element={<Appointments />} />
<Route path="/" element={<Navigate replace to="/configuration" />} />
<Route path="*" element={<NotFound />} />
</Routes>
</HashRouter>
In other words, the basename prop value is applied to the URL hash and not the URL path, i.e. it's applied to everything after the hash.
mysite.com/someSubdomain/#/admin /something / ... more nested paths
|--domain-|--subdomain--|#|--------------hash-----------------|
| | | |basename| app path | ... app subpaths
If you are wanting the "/admin" to show up prior to the hash, then this is part of where the entire app is deployed to and served up from. In this case the app needs to be deployed to mysite.com in a "/admin" subdirectory. You also won't need to specify the basename="/admin" if you don't want an additional "/admin" to show up in the app's routing.
mysite.com/admin/#/something
...
<HashRouter>
<Link to="/dashboard" className="link"> // renders <a href="#/dashboard">
Dashboard
</Link>
...
<Routes>
<Route path="/configuration">
<Route path="configure" element={<Configuration />} />
<Route index element={<SummaryPage />} />
</Route>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/appointments" element={<Appointments />} />
<Route path="/" element={<Navigate replace to="/configuration" />} />
<Route path="*" element={<NotFound />} />
</Routes>
</HashRouter>
update: Not a solution =[ basename not works in Routes, and hashrouter not working with basename
Some solution here:
https://github.com/remix-run/react-router/issues/7128#issuecomment-582591472
but i don't know if it's the best one.
// url where new router is created: https://my-site/who/users
const RootModule = () => {
return (
<main>
<BrowserRouter>
<Routes basename="who/users">
<nav>
<Link to="">Home</Link>
<Link to="who/users/about">About</Link>
<Link to="who/users/users">Users</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="who/users/about" element={<About />} />
<Route path="who/users/users" element={<Users />} />
</Routes>
</Routes>
</BrowserRouter>
</main>
);
};
and here working
SANDBOX
Related
<BrowserRouter>
<Routes>
<Route path='/' element={<Home />} />
<Route path='users' element={<Users />} >
<Route path='user1' element={<User1 />} />
<Route path='user2' element={<User2 />} />
<Route path='user3' element={<User3 />} />
</Route>
</Routes>
</BrowserRouter>
____________________________________________
import React from 'react'
import { NavLink, Outlet } from 'react-router-dom'
function Users() {
return (
<div>
<NavLink to='user1'>User1</NavLink>
<NavLink to='user2'>User2</NavLink>
<NavLink to='user3'>User2</NavLink>
<Outlet/>
</div>
)
}
export default Users;
I am using react router dom#6. While hitting users url wanted to make user1 as a default component to show on screen along with users and it also redirects url to users/user1
You can use the <Navigate> element.
<Route path='users' element={<Users />} >
<Route path='' element={<Navigate to="users/user1" replace/>} />
<Route path='user1' element={<User1 />} />
<Route path='user2' element={<User2 />} />
<Route path='user3' element={<User3 />} />
</Route>
I would like all routes other than those registered below to direct me to the main page.
example:
testexample.com/contact
-> Returns me to the contact page
testexample.com/sdlskdsd
-> Route does not exist, return to main page
import {
BrowserRouter,
Routes,
Route
} from 'react-router-dom'
import Footerpage from './components/footerpage/Footerpage';
import Navegation from './components/navegation/Navegation';
import Homepage from './pages/Homepage';
import Contact from './pages/contact/Contact';
import Bookpage from './pages/bookpage/Bookpage';
import Success from './pages/contact/status/Success';
import Error from './pages/contact/status/Error';
function App() {
return (
<BrowserRouter>
<Navegation />
<Routes>
<Route path='/' exact element={<Homepage />} />
<Route path='/contact' exact element={<Contact />} />
<Route path='/contactsuccess' exact element={<Success />} />
<Route path='/contacterr' exact element={<Error />} />
<Route path='/bookpage' exact element={<Bookpage />} />
</Routes>
<Footerpage />
</BrowserRouter>
);
}
export default App;
Redirect unknown/unhandled routes to the "/" path with the Navigate component.
<Route path="*" element={<Navigate to="/" replace />} />
Also, in RRDv6 all routes are now always exactly matched, there is no longer any exact prop, so these should all be removed.
Full routes example:
import {
BrowserRouter,
Routes,
Route,
Navigate
} from 'react-router-dom';
...
<Routes>
<Route path='/' element={<Homepage />} />
<Route path='/contact' element={<Contact />} />
<Route path='/contactsuccess' element={<Success />} />
<Route path='/contacterr' element={<Error />} />
<Route path='/bookpage' element={<Bookpage />} />
<Route path="*" element={<Navigate to="/" replace />} /> // <-- redirect
</Routes>
Add a default route
import {
BrowserRouter,
Routes,
Route
} from 'react-router-dom'
import Footerpage from './components/footerpage/Footerpage';
import Navegation from './components/navegation/Navegation';
import Homepage from './pages/Homepage';
import Contact from './pages/contact/Contact';
import Bookpage from './pages/bookpage/Bookpage';
import Success from './pages/contact/status/Success';
import Error from './pages/contact/status/Error';
function App() {
return (
<BrowserRouter>
<Navegation />
<Routes>
<Route path='/' exact element={<Homepage />} />
<Route path='/contact' exact element={<Contact />} />
<Route path='/contactsuccess' exact element={<Success />} />
<Route path='/contacterr' exact element={<Error />} />
<Route path='/bookpage' exact element={<Bookpage />} />
<Route element={<Homepage />} />
</Routes>
<Footerpage />
</BrowserRouter>
);
}
export default App;
Another way of doing it
<Routes>
<Route path='/' exact element={<Homepage />} />
<Route path='/contact' exact element={<Contact />} />
<Route path='/contactsuccess' exact element={<Success />} />
<Route path='/contacterr' exact element={<Error />} />
<Route path='/bookpage' exact element={<Bookpage />} />
<Route path='/*' element={<Homepage />} />
</Routes>
I'm trying to upgrade to react-router-dom v6 :
v5
In version 5 it works like a charm:
App.js
import Sidebar from "./components/sidebar/Sidebar";
import Topbar from "./components/topbar/Topbar";
import "./app.css";
import Home from "./pages/home/Home";
import {
BrowserRouter as Router,
Switch,
Route,
} from "react-router-dom";
import UserList from "./pages/userList/UserList";
import User from "./pages/user/User";
import NewUser from "./pages/newUser/NewUser";
import ProductList from "./pages/productList/ProductList";
import Product from "./pages/product/Product";
import NewProduct from "./pages/newProduct/NewProduct";
import Login from "./pages/login/Login";
function App() {
const admin = JSON.parse(JSON.parse(localStorage.getItem("persist:root"))?.user || "{}")?.currentUser?.isAdmin ;
return (
<Router>
<Switch>
<Route path="/login">
<Login />
</Route>
{admin && (
<>
<Topbar />
<div className="container">
<Sidebar />
<Route exact path="/">
<Home />
</Route>
<Route path="/users">
<UserList />
</Route>
<Route path="/user/:userId">
<User />
</Route>
<Route path="/newUser">
<NewUser />
</Route>
<Route path="/products">
<ProductList />
</Route>
<Route path="/product/:productId">
<Product />
</Route>
<Route path="/newproduct">
<NewProduct />
</Route>
</div>
</>
)}
</Switch>
</Router>
);
}
export default App;
v6
When upgraded to v6 I changed my code to be like this:
<Routes>
<Route path="/login" element={<Login />} />
{admin && (
<>
<Route path="/" element={<Topbar />}/>
<Route path="/" element={
<>
<div className="container">
<Route index element={<Sidebar/>}/>
<Route index element={<Home/>}/>
<Route path="/users" element={<UserList />} />
<Route path="/user/:userId" element={<User />} />
<Route path="/newUser" element={<NewUser />} />
<Route path="/productList" element={<ProductList />} />
<Route path="/product/:productId" element={<Product />} />
<Route path="/newProduct" element={<NewProduct />} />
</div>
</>
}
</>
)}
</Routes>
This is my css file for App.js
Notice: the Topbar component should be outside the div, and react router didn't recognize the components inside the as routes even without div, that means each component should have a unique path, I tried also two components with the same path like this:
<Route path="/" element = {<><Home/><Sidebar/><>}, but the css is not taking effect
.container {
display: flex;
margin-top: 50px;
}
It doesn't work. I tried different code and I searched a lot without finding any solution.
Part of the issue is that you are rendering multiple identical paths, i.e. two "/" paths and two nested index paths. This won't work.
In react-router-dom v6 you can create what are called layout components. The layout components can render your headers and footers, sidebars, drawers, and general content layout elements, and importantly an Outlet component for the nested/wrapped Route components to be rendered into.
Example:
import { Outlet } from 'react-router-dom';
const AppLayout = ({ admin }) => admin ? (
<>
<Topbar />
<div className="container">
<Sidebar />
<Outlet />
</div>
</>
) : null;
Render the layout component into a Route wrapping the routes you want to be rendered into the specific layout.
<Routes>
<Route path="/login" element={<Login/>} />
<Route element={<AppLayout admin={admin} />}>
<Route index element={<Home />} />
<Route path="/users" element={<UserList />} />
<Route path="/user/:userId" element={<User />} />
<Route path="/newUser" element={<NewUser />} />
<Route path="/productList" element={<ProductList />} />
<Route path="/product/:productId" element={<Product />} />
<Route path="/newProduct" element={<NewProduct />} />
</Route>
</Routes>
I will share working code from my project, hope this will help you.
Try to create a component layout that should look something like this:
// Layout.js
import React from "react";
import { NavBar } from "./SidebarNav";
export const Layout = ({ children }) => {
return (
<>
<div className="block">
<NavBar />
<div className="w-full ">{children}</div>
</div>
</>
);
};
and then create routes in a similar way:
// routes.js
import { Routes, Route } from "react-router-dom";
import { Layout } from "./layout/Layout";
import Home from "./pages/Home";
import { ItemList } from "./pages/ItemList";
const BaseRouter = () => (
<>
<Layout>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/item-list/" element={<ItemList />} />
</Routes>
</Layout>
</>
);
export default BaseRouter;
Splitting routes into a separate file gives you more freedom and, above all, makes your code more accessible.
// App.js
import { BrowserRouter as Router } from "react-router-dom";
import BaseRouter from "./routes";
function App() {
return (
<Router>
<BaseRouter />
</Router>
);
}
export default App;
I have a basic portfolio app that has the following structure:
App.js
function App() {
return (
<BrowserRouter>
<LeftNav />
<RightNav />
<NavBar />
<Switch>
<Route component={Home} path='/' exact />
<Route component={About} path='/about' />
<Route component={Project} path='/projects' />
<Route component={Contact} path='/contact' />
</Switch>
</BrowserRouter>
)
}
When I click on the link to go the production site it only renders the LeftNav, RightNav, and Navbar. I have to click on the Home component to have the Home Screen load.
I then tried putting the Home component outside of to look like:
function App() {
return (
<BrowserRouter>
<LeftNav />
<RightNav />
<NavBar />
<Home />
<Switch>
<Route component={Home} path='/' exact />
<Route component={About} path='/about' />
<Route component={Project} path='/projects' />
<Route component={Contact} path='/contact' />
</Switch>
</BrowserRouter>
)
}
This is the action I want upon clicking on the link, however then my components don't load. How do I structure this so that the Home component loads up on the initial click and that I'm able to navigate to other pages?
Your first version is good, just add a redirect and change the home path
import React from 'react';
import {BrowserRouter, Switch, Route, Redirect} from 'react-router-dom'; // import Redirect
function App() {
return (
<BrowserRouter>
<LeftNav />
<RightNav />
<NavBar />
<Switch>
<Route component={Home} path='/home' exact /> // change the path
<Route component={About} path='/about' />
<Route component={Project} path='/projects' />
<Route component={Contact} path='/contact' />
<Route path="/" exact> // Add the redirect
<Redirect to="/home" />
</Route>
</Switch>
</BrowserRouter>
)
}
You will have to exchange your home path from being the default page:
<Route component={Home} path='/' exact />
to
<Route component={Home} path='/home' exact />
and then add a 'Redirect' to your App.js :
<Route path="/" exact>
<Redirect to="/home" />
</Route>
In my code, I want Header & Footer components to be rendered in all routes except '/login'so how to do that? How to hide component on a specific route?
const AppRouter = () => {
return (
<BrowserRouter>
<div>
<Header />
<Switch>
<Route path="/login" component={Login} /> {/* I wanna render this route without Header & Footer */}
<Route path="/" component={Home} exact />
<Route path="/product" component={ProductOverview} />
<Route path="/profile" component={Profile} />
<Route component={NotFound} />
</Switch>
<Footer />
</div>
</BrowserRouter>
);
};