Next.js cookbook · Intermediate
Upload files with a Server Action
Let users upload an avatar through a form that works even before JavaScript loads.
Approach
Read the File from FormData in a Server Action, validate size and type (and sniff the magic bytes for images), then stream it to object storage under a generated key. Server Actions have a default body size limit (1 MB) — raise it deliberately in next.config or use presigned direct uploads for larger files.
Solution
// app/settings/actions.ts
"use server";
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
import { randomUUID } from "node:crypto";
import { revalidatePath } from "next/cache";
const s3 = new S3Client({});
const TYPES: Record<string, string> = { "image/png": "png", "image/jpeg": "jpg", "image/webp": "webp" };
export async function uploadAvatar(_prev: { error?: string }, formData: FormData) {
const session = await verifySession();
const file = formData.get("avatar");
if (!(file instanceof File) || file.size === 0) return { error: "Choose an image" };
if (file.size > 2 * 1024 * 1024) return { error: "Max 2 MB" };
const ext = TYPES[file.type];
if (!ext) return { error: "PNG, JPEG or WebP only" };
const bytes = Buffer.from(await file.arrayBuffer());
const key = `avatars/${session.userId}/${randomUUID()}.${ext}`;
await s3.send(new PutObjectCommand({ Bucket: process.env.BUCKET!, Key: key, Body: bytes, ContentType: file.type }));
await db.user.update({ where: { id: session.userId }, data: { avatarKey: key } });
revalidatePath("/settings");
return {};
}
// next.config.ts
const config = { experimental: { serverActions: { bodySizeLimit: "3mb" } } };
Pitfalls
- Trusting
file.typealone for security-sensitive processing (re-encode images withsharp). - Large uploads through Server Actions (memory, timeouts).
- Public buckets exposing every user's files.
Variations
- Presigned direct-to-S3 uploads for large files.
- UploadThing or similar services.
Further reading: Next.js: serverActions config
Reference: https://nextjs.org/docs/app/api-reference/config/next-config-js/serverActions
More Next.js recipes
- Add a Content Security Policy with nonces in Next.js
- Add dark mode to Next.js without a flash
- Add SEO metadata, Open Graph and sitemaps
- Avoid data-fetching waterfalls in Server Components
- Build a validated JSON API with Route Handlers
- Create encrypted session cookies in Next.js
- Handle errors and 404s in the App Router
- Handle forms with Server Actions and validation