Перейти к содержанию
Innovialabs

Формы на сайте: валидация, ошибки и удобство в коде

Практика3 мин

Коротко

Хорошая форма проверяет данные дважды — на клиенте для удобства и на сервере для надёжности, по одной общей схеме, и показывает понятные ошибки рядом с полем.

Главное правило

Клиентская валидация — для удобства, серверная — для безопасности. Всё, что приходит из браузера, можно подделать, поэтому сервер обязан проверять данные сам. А проверка в браузере нужна, чтобы человек узнал об ошибке сразу, а не после отправки.

Лучше всего описать правила один раз в общей схеме и использовать её на обеих сторонах.

Начните с нативного 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 — только если остальное не помогает: она ухудшает опыт пользователей.
  • Санитизация и экранирование данных перед выводом и записью в базу.

Частые вопросы

Нет. Её легко обойти, отправив запрос напрямую. Клиентская проверка — для удобства, а защиту данных обеспечивает только сервер.

Читайте также

Есть идея или задача? Расскажите нам

Ответим в течение рабочего дня и предложим решение. Консультация бесплатная.