Server Actions
Core Patterns
- Define Actions: Use
defineActionwith Zod schema validation for type-safe server-side form handling - Form Integration: Bind actions directly to HTML form
actionattributes for progressive enhancement - Client Enhancement: Override form submit with
actions.*()calls for JavaScript-enhanced UX - Authentication Pattern: Access cookies and session data inside action handlers for auth flows
Type-safe server actions for form handling
When to Read This
- Building forms with server-side validation
- Handling form submissions
- Progressive enhancement
Define Actions
// src/actions/index.ts
import { defineAction } from "astro:actions";
import { z } from "astro:schema";
export const server = {
newsletter: defineAction({
accept: "form",
input: z.object({
email: z.string().email(),
}),
handler: async ({ email }) => {
await subscribeToNewsletter(email);
return { success: true };
},
}),
login: defineAction({
accept: "form",
input: z.object({
email: z.string().email(),
password: z.string().min(8),
}),
handler: async ({ email, password }, context) => {
const user = await authenticateUser(email, password);
if (!user) throw new Error("Invalid credentials");
context.cookies.set("auth_token", generateToken(user), {
httpOnly: true,
secure: true,
});
return { user };
},
}),
};
Use in Forms
---
import { actions } from 'astro:actions';
---
<form method="POST" action={actions.newsletter}>
<input type="email" name="email" required />
<button type="submit">Subscribe</button>
</form>
With Client-Side Enhancement
<form method="POST" action={actions.login}>
<input type="email" name="email" required />
<input type="password" name="password" required />
<button type="submit">Login</button>
</form>
<script>
import { actions } from 'astro:actions';
const form = document.querySelector('form');
form?.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(form);
const { data, error } = await actions.login(formData);
if (error) {
alert(error.message);
} else {
window.location.href = '/dashboard';
}
});
</script>