Server Actions: No API Routes for Mutations
// app/actions.ts
'use server'
export async function createPost(formData: FormData) {
const session = await auth()
if (!session?.user) throw new Error('Unauthorized')
const title = formData.get('title') as string
if (!title || title.length < 3) return { error: 'Title too short' }
await db.post.create({ data: { title, authorId: session.user.id } })
revalidatePath('/posts')
redirect('/posts')
}
Progressive Enhancement (Works Without JS)
export default function NewPostPage() {
return (
<form action={createPost}>
<input name="title" required />
<button type="submit">Create</button>
</form>
)
}
useActionState
'use client'
export function PostForm() {
const [state, formAction, isPending] = useActionState(createPost, {})
return (
<form action={formAction}>
{state?.error && <p>{state.error}</p>}
<input name="title" />
<button disabled={isPending}>{isPending ? 'Saving...' : 'Create'}</button>
</form>
)
}
useOptimistic
'use client'
export function LikeButton({ post }) {
const [optimistic, addOptimistic] = useOptimistic(post.liked, (s) => !s)
async function like() { addOptimistic(null); await toggleLike(post.id) }
return <button onClick={like}>{optimistic ? 'Liked' : 'Like'}</button>
}
-> Encode form data with the Base64 Converter.