How do I add 404 - Not Found page in React with React-Router?
Here's my attempt:
// routes.tsx
export const routes = [
{
path: '/students',
render: (props: any) => <List {...props} title={`Students`} />,
},
{
path: '/teachers',
render: (props: any) => <List {...props} title={`Teachers`} />,
},
]
// App.tsx
import { routes } from './routes'
function App() {
const routeComponents = routes.map(({ path, render }, key) => (
<Route exact path={path} render={render} key={key} />
))
return (
<ThemeProvider theme={theme}>
<CSSReset />
<Suspense fallback={<Loader />}>
<Router>
<Switch>
<Route exact path="/" component={Signin} />
<Route path="/signin" component={Signin} />
<Layout>{routeComponents}</Layout>
{/* <Route component={NotFound} /> */}
<Route path="*" component={NotFound} />
</Switch>
</Router>
</Suspense>
</ThemeProvider>
)
}
export default App
But I can't see my custom "404 - Not Found" page when I go to 'http://localhost:3000/nothing', but <Layout /> component.
What I am doing wrong?
Stack: TypeScript, React#v16.13.1, react-router-dom#v5.1.2
404 Page on react does not need to have a path as it needs to be a page rendered when roue is not found between the paths of the pages you already have. It should work this way:
<Switch>
<Route exact path="/" component={Signin} />
<Route path="/signin" component={Signin} />
{routeComponents()}
<Route component={NotFound} />
</Switch>
Left blank path="". It will render 404 page.
see -
<Route path="" component={PageNotFound} />
<Route exact path="/" component={Signin} />
<Route path="/signin" component={Signin} />
This can fix
https://stackoverflow.com/a/64651959/16361679
return (
<ThemeProvider theme={theme}>
<CSSReset />
<Suspense fallback={<Loader />}>
<Router>
<Switch>
<Route exact path="/" component={Signin} />
<Route path="/signin" component={Signin} />
<Layout>
<Switch>
{routeComponents}
<Route path="*" component={NotFound} />
<Switch>
</Layout>
</Switch>
</Router>
</Suspense>
</ThemeProvider>
)
Related
I have looked at the previous questions and googled for the answer and I think I almost know what the issue in here is, but don't know how to solve it.
return (
<Router>
<Routes>
<Route path="/login" element={<Login />} />
{admin && (
<>
<Topbar />
<div className="container">
<Sidebar />
<Route path="/" element={<Home />} />
<Route path="/users" element={<UserList />} />
<Route path="/user/:userId" element={<User />} />
<Route path="/newUser" element={<NewUser />} />
<Route path="/products" element={<ProductList />} />
<Route path="/product/:productId" element={<Product />} />
<Route path="/newproduct" element={<NewProduct />} />
</div>
</>
)}
</Routes>
</Router>
Please tell me how to setup the topar and slidebar components here.
Requesting my fellow coders to look at this one and thanks in advance.
The error is clear, you only can use the <Route /> tag, and remove the <div className="container"> also.
Try this:
return (
<>
<Topbar />
<Sidebar />
<Router>
<Routes>
<Route path="/login" element={<Login />} />
{admin && (
<>
<Route path="/" element={<Home />} />
<Route path="/users" element={<UserList />} />
<Route path="/user/:userId" element={<User />} />
<Route path="/newUser" element={<NewUser />} />
<Route path="/products" element={<ProductList />} />
<Route path="/product/:productId" element={<Product />} />
<Route path="/newproduct" element={<NewProduct />} />
</>
)}
</Routes>
</Router>
</>
)
Or you can import your TOPBAR and SIDEBAR components inside every other component.
to render a login page without a topbar and sidebar create a private router component and wrap it around all the routes you want to render a topbar and sidebar with
use react-outlet
here is what worked for me
app.js
import "./App.css";
import Sidebar from "./Components/Sidebar/Sidebar";
import TopBar from "./Components/TopBar/TopBar";
import Home from "./Pages/Home/Home";
import UserList from "./Pages/UserList/UserList";
import { BrowserRouter, Routes, Route } from "react-router-dom";
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 { useSelector } from "react-redux";
import Login from "./Pages/Login/Login";
import { Outlet } from "react-router-dom";
const SidebarLayout = () => (
<>
<TopBar />
<div className="container">
<Sidebar />
<Outlet />
</div>
</>
);
function App() {
// const admin = useSelector((state) => state.user.currentUser.isAdmin);
const admin = JSON.parse(
JSON.parse(localStorage.getItem("persist:root")).user
).currentUser.isAdmin;
return (
<BrowserRouter>
<Routes>
<Route element={<SidebarLayout />}>
<Route index element={<Home />} />
<Route exact path="/users" element={<UserList />} />
<Route exact path="/users/:id" element={<User />} />
<Route exact path="/newUser" element={<NewUser />} />
<Route exact path="/products" element={<ProductList />} />
<Route exact path="/products/:id" element={<Product />} />
<Route exact path="/newProduct" element={<NewProduct />} />
</Route>
<Route exact path="/login" element={<Login />} />
</Routes>
</BrowserRouter>
);
}
export default App;
import React from "react";
import { Router, Routes, Route } from "react-router-dom";
import Home from "./Home";
import UserList from "./UserList";
import NewUser from "./NewUser";
import ProductList from "./ProductList";
import Product from "./Product";
import NewProduct from "./NewProduct";
import Login from "./Login";
const AppRoute = () => {
return (
<>
{admin ? (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/users" element={<UserList />} />
<Route path="/user/:userId" element={<User />} />
<Route path="/newUser" element={<NewUser />} />
<Route path="/products" element={<ProductList />} />
<Route path="/product/:productId" element={<Product />} />
<Route path="/newproduct" element={<NewProduct />} />
</Routes>
</Router>
) : (
<Router>
<Routes>
<Route path="/" element={<Login />} />
</Routes>
</Router>
)}
</>
);
};
export default AppRoute;
const admin = JSON.parse(
JSON.parse(localStorage.getItem("persist:root")).user
).currentUser.isAdmin;
return (
<Router>
<>
<Topbar />
<div className="container">
<Sidebar />
<Routes>
<Route
path="/login"
element={admin ? <Navigate to="/" /> : <Login />}
/>
<Route exact path="/" element={<Home />}></Route>
<Route path="/users" element={<UserList />}></Route>
<Route path="/user/:userId" element={<User />}></Route>
<Route path="/newUser" element={<NewUser />}></Route>
<Route path="/products" element={<ProductList />}></Route>
<Route path="/product/:productId" element={<Product />}></Route>
<Route path="/newproduct" element={<NewProduct />}></Route>
</Routes>
</div>
</>
</Router>
); ```
i'm using hashrouter, but when i set the base name, that occours:
http://localhost:3000/admin/#/admin/dashboard
i want:
http://localhost:3000/admin/#/dashboard
how can i disable the reapeating admin after #?
function App() {
return (
<>
<Router basename="/admin">
<SessionProvider>
<Switch>
<Route path="/dashboard" component={Dashboard} />
<Route path="/users" component={Users} />
</Switch>
</SessionProvider>
</Router>
</>
);
}
I am using use location to hide the navbar if the pathname name is /admin, /employee, /publisher. but I got an error saying that Error: useLocation() may be used only in the context of a <Router> component.
This is My App.js
import { BrowserRouter as Router, Routes, Route, useLocation } from 'react-router-dom';
import Navbar from './components/Navbar';
function App() {
const location = useLocation()
return (
<>
<UserState>
<Router>
{!["/admin", "/employee", "/publisher"].includes(location.pathname) && <Navbar/>} //For to hide the navbar if the pathname is /admin, /employee, /publisher
<Routes onUpdate={() => window.scrollTo(0, 0)}>
<Route exact path="/" element={<Home />} />
<Route exact path="/service" element={<Service />} />
<Route exact path="/contact" element={<Contact />} />
<Route exact path="/login" element={<Login />} />
<Route exact path="/reset" element={<Reset />} />
<Route exact path="/reset/:token" element={<Newpassword />} />
<Route path="*" element={<NoteFound/>} />
{/* admin pages */}
<Route path="/admin" element={<Admin />}>
<Route path="add-project" element={<Addproject />} />
<Route path="all-user" element={<AllUser />} />
</Route>
{/* Publisher dasboard */}
<Route path="/publisher" element={<Publisher />}>
<Route path="project-status" element={<ProjectStatus />} />
<Route path="allpublise" element={<Allpublise />} />
</Route>
</Route>
</Routes>
</Router>
</UserState>
</>
);
}
export default App;
The useLocation hook (and all other react-router-dom hooks) need a router above it in the ReactTree so that a routing context is available.
2 options:
Move the Router component up the tree and wrap the App component so it can use the useLocation hook.
import { BrowserRouter as Router } from 'react-router-dom';
...
<Router>
<App />
</Router>
...
import { Routes, Route, useLocation } from 'react-router-dom';
import Navbar from './components/Navbar';
function App() {
const location = useLocation();
return (
<UserState>
{!["/admin", "/employee", "/publisher"].includes(location.pathname) && <Navbar/>}
<Routes onUpdate={() => window.scrollTo(0, 0)}>
<Route path="/" element={<Home />} />
<Route path="/service" element={<Service />} />
<Route path="/contact" element={<Contact />} />
<Route path="/login" element={<Login />} />
<Route path="/reset" element={<Reset />} />
<Route path="/reset/:token" element={<Newpassword />} />
<Route path="*" element={<NoteFound/>} />
{/* admin pages */}
<Route path="/admin" element={<Admin />}>
<Route path="add-project" element={<Addproject />} />
<Route path="all-user" element={<AllUser />} />
</Route>
{/* Publisher dasboard */}
<Route path="/publisher" element={<Publisher />}>
<Route path="project-status" element={<ProjectStatus />} />
<Route path="allpublise" element={<Allpublise />} />
</Route>
</Routes>
</UserState>
);
}
export default App;
Move the useLocation hook down the tree so that it's used within the Router component.
import { Routes, Route, useLocation } from 'react-router-dom';
import Navbar from './components/Navbar';
function App() {
return (
<UserState>
<Router>
<Navbar/>
<Routes onUpdate={() => window.scrollTo(0, 0)}>
<Route path="/" element={<Home />} />
<Route path="/service" element={<Service />} />
<Route path="/contact" element={<Contact />} />
<Route path="/login" element={<Login />} />
<Route path="/reset" element={<Reset />} />
<Route path="/reset/:token" element={<Newpassword />} />
<Route path="*" element={<NoteFound/>} />
{/* admin pages */}
<Route path="/admin" element={<Admin />}>
<Route path="add-project" element={<Addproject />} />
<Route path="all-user" element={<AllUser />} />
</Route>
{/* Publisher dasboard */}
<Route path="/publisher" element={<Publisher />}>
<Route path="project-status" element={<ProjectStatus />} />
<Route path="allpublise" element={<Allpublise />} />
</Route>
</Routes>
</Router>
</UserState>
);
}
export default App;
...
const Navbar = () => {
const location = useLocation();
return !["/admin", "/employee", "/publisher"].includes(location.pathname)
? /* Return navbar JSX here */
: null;
};
useLocation() hook is used to extract the current location from the router.
For this purpose it's need the router context to pass the Location content
So if you want to use this hook, you need to use it in one of the nested components in <Router> Provider.
For your situation, you need to move the hook into the navbar component.
import { useLocation } from 'react-router-dom';
function Navbar() {
const location = useLocation()
if(["/admin", "/employee", "/publisher"].includes(location.pathname))
return <></>
return (
<>
I'm a navbar
</>
);
}
export default Navbar;
I am using react-router-dom v6. I want my Login page to be rendered without the Sidebar and Topbar components. How to do it?
function App() {
return (
<Router>
<Container>
<Sidebar />
<Content>
<Topbar />
<MainContent>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/users" element={<UserList />} />
<Route path="/users/:id" element={<User />} />
<Route path="/newUser" element={<NewUser />} />
<Route path="/products" element={<ProductList />} />
<Route path="/products/:id" element={<Product />} />
<Route path="/newProduct" element={<NewProduct />} />
<Route path="/login" element={<Login />} />
</Routes>
</MainContent>
</Content>
</Container>
</Router>
);
}
I don't want my login page to render inside the MainContent but taking the whole page without Sidebar and Topbar.
I tried moving the Routes upper and have the login route above the sidebar but there is an error Error: [Sidebar] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>
Since SideBar and TopBar appear to be part of a "layout", and you want a different "layout" specifically for "/login" then I suggest abstracting the container components into layout components. Each layout container should render an Outlet for their respective nested routes.
const AppLayout = () => (
<Container>
<Sidebar />
<Content>
<Topbar />
<MainContent>
<Outlet />
</MainContent>
</Content>
</Container>
);
const LoginLayout = () => (
<Container>
<Content>
<MainContent>
<Outlet />
</MainContent>
</Content>
</Container>
);
...
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<AppLayout />}>
<Route index element={<Home />} />
<Route path="users" element={<UserList />} />
<Route path="users/:id" element={<User />} />
<Route path="newUser" element={<NewUser />} />
<Route path="products" element={<ProductList />} />
<Route path="products/:id" element={<Product />} />
<Route path="newProduct" element={<NewProduct />} />
</Route>
<Route path="/login" element={<LoginLayout />}>
<Route index element={<Login />} />
</Route>
</Routes>
</Router>
);
}
function App() {
return (
<Router>
<Container>
{
Login || <Sidebar />
}
<Content>
{
Login || <Topbar />
}
<MainContent>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/users" element={<UserList />} />
<Route path="/users/:id" element={<User />} />
<Route path="/newUser" element={<NewUser />} />
<Route path="/products" element={<ProductList />} />
<Route path="/products/:id" element={<Product />} />
<Route path="/newProduct" element={<NewProduct />} />
<Route path="/login" element={<Login />} />
</Routes>
</MainContent>
</Content>
</Container>
</Router>
);
}
try conditional rendering. It works for me!
how to get params using react router dom v5.0.1 before I could get parameters, but now I can't get them, what's the problem?
<BrowserRouter>
<Route path='/' exact component={Login} />
<Route path='/register' component={Register} />
<Route path='/home' component={Home} />
<Route path='/all/merchant' component={AllMerchant} />
<Route path='/detail/merchant/:id_merchant' exact component={DetailMerchant} />
<Route path='/detail/merchant/promo/:id_merchant' component={MerchantByPromo} />
<Route path='/promo/voucher/by/merchant/' component={MerchantByPromoDetail} />
<Route path='/all/category' component={Category} />
<Route path='/category/id/:category_id' component={CategoryById} render = {props => <CategoryById {...props} key={this.props.match.params} /> }/>
<Route path='/promo/grid/view' component={PromoGridView} />
{/* params not found */}
<Route path='/promo/grid/view/by/category/:category_id' component={AllPromoGridView} />
<Route path='/detail/promo/:voucher_id/category_id/:category_id' component={PromoGridViewDetail} />
{/* params not found */}
<Route path='/account' component={Account} />
</BrowserRouter>
According to the react-router doc, it should be accessible in your Component via the props match:
Example:
// the route
<Route path="/:id" component={Child} />
/*
...
*/
//the component
function Child({ match }) {
return (
<div>
<h3>ID: {match.params.id}</h3>
</div>
);
}