react-router
react-router copied to clipboard
useFetcher.submit causes 404 when basename is set in react-router.config.ts
Reproduction
stackblitz https://stackblitz.com/edit/2025-10-17-41732624-836b-4844-4054-e103b9099801?file=src%2Fapp%2Froutes%2Farticle.tsx
When basename is specified in react-router.config.ts, calling useFetcher().submit() causes the client to navigate to a 404 page. This issue does not occur when no basename is set.
After tracing the execution in debug mode, I found that the problem seems to originate in the normalizeTo function. It appears that the normalizedPath string is being constructed incorrectly. For example, when I set: basename = "/react-router-base/"
the resulting value of normalizedPath becomes:
/react-router-base/react-router-base/?index
—notice that basename is duplicated.
If I manually change the value of normalizedPath in the debugger to: /react-router-base/?index the 404 behavior disappears and everything works correctly.
I filed another issue yesterday, but I realized that I described the wrong root cause. This new issue correctly points out the actual problem.
System Info
from stackblitz
❯ npx envinfo --system --npmPackages '{vite,react-router,@react-router/*}' --binaries --browsers
System:
OS: Linux 5.0 undefined
CPU: (8) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
Memory: 0 Bytes / 0 Bytes
Shell: 1.0 - /bin/jsh
Binaries:
Node: 20.19.1 - /usr/local/bin/node
Yarn: 1.22.19 - /usr/local/bin/yarn
npm: 10.8.2 - /usr/local/bin/npm
pnpm: 8.15.6 - /usr/local/bin/pnpm
npmPackages:
@react-router/dev: 7.9.4 => 7.9.4
@react-router/node: 7.9.4 => 7.9.4
@react-router/serve: 7.9.4 => 7.9.4
react-router: 7.9.4 => 7.9.4
vite: 7.1.10 => 7.1.10
Used Package Manager
npm
Expected Behavior
navigation to 404 page. ↓ here is a movie https://github.com/user-attachments/assets/45a48a82-75ce-40da-a450-ecf167300647
Actual Behavior
When calling fetcher.submit(), the page should not switch to a 404 screen.
It certainly might be a bug, but in the first place, I don't think you need useFormAction if you want to submit to the current route.
Also, useFetcher's submit() is designed to always return a Promise<void>, so if you want to use the return value, you need to reference fetcher.data.
export default function Page({ params, loaderData }: Route.ComponentProps) {
const fetcher2 = useFetcher();
useEffect(() => {
console.log(`server side response?:`, fetcher2.data);
}, [fetcher2.data]);
return (
<>
test
<hr />
<hr />
<Form method="post" navigate={false}>
<input type="hidden" name="title" defaultValue="foo" />
<button type="submit">server-side-process-1</button> ← watch server side
console
</Form>
<hr />
<div>
<button
onClick={() => fetcher2.submit({ title: 'bar' }, { method: 'post' })}
>
server-side-process-2
</button>{' '}
← click to redirect 404 why?
</div>
</>
);
}
@rururux Thanks for your reply! I understand that to use the return value of a fetcher, I need to reference fetcher.data. That part is not the issue here. The current problem is that the moment I execute fetcher2.submit, the screen immediately switches to a “404 not found” page. When I traced the process in debug mode, I found that the issue is an incorrect value in the normalizedPath variable when a basename is set. As a result, the server-side action is never executed, and fetcher.data remains empty. This clearly shows that the fetcher isn’t functioning properly. There’s no issue when basename is not specified.
I see. I can confirm that a problem is indeed occurring.
It looks like the basename is being set again on the following line:
https://github.com/remix-run/react-router/blob/64f82f1581bd5e72a9312802c60bb61b9278135a/packages/react-router/lib/router/router.ts#L4536-L4538
I will investigate a bit further.