Creating a Server Action for Form Data
So far, we've explored two out of four different types of interaction with the server: an API endpoint that takes JSON and another that takes form data.
Now we'll dive into creating a server action and using it to submit data.
Creating a Server Action
Server actions can be placed within a React server component, like a page, or within a separate file. For now, we'll create the server action inside src/app/page.tsx.
The server action will be an asynchronous function named onDataAction that we'll pass into the RegistrationForm client component:
However, there's an error underneath the onDataAction prop because it isn't defined as a property on RegistrationForm.
On the page in the browser, we are shown an error:
Next.js thinks that onDataAction is an event handler because it doesn't know that it's a server action.
In order to resolve this, we need to include "use server"; inside of the onDataAction function to notify Next.js that the function is server-only:
With this change, the error goes away in the browser but TypeScript isn't happy that onDataAction not being defined as a property on RegistrationForm.
Let's fix it.
Adding onDataAction to RegistrationForm
Over in RegistrationForm.tsx, we'll define onDataAction as a function that takes data and returns a promise. For now we'll broadly define the data type as any, and the return type will be Promise<void> since nothing is returned:
With the onDataAction function defined and wired up, we can test it out.
Testing the Server Action
On the web page, fill out the form and hit submit.
Nothing seems to happen in the browser console where we previously saw our console.log() output.
Instead, we'll check the terminal and see that our data has successfully been sent to the server:
Now that we know our submit is working properly, let's add the proper types to our server action.
Adding Typing and Validating Data
Back in page.tsx we import z from Zod and our schema:
Inside the onDataAction function, we'll type data using the z.infer trick we've used before. We also will use the schema's safeParse to parse the data and use the conditional logic to check if the parsing was successful: