Docs
Server Actions

Server Actions

Server Actions are asynchronous functions that execute on the server. They can be used in both Server and Client Components to handle form submissions and data mutations in Next.js applications.

Overview

http-react provides a hook for server actions: useAction

Server actions used with this hook should return a data property. Optionally, you can also return status and error for more predictable results. Use the actionData helper function for consistent return values.

Basic Server Action

Define a server action that returns structured data:

actions.ts
'use server'
 
import { actionData } from 'http-react'
 
export async function getProfile() {
  const profileData = {
    email: 'dany@email.com',
    name: 'Dany'
  }
 
  return actionData(profileData) // Returns { data, error, status }
}

Using useAction

Use useAction for things like form submissions. The server action parameter types is automatically inferred.

With FormData

Extract form data using the $form helper:

actions.ts
'use server'
 
import { actionData, $form } from 'http-react'
import db from './db'
 
type Entry = {
  title: string
  description: string
}
 
export async function createEntry(data: FormData) {
  const entry = $form<Entry>(data)
  const { title, description } = entry
 
  const newEntry = await db.create({ title, description })
 
  return actionData(newEntry)
}

Call the action using submit:

entry-form.tsx
'use client'
 
import { useAction } from 'http-react'
import { createEntry } from '@/actions'
 
export default function EntryForm() {
  const { submit, isPending } = useAction(createEntry)
 
  return (
    <div>
      <form action={submit}>
        <input
          name='title'
          type='text'
          placeholder='Entry title'
          required
          disabled={isPending}
        />
        <input
          name='description'
          type='text'
          placeholder='Entry description'
          required
          disabled={isPending}
        />
        <button>Save</button>
      </form>
 
      {isPending && <p>Creating entry...</p>}
    </div>
  )
}

Auto-Reset Form

Automatically reset the form after submission using formProps:

entry-form.tsx
'use client'
 
import { useAction } from 'http-react'
import { createEntry } from '@/actions'
 
export default function EntryForm() {
  const { isPending, formProps } = useAction(createEntry, {
    onSubmit: 'reset' // Or pass a function that receives the action payload
  })
 
  return (
    <div>
      <form {...formProps}>
        <input
          name='title'
          type='text'
          placeholder='Entry title'
          required
          disabled={isPending}
        />
        <input
          name='description'
          type='text'
          placeholder='Entry description'
          required
          disabled={isPending}
        />
        <button>Save</button>
      </form>
 
      {isPending && <p>Creating entry...</p>}
    </div>
  )
}

Explicit Parameters

Pass parameters directly instead of using FormData:

actions.ts
'use server'
 
import { actionData } from 'http-react'
import db from './db'
 
type Entry = {
  title: string
  description: string
}
 
// Single object argument instead of multiple parameters
export async function createEntry({ title, description }: Entry) {
  const newEntry = await db.create({ title, description })
  return actionData(newEntry)
}

Use the params option to provide typed parameters:

entry-form.tsx
'use client'
 
import { useAction } from 'http-react'
import { createEntry } from '@/actions'
 
export default function EntryForm() {
  const [newEntry, setNewEntry] = useState({
    title: '',
    description: ''
  })
 
  const { isPending, refresh } = useAction(createEntry, {
    params: newEntry, // TypeScript validates type compatibility
    onResolve() {
      // Reset state after success
      setNewEntry({
        title: '',
        description: ''
      })
    }
  })
 
  return (
    <div>
      <form action={refresh}>
        <input
          name='title'
          type='text'
          placeholder='Entry title'
          required
          disabled={isPending}
          value={newEntry.title}
          onChange={(e) => {
            setNewEntry({
              ...newEntry,
              title: e.target.value
            })
          }}
        />
        <input
          name='description'
          type='text'
          placeholder='Entry description'
          required
          disabled={isPending}
          value={newEntry.description}
          onChange={(e) => {
            setNewEntry({
              ...newEntry,
              description: e.target.value
            })
          }}
        />
        <button>Save</button>
      </form>
 
      {isPending && <p>Creating entry...</p>}
    </div>
  )
}

When using params, the form submission triggers the action with the provided parameters instead of FormData.