working with Server Actions

Nelson Chege
2 min readAug 17, 2023

--

Server actions were introduced in NextJs version 13.4 in Alpha together with turbopack (Beta) and App router(stable).

They have a good developer Experience when using them plus other features that are really nice to have.

with server actions:

  • allows progressive Enhancement which allows forms to function correctly without javascript
  • you can now you forms inside of server action, reducing the amount of javascript shipped to the client
  • you don't need to create other endpoints todo mutations. (but these actions are considered as api endpoints by nextjs)

with server actions, you can either use Action or formAction to handle submit.

Because at the moment, server Actions is still in Alpha, you have to add this to your next.config.js


/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
serverActions: true,
},
};

module.exports = nextConfig;

now creating a simple form that calls an action

import { addTodo } from "../lib/actions";
const AddTodo = () => {
return (
<div>
<form action={addTodo}>
<div className="">
<input
type="text"
id="todo"
required
/>
</div>
<div>
<button
type="submit"
>
Submit
</button>
</div>
</form>
</div>
);
};

export default AddTodo;

server actions can be imported and saved in a separate file making them cool to work with.

the ”use server” must be included on top of the file

"use server";

import { randomUUID } from "crypto";
import { revalidatePath } from "next/cache";

export async function addTodo(data: FormData) {
const todo = data.get("todo");

await fetch("http://localhost:3500/todos", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
todo,
userId: randomUUID,
}),
});

revalidatePath("/");
}

inside the Action you are able to do all the things that a normal API endpoint used to handle such as communicating to a database

the revalidatePath will update the cache with the new data add to the specified path.

And that's all required to get started using server actions.

An extra feature that can be included inside server action is the useTransition hook.
This provides pending and startTransitions to play around with. you can disable the button using the pending status.

"use client";

import React, { useRef, useTransition } from "react";

type AddTodoFormProps = { addTodo: (todo: string) => Promise<void> };

const AddTodoForm = ({ addTodo }: AddTodoFormProps) => {
const todoRef = useRef<HTMLInputElement>(null);
let [pending, startTransition] = useTransition();
return (
<>
<div className="">
<input
ref={todoRef}
type="text"
id="todo"
name="todo"
required
/>
</div>
<div>
<button
disabled={pending}
onClick={async () => {
startTransition(async () => {
await addTodo(todoRef.current!.value);
});
}}
>
Submit
</button>
</div>
</>
);
};

export default AddTodoForm;

for the action, data passed to the action will only be that one variable

"use server";

import { randomUUID } from "crypto";
import { revalidatePath } from "next/cache";

export async function addTodo(data: string) {
const todo = data;
const payload = {
todo,
userId: randomUUID(),
createdAt: new Date(new Date()).toLocaleString(),
};

await fetch("http://localhost:3500/todos", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});

revalidatePath("/");
}

conclusion

server actions is an easier way to communicate with your backend without extra endpoints and also reduce the javascript code that is passed to the client side.

--

--

No responses yet