How to display register/signin errors on the client from a server action?

I'm using a server action on a register page to conditionally create db entries for a new user, and send out a verification email. My question is: How can I display an error on the register form like: "that username is already taken," based on the internal logic of the server action. Attached are my form and the server action respectively
No description
No description
17 Replies
sean
sean16mo ago
On your backend you need to return errors, and then on your frontend you could create a variable called "error" (or errors, if you wanna display multiple of them). And you can just conditionally check if that variable has an assigned value and then display an error component
Ryan
RyanOP16mo ago
I’m more wondering how to do this with server actions. I’ve only done that with API routes that return NextResponses
sean
sean16mo ago
Exactly how I said, just use an error variable and change that Also remember that you should handle errors both on the backend and frontend
Ryan
RyanOP16mo ago
How? just like return a JSON object?
sean
sean16mo ago
Yes And a status code that is different from 200
sean
sean16mo ago
If you don't know what status code to use, check out https://http.cat
HTTP Status Cats API
HTTP Cats
API for HTTP Cats
sean
sean16mo ago
Funnily enough there's also https://http.dog
Ryan
RyanOP16mo ago
How do you intercept the response on the client? this is my current code:
export default async function Register() {

async function registerUser(data: FormData) {
"use server";

return new NextResponse(
JSON.stringify({
message: "user created successfully",
}),
{ status: 200 }
);
}

return (

<form action={registerUser} className="space-y-12 w-full sm:w-[400px]">
<div className="grid w-full items-center gap-1.5">
<Label>Name</Label>
<Input className="mb-6" name="name" required />
<Label>Email</Label>
<Input className="mb-6" name="email" type="email" required />
<Label>Password</Label>
<Input className="mb-6" name="password" type="password" required />
<Button type="submit">Register</Button>
</div>
</form>

);
}
export default async function Register() {

async function registerUser(data: FormData) {
"use server";

return new NextResponse(
JSON.stringify({
message: "user created successfully",
}),
{ status: 200 }
);
}

return (

<form action={registerUser} className="space-y-12 w-full sm:w-[400px]">
<div className="grid w-full items-center gap-1.5">
<Label>Name</Label>
<Input className="mb-6" name="name" required />
<Label>Email</Label>
<Input className="mb-6" name="email" type="email" required />
<Label>Password</Label>
<Input className="mb-6" name="password" type="password" required />
<Button type="submit">Register</Button>
</div>
</form>

);
}
so registerUser() is the server action, and inside the server action, I return a 200. How do I "catch" the response? so to speak Such that I can render a “registration failed” error In the page
sean
sean16mo ago
I'm at work so I can't really help you now, I'll take a look once I'm done @machina0 :)
Ryan
RyanOP16mo ago
No worries at all
sean
sean16mo ago
Hey @machina0 I wrote a small example on how you can get errors from your server actions (HomePage)
"use client";

import { useState } from "react";
import { fetchYourData } from "./action";

export default function Home() {
const [error, setError] = useState("");

return (
<>
<div>{error}</div>
<button
onClick={async () => {
const data = await fetchYourData();
if (data.status === 200) {
setError("");
} else if (data.error) {
setError(data.error);
}
}}
>
Click me!
</button>
</>
);
}
"use client";

import { useState } from "react";
import { fetchYourData } from "./action";

export default function Home() {
const [error, setError] = useState("");

return (
<>
<div>{error}</div>
<button
onClick={async () => {
const data = await fetchYourData();
if (data.status === 200) {
setError("");
} else if (data.error) {
setError(data.error);
}
}}
>
Click me!
</button>
</>
);
}
(action.ts)
"use server";

export async function fetchYourData() {
const requestShouldFail = Math.random() < 0.5;
if (requestShouldFail) {
return {
status: 500,
error: "Internal server error",
};
} else {
return {
status: 200,
error: null,
};
}
}
"use server";

export async function fetchYourData() {
const requestShouldFail = Math.random() < 0.5;
if (requestShouldFail) {
return {
status: 500,
error: "Internal server error",
};
} else {
return {
status: 200,
error: null,
};
}
}
Ryan
RyanOP16mo ago
thank you! @gigantino
sean
sean16mo ago
No problem :)
Ryan
RyanOP16mo ago
is this how you do it, or do would you personally still use an api route with a client fetch to do most stuff i'm trying to understand what the best circumstances are to use server actions
sean
sean16mo ago
I wouldn't always use them personally, and I've yet to experiment enough (they are still experimental, so I haven't had a chance as of right now) If I were you I'd go with API routes I usually just prefer to fetch data on the server to pre-render it, and then if I need to make other requests I just do them with API routes. I honestly though love Svelte's approach of fetching data, and I think that the React and Next team should take inspiration from them
Ryan
RyanOP16mo ago
I'm curious about svelte. I'll have to mess around with it someday
sean
sean16mo ago
You have to, it feels like black magic I don't get how it does what it does
Want results from more Discord servers?
Add your server