GuideEdit this page.md

Server functions

Server functions are async functions that can be called from the client-side. You don't have to implement API endpoints for these functions to be callable as the client can call these functions like a direct reference to the function itself. Server functions are usable on forms, buttons, submit inputs, and as props to client components. React and the runtime will manage calling these functions on the server-side.

You can expose any "use server"; marked function as a server function. Server functions can be called from the client using the action prop on a <form> element, formAction on a <button> or <input> element, or from a client component passing down the server function as a prop to the client component.

If you use TypeScript, all of your server functions are type-safe and you will get type errors if you pass the wrong parameters to a server function or if you try to access a property on the response object that doesn't exist.

You can define server functions inline in your components as any other event handler in a React component.

export default function App() { async function action() { "use server"; console.log("Server function called!"); } return ( <form action={action}> <button type="submit">Submit</button> </form> ); }

Progressive enhancement: if you have JavaScript enabled, the server function will be called using the fetch API and the response will be used to update the DOM in a React transition. If you don't have JavaScript enabled, the server function will be called using a regular HTTP request.

You can even define your server functions inline your JSX.

export default function App() { return ( <form action={async () => { "use server"; console.log("Server function called!"); }} > <button type="submit">Submit</button> </form> ); }

Server functions are able to access all variables in scope, including references to props and any variables available in the scope of the server function from the last render of the component as server functions are mapped on each render of the server component.

If you want to keep your server functions in separate modules, you can do so by using the "use server"; pragma at the top of your module. All exported functions from a server function module will be usable as server functions.

"use server"; export async function action() { console.log("Server function called!"); }

You will get all form data as an object as the first parameter to your server function.

export default function App() { async function action(formData) { "use server"; console.log(`Server function called by ${formData.get("name")}!`); } return ( <form action={action}> Your name: <input name="name" /> <button type="submit">Submit</button> </form> ); }
import { useActionState } from "@lazarv/react-server/router"; export default function App() { async function action(formData) { "use server"; console.log(`Server function called by ${formData.get("name")}!`); } const { error } = useActionState(action); return ( <form action={action}> Your name: <input name="name" /> <button type="submit">Submit</button> {error && <p>{error.message}</p>} </form> ); }

To access the action state, you can use the useActionState hook. The useActionState hook takes the server function as the first parameter and returns an object with the following properties:

You can also pass server function references to client components as props and call them from the client component as any other async function.

"use client"; export default function MyClientComponent({ action }) { const handleClick = () => { action({ name: "John" }); }; return <button onClick={handleClick}>Click me!</button>; }
import MyClientComponent from "./MyClientComponent"; export default function App() { async function action({ name }) { "use server"; console.log(`Server function called by ${name}!`); } return ( <div> <MyClientComponent action={action} /> </div> ); }

Server functions called from client components can return data to the client component directly. This can be useful if you want to display a message to the user after the server function has been completed or if you want to display an error message if the server function failed.

"use client"; export default function MyClientComponent({ action }) { const [response, setResponse] = useState(null); const handleClick = async () => { const response = await action({ name: "John" }); setResponse(response); }; return ( <> <button onClick={handleClick}>Click me!</button> {response && <p>{response.message}</p>} </> ); }
import MyClientComponent from "./MyClientComponent"; export default function App() { async function action({ name }) { "use server"; console.log(`Server function called by ${name}!`); return { message: `Hello ${name}!` }; } return ( <div> <MyClientComponent action={action} /> </div> ); }

You can combine inline "use server" functions and inline "use client" components in the same file. This is useful when a server function and the client UI that consumes it are closely related.

import { useState, useTransition } from "react"; const greeting = "Hello"; async function greet(name) { "use server"; return `${greeting}, ${name}!`; } function Greeter() { "use client"; const [message, setMessage] = useState(""); const [, startTransition] = useTransition(); return ( <div> <button onClick={() => startTransition(async () => { setMessage(await greet("World")); }) } > Greet </button> {message && <p>{message}</p>} </div> ); } export default function App() { return <Greeter />; }

Both the server function and the client component are extracted into separate modules automatically. The server function can capture variables from the module scope, and the client component can call the server function directly.

A server function can define an inline "use client" component and return it as rendered JSX. The client component will be serialized through the RSC protocol and hydrated on the client, making it fully interactive.

import { useState, useTransition } from "react"; async function createCounter(initialCount) { "use server"; function Counter({ start }) { "use client"; const [count, setCount] = useState(start); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); } return <Counter start={initialCount} />; } function Shell() { "use client"; const [content, setContent] = useState(null); const [, startTransition] = useTransition(); return ( <div> <button onClick={() => startTransition(async () => setContent(await createCounter(0))) }> Create Counter </button> {content} </div> ); } export default function App() { return <Shell />; }

The createCounter server function defines a Counter client component, renders it with the given props, and returns the element. The framework extracts the nested directives into separate modules through a chain of virtual modules, so everything works from a single file.

You can define inline "use server" functions inside a file that already has a top-level "use client" directive. The server functions are automatically extracted from the client module, so you can keep related server logic and client UI together in a single file.

"use client"; import { useState, useTransition } from "react"; export default function TodoApp() { const [items, setItems] = useState([]); const [, startTransition] = useTransition(); async function addItem(text) { "use server"; return { id: Date.now(), text }; } return ( <div> <button onClick={() => startTransition(async () => { const item = await addItem(`item-${items.length}`); setItems((prev) => [...prev, item]); }) } > Add </button> <ul> {items.map((item) => ( <li key={item.id}>{item.text}</li> ))} </ul> </div> ); }

The file is treated as a client component because of the top-level "use client" directive, but the addItem function is extracted into a separate server module. This works the same way as defining the server function in a standalone "use server" file — the framework handles the extraction automatically.