This page contains the complete setup for editing a transaction in Next.js 16, including the page, client form, and server-side Zod validation.
You can copy and use this code directly in your project.
Edit Transaction Page
import Link from "next/link";
import prisma from "@/lib/prisma";
import EditTransactionForm from "./EditTransactionForm";
type PageProps = {
params: {
id: string;
};
};
export default async function EditTransactionPage({ params }: PageProps) {
const { id } = await params;
const transaction = await prisma.transaction.findUnique({
where: { id: BigInt(id) },
});
if (!transaction) {
return (
<div>
<p>Transaction not found.</p>
<Link href="/transactions">Back to Transactions</Link>
</div>
);
}
return <EditTransactionForm transaction={transaction} />;
}
EditTransactionForm (Client Component)
"use client";
import Link from "next/link";
import { useActionState } from "react";
import { updateTransaction } from "@/app/actions/updateTransaction";
import { useEffect } from "react";
type Props = {
transaction: {
id: bigint;
amount: number | null;
type: string | null;
description: string | null;
};
};
type State = {
errors?: {
amount?: string[];
type?: string[];
description?: string[];
};
};
export default function EditTransactionForm({ transaction }: Props) {
const initialState: State = { errors: {} };
const [state, formAction] = useActionState(updateTransaction, initialState);
useEffect(() => {
if (state?.errors) {
console.log("Validation errors:", state.errors);
}
}, [state]);
return (
<form action={formAction}>
<input type="hidden" name="id" value={transaction.id.toString()} />
<input type="number" name="amount" defaultValue={transaction.amount ?? ""} />
<select name="type" defaultValue={transaction.type ?? ""}>
<option value="income">Income</option>
<option value="expense">Expense</option>
</select>
<input type="text" name="description" defaultValue={transaction.description ?? ""} />
<button type="submit">Update Transaction</button>
<Link href="/transactions">Cancel</Link>
</form>
);
}
Server Action with Zod Validation
"use server";
import prisma from "@/lib/prisma";
import { redirect } from "next/navigation";
import * as z from "zod";
const formSchema = z.object({
amount: z.number().positive(),
type: z.enum(["income", "expense"]),
description: z.string().min(5).max(255),
});
export async function updateTransaction(prevState: any, formData: FormData) {
const rawData = {
id: BigInt(formData.get("id") as string),
amount: parseFloat(formData.get("amount") as string),
type: formData.get("type") as string,
description: formData.get("description") as string,
};
const validatedResult = formSchema.safeParse(rawData);
if (!validatedResult.success) {
return { errors: validatedResult.error.flatten().fieldErrors };
}
await prisma.transaction.update({
where: { id: rawData.id },
data: validatedResult.data,
});
redirect("/transactions");
}