Server Functions
Call server-side logic directly from components with RPC-like functions. Dinou uniquely allows Server Functions to return rendered Components, not just JSON data.
Server Functions ("use server")
Define functions with the "use server" directive to execute server-side logic directly from your components. Unlike traditional RPC, these functions can return fully rendered React Components.
// src/server-functions/get-post.jsx
"use server";
import db from "./db";
import Post from "@/components/post.jsx";
export async function getPost(postId) {
const data = await db.query("SELECT * FROM posts WHERE id = ?", [postId]);
// 🪄 Returns a rendered Component, not just JSON
return <Post post={data} />;
}Server Functions always execute on the server, even when called from Client Components. This keeps sensitive logic and database credentials secure.
Server Actions (Form Mutations)
Server Functions can also be used as Server Actions by passing them to the action prop of a <form>. This allows you to handle form submissions and data mutations directly on the server without creating API endpoints manually.
The function receives a FormData object containing input values automatically.
Forms work natively even before JavaScript loads on the client.
Use getContext to redirect the user after a successful mutation.
1. Define the Action
Create a Server Function that extracts data from FormData and performs the mutation.
// src/actions/create-post.js
"use server";
import { getContext } from "dinou";
import { addPost } from "@/db/posts.js";
export async function createPost(formData) {
const context = getContext();
// 1. Extract data
const title = formData.get("title");
const content = formData.get("content");
// 2. Mutate (Save to DB)
await addPost({ title, content });
// 3. Redirect
context?.res?.redirect("/posts");
}2. Use in Client Components
Pass the function to the form action. You can use the new React 19 useFormStatus hook to show pending states.
// src/new-post/page.jsx
"use client";
import { useFormStatus } from "react-dom";
import { createPost } from "@/actions/create-post";
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending} className="btn-primary">
{pending ? "Saving..." : "Create Post"}
</button>
);
}
export default function Page() {
return (
<form action={createPost} className="flex flex-col gap-4">
{/* The 'name' attribute is required for FormData extraction */}
<input name="title" placeholder="Title" required className="input" />
<textarea name="content" placeholder="Content" required className="textarea" />
<SubmitButton />
</form>
);
}