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.
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
const form = await Astro.request.formData()
const name = form.get('name')
// validate → process → return typed result
}
Setting up an Action
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}!` }
},
}),
} 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> 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!"
}
Astro Actions handle CSRF automatically — no need for hidden tokens or
security: { checkOrigin: false } workarounds.
Actions require output: 'server' or output: 'hybrid'. They are not available
in fully static builds.