Astro Actions: форма с валидацией Zod
Определение Action с accept: form, валидация полей через Zod (astro/zod), вызов из HTML-формы и Astro.getActionResult().
Как использовать
- Скопируйте нужный фрагмент кода.
- Вставьте в свой проект и при необходимости измените под задачу.
- Проверьте зависимости и окружение (версии, переменные).
Обработка форм в Astro 5 через Actions: типобезопасность и валидация на сервере. Zod импортируется из astro/zod, Actions — из astro:actions. Для вызова action через атрибут формы страница должна быть on-demand (export const prerender = false или output: "server" / "hybrid").
Определение Action (docs.astro.build — Actions):
// src/actions/index.ts
import { defineAction } from "astro:actions";
import { z } from "astro/zod";
export const server = {
contact: defineAction({
accept: "form",
input: z.object({
name: z.string().min(2, "Имя минимум 2 символа"),
email: z.string().email("Некорректный email"),
message: z.string().min(10, "Сообщение минимум 10 символов"),
}),
handler: async (input) => {
const { name, email, message } = input;
// отправка email, запись в БД и т.д.
return {
success: true,
message: "Спасибо за обращение! Мы свяжемся с вами.",
};
},
}),
};
Форма и результат на странице (Call actions from HTML form action):
---
// src/pages/contacts.astro
import { actions } from "astro:actions";
const result = Astro.getActionResult(actions.contact);
---
{result?.data?.success && (
<p class="success">{result.data.message}</p>
)}
{result?.error && (
<p class="error">{result.error.message}</p>
)}
<form method="POST" action={actions.contact}>
<label for="name">Имя</label>
<input type="text" id="name" name="name" required />
{result?.error?.fields?.name && (
<span class="field-error">
{Array.isArray(result.error.fields.name) ? result.error.fields.name[0] : result.error.fields.name}
</span>
)}
<label for="email">Email</label>
<input type="email" id="email" name="email" required />
<label for="message">Сообщение</label>
<textarea id="message" name="message" rows="5" required></textarea>
<button type="submit">Отправить</button>
</form>
Вызов Action из клиентского скрипта (прогрессивное улучшение):
import { actions, isInputError } 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.contact(formData);
if (isInputError(error)) {
// ошибки полей в error.fields
return;
}
if (error) {
// серверная ошибка
return;
}
if (data) {
// успех: data.message и т.д.
}
});
Usage:
- Все Actions экспортируются из объекта
serverвsrc/actions/index.ts. - При
accept: "form"handler получает уже распарсенный и провалидированный объект (не сырой FormData). - Ошибки валидации содержат
error.fields(сообщения по имени поля); для проверки используйтеisInputError(error).
Notes:
⚠️ Страница с формой, вызывающей action через action={actions.contact}, должна быть отрендерена on-demand. В статическом режиме добавьте в frontmatter страницы export const prerender = false или используйте output: "server" / "hybrid".