AstroShowcase
Forms Intermediate Astro 6.3

Astro Actions

Type-safe form handling and RPC calls — define server functions, call them from the client, get full TypeScript types end-to-end.

What are Astro Actions?

Astro Actions are a type-safe way to handle form submissions and server function calls. You define an action with an input schema (using Zod) and a handler function. Astro generates a matching client-side caller automatically — no API routes, no fetch() wiring, no manual type casting.

▶ Live Form Submission

This form uses Astro's same-page POST pattern — no JavaScript required for the submission. Astro Actions let you handle forms with full TypeScript type safety on both client and server.

See server-side handler code
if (Astro.request.method === 'POST') {
  const form = await Astro.request.formData()
  const name = form.get('name')
  // validate → process → return typed result
}

Setting up an Action

1

Create src/actions/index.ts

Define your actions in a dedicated file. Astro discovers them automatically.

// src/actions/index.ts
import { defineAction } from 'astro:actions'
import { z }            from 'astro:schema'

export const server = {
  sendMessage: defineAction({
    // Validate incoming form data with Zod
    input: z.object({
      name:    z.string().min(1).max(60),
      message: z.string().min(1).max(200),
    }),

    handler: async ({ name, message }) => {
      // runs on the server — db calls, emails, anything
      await saveMessageToDb({ name, message })
      return { ok: true, greeting: `Thanks, ${name}!` }
    },
  }),
}
2

Use the action in a form (progressive enhancement)

Add action={actions.sendMessage} to a standard HTML form. Astro serialises the result back to the page with zero JavaScript required.

---
import { actions } from 'astro:actions'
---
<form method="POST" action={actions.sendMessage}>
  <input name="name"    required />
  <textarea name="message" required></textarea>
  <button type="submit">Send</button>
</form>
3

Handle the result in the frontmatter

After the form posts, Astro populates Astro.getActionResult() with the typed return value from your handler.

---
import { actions }         from 'astro:actions'
import { isInputError }    from 'astro:actions'

const result = Astro.getActionResult(actions.sendMessage)

if (result?.data?.ok) {
  return Astro.redirect('/thank-you')
}
---

Client-side usage (JavaScript)

Actions also work from client-side scripts for richer UIs:

import { actions } from 'astro:actions'

const { data, error } = await actions.sendMessage({
  name:    'Ada',
  message: 'Hello from the client!',
})

if (error) {
  console.error(error.message)
} else {
  console.log(data.greeting) // "Thanks, Ada!"
}
Tip

Astro Actions handle CSRF automatically — no need for hidden tokens or security: { checkOrigin: false } workarounds.

Info

Actions require output: 'server' or output: 'hybrid'. They are not available in fully static builds.

Further reading