What are Server Actions and how do they differ from API Routes?
Server Actions are asynchronous functions that run on the server but can be invoked from Client Components. Unlike API routes, they don't require manually creating a separate endpoint file; Next.js handles the RPC (Remote Procedure Call) layer automatically.
How do you handle pending states in Server Actions?
Using the useFormStatus hook in a Client Component rendered inside the form. It provides a pending boolean.
How do you validate data in Server Actions?
Since Server Actions receive raw FormData or JSON, you should validate inputs using a schema validation library like Zod before processing.
import { z } from 'zod';
const schema = z.object({
email: z.string().email(),
});
export async function create(formData) {
const parsed = schema.safeParse({
email: formData.get('email'),
});
if (!parsed.success) {
return { errors: parsed.error.flatten().fieldErrors };
}
// ...
}
What is `useFormState` (or `useActionState`)?
It is a hook used to handle the result of a Server Action (like validation errors or success messages) in a Client Component. It takes the action and an initial state.
const [state, formAction] = useFormState(createUser, initialState);
Can Server Actions be used outside of forms?
Yes. You can invoke them from event handlers (onClick), useEffect, or other side effects. However, for non-form interactions, you often need to manage the pending state manually or use useTransition.
What is Optimistic UI and how do you implement it with Server Actions?
Optimistic UI updates the interface immediately as if the action succeeded, without waiting for the server response. If the server fails, the UI rolls back.
Implementation: Use the useOptimistic hook.
import { useOptimistic } from 'react';
function MessageList({ messages, sendMessage }) {
const [optimisticMessages, addOptimisticMessage] = useOptimistic(
messages,
(state, newMessage) => [...state, newMessage]
);
async function action(formData) {
const message = formData.get('message');
addOptimisticMessage({ text: message, sending: true });
await sendMessage(formData);
}
return (
// ... render optimisticMessages ...
);
}