How to Fix: Issue with `basePath` and Server Actions on client components
The failure is caused by a mismatch between Next.js basePath routing and how a Server Action request is generated from a client component. The page renders correctly under the prefixed route, but the action POST can be sent to the wrong internal endpoint, which breaks the request flow and usually surfaces as a 404, a failed action invocation, or a silent form/action error when basePath is enabled.
What the bug looks like
In the reproduction, the application runs correctly with a configured basePath, and the page itself loads from the prefixed URL. The problem starts when a Server Action is triggered from a client component. Instead of consistently targeting an endpoint under the configured prefix, the generated request can ignore that prefix. When that happens, the server receives a request for the wrong route, so the action cannot be resolved.
This issue is especially visible when using the App Router with forms or action-bound event flows in components marked with 'use client'. It can appear only in development for some setups, but the underlying cause is still a routing inconsistency that should be handled carefully before deploying behind a sub-path.
Understanding the Root Cause
basePath tells Next.js that every application route lives under a prefix such as /docs or /app. Normal navigation usually respects that prefix because the router knows how to generate prefixed links and asset paths. However, Server Actions use a specialized request mechanism that is separate from ordinary page navigation.
When a server action is referenced inside a client component, Next.js serializes metadata for that action and later submits a request back to the server. The bug occurs when that request URL is built without correctly incorporating the configured basePath. In practice, the page may be served from something like /base/some-page, while the action POST is sent as if the app were mounted at /some-page.
Technically, this is not a problem with your action logic itself. The action function can be valid, the client component can be valid, and the page can be valid. The failure happens at the boundary where client-side action submission meets prefixed routing. That is why the bug is reproducible even with very small examples.
Common contributing factors include:
- Using Server Actions directly from a client component form.
- Running the app under a non-root basePath.
- Using development tooling where internal routing behavior differs slightly, such as turbo/dev mode.
- Assuming that all internal framework-generated requests automatically respect the same prefix rules as page navigation.
Step-by-Step Solution
The most reliable fix is to avoid invoking the action directly from a client component boundary that triggers the broken request generation path. Instead, move the action binding into a server component, and pass client-safe props down as needed.
This keeps the action resolution inside the part of the framework that already has correct routing context for basePath.
1. Keep basePath in next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
basePath: '/base'
}
module.exports = nextConfig
If your app is intentionally hosted under a sub-path, this setting is correct and should stay in place.
2. Define the server action in a server file
'use server'
export async function submitAction(formData) {
const value = formData.get('value')
console.log('submitted value:', value)
return { ok: true }
}
This action should remain on the server side and not depend on browser-only APIs.
3. Attach the action from a server component, not directly from a client component
import { submitAction } from './actions'
import ClientForm from './ClientForm'
export default function Page() {
return <ClientForm action={submitAction} />
}
The server component acts as the integration point. This is often enough to avoid the broken client-side route construction.
4. Use the action in the client component through props
'use client'
export default function ClientForm({ action }) {
return (
<form action={action}>
<input name="value" type="text" />
<button type="submit">Submit</button>
</form>
)
}
If your current code imports the server action straight into the client component and that path reproduces the bug, this refactor is the safest first change.
5. If the issue still occurs, move the entire form to a server component
import { submitAction } from './actions'
export default function Page() {
return (
<form action={submitAction}>
<input name="value" type="text" />
<button type="submit">Submit</button>
</form>
)
}
This removes the client component boundary completely for the form submission path. If the bug disappears after this change, that confirms the routing problem is tied to the client-side action handoff.
6. For interactive UI, split responsibilities
If you need rich client-side interactivity, keep the interactive controls in a client component but let a server component own the actual form/action wiring. For example:
import { submitAction } from './actions'
import InteractiveFields from './InteractiveFields'
export default function Page() {
return (
<form action={submitAction}>
<InteractiveFields />
<button type="submit">Submit</button>
</form>
)
}
'use client'
export default function InteractiveFields() {
return <input name="value" type="text" />
}
This pattern preserves interactivity while avoiding the most fragile part of the integration.
7. Verify with the prefixed route
After refactoring, start the app again and test from the prefixed URL. Confirm that:
- The page loads under the configured basePath.
- The form submits successfully.
- No action request is sent to a non-prefixed route.
- The network panel shows the POST originating from the correct application path.
If you want to inspect the original reproduction or compare behavior, use the GitHub reproduction repository.
Common Edge Cases
- Using both basePath and assetPrefix: These settings solve different problems.
assetPrefixdoes not fix action routing. It only affects static asset loading patterns. - Reverse proxy deployments: If Nginx, Traefik, or a platform router also rewrites paths, you may have two layers of prefix handling. That can make the bug appear inconsistent between local and production environments.
- Hardcoded form targets or fetch calls: If you manually call APIs from a client component, ensure every path includes the correct prefix. A missing basePath in one custom request can look similar to this framework-level issue.
- Differences between dev and production: Some internal Next.js behaviors vary across modes. Always run a production build to confirm whether the bug is dev-only or architecture-related.
- Middleware and rewrites: Rewrites can hide the real route shape. If an action works without middleware but fails with it, inspect whether the rewritten request path still matches the app’s prefixed structure.
- Nested client boundaries: Deeply nested client components can make it harder to identify where the action reference is crossing from server to client. Trace the component tree and simplify the action ownership path.
Recommended debugging checklist
- Open the browser network tab and inspect the action request URL.
- Confirm the current page URL includes the expected basePath.
- Temporarily move the form into a pure server component.
- Remove custom rewrites or middleware to isolate the framework bug.
- Test with a production build using
next buildandnext start. - Compare direct server component action binding versus imported action usage in a client component.
FAQ
1. Is this caused by a mistake in my server action code?
Usually no. If the action works without basePath or works when the form is owned by a server component, the main issue is the request path generation, not the business logic inside the action.
2. Can I fix this by manually prepending basePath somewhere in the form?
Not reliably for Server Actions. Unlike a normal form POST to a custom endpoint, server action submission is framework-managed. The safer fix is to restructure where the action is attached rather than trying to patch internal action URLs manually.
3. Should I stop using client components with server actions?
No. You should use them carefully. A strong pattern is to let a server component own the form and action binding, while smaller client components handle UI interactivity inside that form. This preserves the benefits of both models and avoids the routing bug.
The key takeaway is simple: when Next.js basePath and Server Actions collide inside a client component, the real problem is usually the generated action request path. Move the action binding back toward the server component layer, verify the network request uses the prefixed route, and the issue typically disappears without changing your core application logic.