It's a Next.js v15 app with React v19 where I want to get benefits from the action
property in a form
. This ensures the form could be submitted even when the JavaScript is disabled on the browser.
The problem is that when I keep the relevant function inside the same server component, everything works. But, if I export the relevant function from a separate action.ts
file, it doesn't work.
For example, the following works:
import { authClient } from "@/lib/auth-client";
export default function SignInPage() {
const abc = async () => {
"use server";
const { data, error } = await authClient.signUp.email({
email: "hello@example.com",
password: "password",
name: "John Doe",
});
console.log(data, error);
};
return (
<form
action={async (formData: FormData) => {
"use server";
abc();
}}
>
<input type="email" name="email" placeholder="Email" />
<input type="password" name="password" placeholder="Password" />
<button type="submit">Sign In</button>
</form>
);
}
As soon as I move the abc()
function in a separate file, it doesn't work:
'use server'
export const abc = async () => {
"use server";
const { data, error } = await authClient.signUp.email({
email: "hello@example.com",
password: "password",
name: "John Doe",
});
console.log(data, error);
};
It throws the following error:
GET /test 200 in 68ms
GET /favicon.ico?favicon.45db1c09.ico 200 in 60ms
⨯ TypeError: Cannot read properties of undefined (reading 'bind')
at bind (webpack://next/dist/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js:3713:22)
at async (webpack://next/dist/src/server/app-render/action-handler.ts:797:27)
at async handleAction (webpack://next/dist/src/server/app-render/action-handler.ts:617:4)
at async renderToHTMLOrFlightImpl (webpack://next/dist/src/server/app-render/app-render.tsx:1310:34)
at async doRender (node_modules/.pnpm/next@15.0.4_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/src/server/base-server.ts:2666:21)
at async responseGenerator (node_modules/.pnpm/next@15.0.4_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/src/server/base-server.ts:3027:21)
at async DevServer.renderToResponseWithComponentsImpl (node_modules/.pnpm/next@15.0.4_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/src/server/base-server.ts:3039:23)
at async DevServer.renderPageComponent (node_modules/.pnpm/next@15.0.4_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/src/server/base-server.ts:3597:15)
at async DevServer.renderToResponseImpl (node_modules/.pnpm/next@15.0.4_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/src/server/base-server.ts:3659:23)
at async DevServer.pipeImpl (node_modules/.pnpm/next@15.0.4_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/src/server/base-server.ts:1698:20)
at async NextNodeServer.handleCatchallRenderRequest (node_modules/.pnpm/next@15.0.4_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/src/server/next-server.ts:1034:6)
at async DevServer.handleRequestImpl (node_modules/.pnpm/next@15.0.4_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/src/server/base-server.ts:1462:8)
at async (node_modules/.pnpm/next@15.0.4_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/src/server/dev/next-dev-server.ts:514:13)
at async Span.traceAsyncFn (node_modules/.pnpm/next@15.0.4_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/src/trace/trace.ts:143:13)
at async DevServer.handleRequest (node_modules/.pnpm/next@15.0.4_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/src/server/dev/next-dev-server.ts:512:19)
at async invokeRender (node_modules/.pnpm/next@15.0.4_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/src/server/lib/router-server.ts:284:10)
at async handleRequest (node_modules/.pnpm/next@15.0.4_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/src/server/lib/router-server.ts:530:15)
at async requestHandlerImpl (node_modules/.pnpm/next@15.0.4_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/src/server/lib/router-server.ts:576:6)
at async Server.requestListener (node_modules/.pnpm/next@15.0.4_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/src/server/lib/start-server.ts:146:6)
795 | })
796 | const formData = await fakeRequest.formData()
> 797 | const action = await decodeAction(formData, serverModuleMap)
| ^
798 | if (typeof action === 'function') {
799 | // Only warn if it's a server action, otherwise skip for other post requests
800 | warnBadServerActionRequest() {
page: '/test'
}
POST /test 500 in 282ms
I need to separate the abc()
function in a separate file. Otherwise, I can't make the SignInPage()
a Client component to use React hooks. The authclient()
we have is coming from the https://www.better-auth.com/docs/basic-usage#sign-in
library. The error isn't specific to authClient
but any asynchronous function will throw the error.
Tinkered with a lot but couldn't figure it out. Would appreciate if anyone can show some light on this 🙏
It turns out an issue to my own implementation even though I am pretty sure that I tried the same thing before posting here but that that didn't work before but now it does.
Anyways, I had to use the correct API from Better Auth and that did it this time:
await authClient.signUp.email()