Introduction to Next.js 13+, v3

Form Handling with Server Actions

Scott Moss

Scott Moss

Superfilter AI
Introduction to Next.js 13+, v3

Check out a free preview of the full Introduction to Next.js 13+, v3 course

The "Form Handling with Server Actions" Lesson is part of the full, Introduction to Next.js 13+, v3 course featured in this preview video. Here's what you'd learn in this lesson:

Scott demonstrates using server actions to create a form that submits data to the server without using JavaScript. Server actions allow the ability to run code on the server without having to setup a new API route.


Transcript from the "Form Handling with Server Actions" Lesson

>> So let's make some todos. So we need to create a new todo form, which we already did as a test case. I went through and show you how to do it as a client. But I'm gonna try to make a form that submits data without using JavaScript.

I know when was the last time we've done that, this is OG stuff. But we're gonna do that. So I'm actually gonna get rid of the client, get rid of the state, get rid of all this. And we're gonna make a form that submits on the server side.

So just hang tight with me. Where am I using this? I got to take it out somewhere. Yeah, I got to take it out of the home page, otherwise it'll break. So I'm gonna take that out right quick. There we go. Okay, it's just a homepage now. And then for the actual new todo form before we build it out and talk about server actions, I just need to figure out where I wanna put it.

It's a form it never changes. So I'm probably just gonna put it in the dashboard layout. It's gonna put it here. So right above here, I'm just gonna put the new todo form. You can also put it inside of the todo page, that's also fine. But the reason I'm gonna put it in here instead of the todo page is that I don't want the form to wait on the todo page, to load in todos, in order to see the form.

If it was in the same page and the todos took two seconds to load, then you wouldn't see the form for two seconds. So if I put it in the layout, it'll show instantly while the todos are loading. But then you might ask yourself, why would you let someone interact with the form if the todos haven't loaded?

We're not gonna talk about that. That's a UX problem. That's not my job. I'm not a UX designer. So that's why I put that there. So it's not bound. There's no reason to punish this form because it's in a page that's slow, it's not fair. So I'll put it in a layout that renders instantly.

Okay, so there we go and it's right there you just can't see it because there's no style on it. So let me add a style to the form I guess for now. Class name, border, border black, 25% opacity, something that. There you go now you can see it.

So let's get into the next thing then, which is gonna be, it says loading an error, but we'll actually do server actions first, then we'll come back to loading error. I think it's better to talk about server actions first since we're kind of already here. So server actions, the best way I can describe server, actually, you've been doing server actions.

If you've ever made an API, you've already been doing server actions, right? A server action is like running code on the server, that's literally it. So why this thing called server actions if we've already been doing that? Well, what if you didn't have to set the route up yourself.

What if it was just set up for you? What if you could just call a function that gets executed on the server? That's basically what a server action is. Your ability to call a function that gets executed on the server. I mean, which is exactly what happens if you hit an API.

But you don't have to set up the API yourself. You don't have to worry about the route, you don't do any of that stuff. And depending on when you use that action, you can actually trigger it without using JavaScript in the case of forms. So that means you can have a pretty interactive app without any JavaScript on it.

If all your app was was forms, which I know brings us back to when JavaScript and HTML was first invented yeah cool story, but it's you won't get full page reloads either. So you kind of get the best of both worlds. Yeah, so it's not back in a we submit a form and it's just you're just waiting for the whole thing to reload.

You still not getting that. But yeah, you execute code on the server. And this is same thing as something PHP, literally the same thing as something like Elixir and Phoenix. It's literally the same thing. The only difference is, it's all JavaScript. Whereas with those languages, you will do PHP on the back end, but then you're doing JavaScript on front end, or you're doing Phoenix and Elixir on the back end, you're doing JavaScript on front end.

This is just all JavaScript, which could be a benefit if you know JavaScript. So it's really cool. Okay, so how do you make a server action, there's two ways basically if you are in a component. All you have to do is make a function and just use the use server directive at the top of that function, this is one way you can do it.

We're gonna do it the other way, but this is just one way you can do it and then you can use that function almost anywhere. There are some caveats, we'll talk about that. But the preferable one and the one that we'll be doing right now is using it on the action prop on a form.

So an action prop is basically a function to run when the form is submitted. If you just put a function there, it'll reload the whole page and do things. We're going to run this server action when this form is updated. So that's one way todo it. Another way todo it is to basically, the way that we're gonna do it is we're going to create another file in our utils.

I'm gonna call it actions. At the very top of this file, I'm gonna say use server. So I'd rather have use server in this entire file. And then that way everything in this file is gonna be guaranteed to execute on the server. So I'm just gonna do that.

And we're gonna make a new action. We're gonna export it, and it's just a function that creates a todo. I'm gonna call it new todo. It takes in some content, and it's just gonna import the database and use that. So we're gonna say import DB from, actually we don't have todo the at sign, we're right next to it, DB And I'm just gonna say todo equals, need to async this.

So todo = await dbtodo.create. And you wanna add a property called data, and data is all the things that you need to create todo with. In this case, the only thing todo needs to be created is content. Everything else is either defaulted or not necessary. How do I know that?

We wrote the schema. So we can just look right here. ID gets generated. Created at gets generated. Content, if it had a question mark on it, that means it'd be optional. It doesn't, so we have to add content. Completed is also required, but it also has a default, so we don't have to supply it.

So really, the only you have to pass to it todo to create this content. So that's what we did. Prisma knows that too. If I get rid of this, it'll scream at me, hey. If you forget, if you read this, you will see that it's asking me for content somewhere else here.

You just gotta read it very carefully, cool. So we have our todo. Now the only problem with the server actions. Unlike API calls where you can open up http requests, hit the API, get back a response. You can't respond back. So that's probably a big blocker right there.

That's a big no. You kind of need that. If you're performing some action, you need a response back. So that's why server actions are really good for side effects. If you don't know what a side effect is, a side effect is basically like a site, for instance, if you've ever done analytics, you've ever rigged analytics for an app, every time you'd call track, that's a side effect.

The app doesn't depend on you calling analytics. It's probably happening in the background as the person navigates through the app. You're not sitting there waiting, showing a loading screen until you finish this track, right? It's just happening. On the side, it's a side effect. So server actions are great for side effects, things that have to happen on the server in which the UI or the user doesn't need to wait for.

Maybe you have some asynchronous event driven architecture that's handled by web hooks. So you can just fire this that fires a web hook and sell the response to it later. Who knows, but it's great for side effects. If you need to change or what next js would say, mutate your back end, you turn your back end basically this means not only did you change something on your database or whatever source of truth you have, but you need the response from it.

If you need todo that, you can still use our actions and that's what we're gonna do. But I think right now, I would probably still just do all that on a client for now. I think if you need to change the UI based off something that happened on the back end, you should probably just make an API call on the front end, in my opinion.

The same way you've ever used React. Just do a useEffects or whatever, or onClick call this function that does a fetch. I would probably still do that right now, although, as you'll see, we'll be able to do this. So we have a todo, we created it, let's see what happens before we try to get it back.

So let's bring it into our new form todo. So instead of our new new todo form, I'm gonna bring in that action. It's called a new todo. So I'm just gonna say new todo. Bring that in here. And then on the form, I'm gonna say action this. And I'm gonna say new todo, like so, the last thing I need to do is add a button here that says, new todo, and give it a type submit that.

So because we added this to the form now, which means the form is gonna pass in whatever argument goes to this function, whereas before when we wrote it. We assume we're gonna get content. Well, now that it's on a form, we're actually not gonna get content. We're gonna get something called form data.

We're gonna get form data. So form data is just an object that you can use to get data from a form. So in this case, we'll say form data.get, and then the name of the input on which we wanna get the value from. So I haven't given this input a name, let's give it one.

So I'll say the name of this input is content. Right, you can call it whatever you want. You can call it todo, you can say whatever. I'm just gonna say content. And then now in my actions, I can say formData.get('content'). By the name of that input that, this is just freaking out because it doesn't any.

Cool if you've never seen this before, this is a standard, form data is actually standards, not something created by next js, it's a web standard. And I think that's what I kind of like about what they're doing is that they're just relying on standards going forward. Even when we get to API's, you'll see they're just relying on standards.

They're not creating some new thing, which I think is really cool. Looks we're coming full circle, just getting back to the basics. But even more complicated somehow, so it's kinda cool to see it happen. Okay, so now we have that, let's go see what we broke. Yeah, so now you can see right now it's saying, yeah, you wanna use server actions you have to add this to your config.

So what we need todo is go down to the root of our app. You should see a file called next.config.js at the top here in this object. You can add experimental, that's an object and you can add server actions, you can add true, so yeah, turn that on.

Once we have that on, we should get that error to go away. We might have to do a rebuild on that. No, I guess not. I guess it figured itself out. Okay, so there we go. Now let's try to create a new todo and see what happens. I'm just gonna open up the network panel so we can observe some things.

I'm just gonna type in something here, new todo. I'm just gonna click new todo. And you can see immediately a request fired off. Remember, we did not write any network code to fire off a request, so that's one thing. And we don't have an API route that handles this.

So, why did it fire and where did it fire? So, you can see right here, it fired a post request to the page that we're on. And if we look at the payload of it. Yeah, it's got some stuff we didn't write, what is that? We didn't write that.

It's got some action thing. It's got the content that we added, but we didn't add any of this stuff. So this is the part that I'm saying though it does the API routing and submitting for you for free. You could do it yourself with client side JavaScript, but this is happening on the server or this has been executed on a server.

So then if we go look at the terminal here, we don't have any any error so that means it probably saved to the database so we can check the database right click. Easiest way todo that is in another terminal type MPX, Prisma, Studio. If you type that in another terminal, it opened up this other app on localhost 55 55 where you can browse your database visually and you can see I have one todo in there called new todo.

So it worked, it went to the database, no problems. Well, except there's one problem. It didn't show up on the page. I made a new todo and we know we're pulling all the to dues on this page. We saw it. It was a it was an empty array.

Why is it not there? Well, if I just refresh, ergo this shows up now on the page. But why do I have to refresh it after I save that? That's kind of gross, right? We want that just to pop up immediately. And this is why I said server actions are really great for mutations, because if you need the UI to be updated after you perform a server action, it's not as simple.

You don't get that for free as you would waiting for a response on an http call where you can, I'm just gonna await this fetch, and then get the result and then do whatever I want with the result, or even do an optimistic update. You can do all of that with server actions.

You can do automatic updates, it's a lot harder. But at that point, you're writing client side code, you need client side code, optimistic updates, so I might have to choose a client site fetch. But for now, we're gonna get around all of that by tapping into the revalidation.

The revalidation functionality. So, if you look at the page in which needs to be updated, whenever this server action is created is gonna be /todos, right we want /todos to re-fetch its data cuz what is it doing on load? It's getting all the todos. So we wanna re-validate.

This page is cached right now. So we wanna tell next.js, hey, after we perform the server action, re-validate this page, which will then tell it to go fetch its data. So that's what we wanna do after our server action. So let's do that. So we can go into our action.

And right after we do this, we can just say, yeah, just do a revalidate. Right-click, revalidate path for slash todos. You can think of this as a soft refresh. It's not gonna refresh the page, but it's gonna expire the cache of that page, which then forces it to go refresh its data.

So let's do that. So now when I add a new todo, clean my room, it shows up on the page without me refreshing. That's because we did a server action on the server,that changed something in the database. We waited for that to happen. And then we told next.js, delete the cache for this page /todos, which then made it go fetch its own data all over again, which then got an array of two todos and put them on the page.

The crazy thing about this is that this would work even with JavaScript off, because there's no JavaScript on the front end that's making this happen. It would work completely without JavaScript. And that's the benefit of server actions that you can have interactive apps without JavaScript. But you can also do this without writing JavaScript.

Just use a pure HTML too. So it's kind of cool to see next js come around to this and building this functionality. But we've had this functionality before frameworks existed, but not in a structured way. So I guess this is kind of the best of both worlds, having that structure but also kind of getting back to the standards of the web, form actions, response objects, things that, which I think is kind of cool.

Okay, so yeah, any questions on that? When I first saw this, I thought it was just pretty magical. But then I realized, it's just like web standard stuff. But it is still pretty cool to be able to use React purely as a templating engine and not have to rely on it for heavy client-side stuff unless you opt into it.

So it's kind of cool. Yeah, but any questions on that? No, so yeah, we can just keep adding stuff right here, right? We can be, hello, yellow, whatever. And then if we refresh this, they should still be there because we're pulling that down.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now