Формы на сайте: валидация, ошибки и удобство в коде
Практика3 минРедакция Innovialabs
Коротко
Хорошая форма проверяет данные дважды — на клиенте для удобства и на сервере для надёжности, по одной общей схеме, и показывает понятные ошибки рядом с полем.
Главное правило
Клиентская валидация — для удобства, серверная — для безопасности. Всё, что приходит из браузера, можно подделать, поэтому сервер обязан проверять данные сам. А проверка в браузере нужна, чтобы человек узнал об ошибке сразу, а не после отправки.
Лучше всего описать правила один раз в общей схеме и использовать её на обеих сторонах.
Начните с нативного HTML
Браузер многое умеет без JavaScript:
<label for="email">Email</label>
<input id="email" name="email" type="email" autocomplete="email" required>
<label for="phone">Телефон</label>
<input id="phone" name="phone" type="tel" autocomplete="tel" inputmode="tel">
type(email,tel,url,number,date) — правильная клавиатура на телефоне и базовая проверка.required,minlength,maxlength,pattern,min,max— простые правила без кода.autocomplete— браузер подставит сохранённые данные, это сильно ускоряет заполнение.<label>для каждого поля — placeholder не заменяет подпись.
Стандартные всплывающие подсказки браузера не всегда красивы, поэтому часто ставят novalidate на форму и выводят ошибки сами, сохраняя атрибуты для семантики.
Общая схема для клиента и сервера
В TypeScript-проектах удобно использовать библиотеку схем, например Zod:
import { z } from "zod";
export const leadSchema = z.object({
name: z.string().trim().min(2, "Укажите имя"),
email: z.string().trim().email("Проверьте email"),
message: z.string().trim().max(2000, "Слишком длинный текст"),
});
На клиенте схема даёт мгновенные ошибки, на сервере — та же проверка перед сохранением:
const result = leadSchema.safeParse(body);
if (!result.success) {
return Response.json(
{ errors: result.error.flatten().fieldErrors },
{ status: 422 },
);
}
Сервер возвращает ошибки по полям, чтобы клиент показал их в нужных местах. Проверки, которые возможны только на сервере (занят ли email, существует ли промокод), приходят тем же форматом.
Когда показывать ошибки
- Не при первом вводе символа — раздражает. Проверяйте поле при потере фокуса (
blur). - После первой ошибки проверяйте поле при каждом изменении, чтобы сообщение исчезло, как только всё исправлено.
- При отправке — проверьте всё, покажите ошибки и переведите фокус на первое неверное поле.
- Не стирайте введённые данные при ошибке сервера.
Доступные сообщения об ошибках
<input id="email" aria-invalid="true" aria-describedby="email-error">
<p id="email-error">Проверьте email: не хватает символа @</p>
- Текст ошибки рядом с полем и связан через
aria-describedby. aria-invalid="true"для неверных полей.- Не только красный цвет — добавьте текст или иконку.
- Сообщение объясняет, как исправить, а не просто «Неверное значение».
- Для длинных форм — сводка ошибок вверху со ссылками на поля.
Удобство, которое часто забывают
- Минимум полей: каждое лишнее поле снижает желание заполнять.
- Не ограничивайте формат там, где можно нормализовать: пробелы в телефоне уберите сами.
- Блокируйте кнопку на время отправки, чтобы не было дублей, и покажите состояние загрузки.
- Ясное сообщение об успехе: что произойдёт дальше.
Базовая защита от спама
- Honeypot: скрытое поле, которое человек не заполнит, а бот заполнит.
- Проверка времени: форма, отправленная через долю секунды после загрузки, подозрительна.
- Rate limiting по IP на сервере.
- CAPTCHA — только если остальное не помогает: она ухудшает опыт пользователей.
- Санитизация и экранирование данных перед выводом и записью в базу.