I'm implementing protection route in my vanlife practice project and its not redirecting me it keep giving me this error when i try to access protected route
I'm supposed to get redirected to /login when I try to access the /host.
This is my main.jsx
import { createRoot } from 'react-dom/client'
import Layout from './components/Layout.jsx';
import Home from './pages/Home.jsx';
import About from './pages/About.jsx';
import Login from './pages/Login.jsx';
import Error from './components/Error.jsx';
import Vans, { loader as vanLoader } from './pages/Vans/Vans.jsx';
import VansDetails, { loader as vanDetailLoader } from './pages/Vans/VansDetails.jsx';
import Dashboard from './pages/Host/Dashboard.jsx';
import Income from './pages/Host/Income.jsx';
import Reviews from './pages/Host/Reviews.jsx';
import HostVans, { loader as hostvanLoader } from './pages/Host/HostVans.jsx';
import HostVansDetail, { loader as hostvandetailLoader } from './pages/Host/HostVansDetail.jsx';
import Pricing from './pages/Host/HostVanDetails/Pricing.jsx';
import Photos from './pages/Host/HostVanDetails/Photos.jsx';
import Info from './pages/Host/HostVanDetails/Info.jsx';
import HostLayout from './components/HostLayout.jsx';
import {
RouterProvider,
Route,
createBrowserRouter,
createRoutesFromElements
} from 'react-router-dom';
import NotFound from './pages/NotFound.jsx';
import './server.js';
import { requireAuth } from './utils.js';
import { StrictMode } from 'react';
const router = createBrowserRouter(createRoutesFromElements(
<Route element={<Layout />}>
<Route index element={<Home />} />
<Route path='about' element={<About />} />
<Route path='vans' element={<Vans />} loader={vanLoader} errorElement={<Error />} />
<Route path='vans/:id' element={<VansDetails />} loader={vanDetailLoader} />
<Route path='host' element={<HostLayout />} >
<Route
index
element={<Dashboard />}
loader={async () => await requireAuth()}
/>
<Route
path='income'
element={<Income />}
loader={async () => await requireAuth()}
/>
<Route
path='reviews'
element={<Reviews />}
loader={async () => await requireAuth()}
/>
<Route
path='vans'
element={<HostVans />}
loader={async () => {
await requireAuth()
return hostvanLoader()
}}
/>
<Route
path='vans/:id'
element={<HostVansDetail />}
loader={async (obj) => {
await requireAuth()
return hostvandetailLoader(obj)
}}
>
<Route
index
element={<Info />}
loader={async () => await requireAuth()}
/>
<Route
path='pricing'
element={<Pricing />}
loader={async () => await requireAuth()}
/>
<Route
path='photos'
element={<Photos />}
loader={async () => await requireAuth()}
/>
</Route>
</Route>
<Route path='login' element={<Login />} />
<Route path='*' element={<NotFound />} />
</Route>
));
function App() {
return (
<div className='bg-[#FFF7ED] h-screen font-[inter] relative flex flex-col overflow-x-hidden'>
<RouterProvider router={router} HydrateFallback={<h1>Loading...</h1>} />
</div>
)
}
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>
)
This is my utils.js which is supposed to run by the loader and redirect me to /login
import { redirect } from "react-router-dom"
export async function requireAuth() {
const isLoggedIn = false
if (!isLoggedIn) {
throw redirect("/login")
}
}
throw redirect('/login')
not redirecting but rendering errorElement
instead?I was working with React Router's data APIs and using throw redirect("/login")
in my loader
functions to protect routes. However, instead of redirecting to /login
, React Router was rendering the errorElement
for that route or showing an error response.
I found that manually setting response.body = true
after calling redirect()
made it work (got this idea from this post answer of this guy https://stackoverflow.com/a/76852081/13784221 :
import { redirect } from "react-router-dom";
export async function requireAuth() {
const isLoggedIn = false;
if (!isLoggedIn) {
const response = redirect("/login");
response.body = true; // 👈 This made it work!
throw response;
}
return null;
}
This issue is related to how React Router internally handles thrown Response
objects in loaders and actions. It distinguishes between:
Redirect responses (status
codes 3xx)
Error responses (4xx or 5xx)
And... "unexpected" responses (e.g., missing body info)
When I throw redirect("/login")
, it creates a Response
object with status = 302
and no body. Now, when you also provide an errorElement
, React Router plays safe and tries to render it unless it's very sure you're doing a proper redirect.
React Router checks for:
error instanceof Response && error.status >= 300 && error.status < 400 && error.bodyUsed
So if bodyUsed is false, it falls back to showing the errorElement.
response.body = true
Setting response.body = true
(or even response.bodyUsed = true
) tricks React Router into treating your Response
object as “used” and safe to redirect.
So this:
const response = redirect("/login");
response.body = true;
throw response;
...acts as if the redirect body has been processed, and skips rendering the errorElement
.