References

Server Actions

Core Patterns

  • Define Actions: Use defineAction with Zod schema validation for type-safe server-side form handling
  • Form Integration: Bind actions directly to HTML form action attributes 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>

References