Typed Server Functions Called From Anywhere
Actions
Astro Actions are typed server functions that you can call from pages, components, islands, or plain `<form>`s — without hand-writing endpoints.
What you'll learn
- Define an action
- Call it from a form
- Call it from a client island
Astro Actions are a higher-level alternative to hand-rolling
endpoints. You write a typed function on the server; you call it
from anywhere — a <form>, an island, an Astro page.
Actions require server mode (or hybrid with the page non-prerendered).
Define an Action
// src/actions/index.ts
import { defineAction } from "astro:actions";
import { z } from "astro:schema";
export const server = {
saveContact: defineAction({
accept: "form", // accept FormData submissions
input: z.object({
email: z.string().email(),
message: z.string().min(1),
}),
handler: async ({ email, message }) => {
await db.contacts.insert({ email, message });
return { ok: true };
},
}),
}; Astro generates types so callers know what saveContact accepts
and what it returns.
Call From a Form
---
import { actions } from "astro:actions";
---
<form method="POST" action={actions.saveContact}>
<input name="email" type="email" required />
<textarea name="message" required></textarea>
<button>Send</button>
</form> The action={actions.saveContact} attribute points the form at
the right action. Astro handles validation, runs your handler,
and exposes the result via Astro.getActionResult() on the page.
Call From a Client Island
// In a React island
import { actions } from "astro:actions";
export default function NewsletterForm() {
async function onSubmit(e) {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const { data, error } = await actions.saveContact(formData);
if (error) console.error(error);
else alert("Thanks!");
}
return (
<form onSubmit={onSubmit}>
<input name="email" type="email" required />
<button>Subscribe</button>
</form>
);
} The same typed action, called from a fetch-like API on the client.
Why Use Actions vs Endpoints?
- Type safety end-to-end — Zod input + typed return value
- Less boilerplate — no
Response.jsonplumbing - Form-and-client friendly — one definition handles both
- Built-in validation — Astro returns errors automatically
For simple use cases or when you need raw control over the Response, plain endpoints are still fine.
Up Next
Code that runs before every request — middleware.
Middleware →