Svelte native формы

Нативные формы в SvelteKit строятся вокруг стандартного HTML-механизма <form>, но расширяются возможностями фреймворка: серверные обработчики, прогрессивное улучшение, реактивность и управление состоянием без сторонних библиотек. Основной принцип — использование встроенного поведения браузера с постепенным добавлением клиентской логики.

Ключевое отличие от SPA-подхода заключается в том, что форма может корректно работать даже без JavaScript. При этом при наличии JS включается механизм перехвата и оптимизации запросов.


Базовая форма и серверный обработчик

Форма отправляет данные на сервер через POST, где они обрабатываются в +page.server.js или +server.js.

<form method="POST">
  <input name="email" type="email" required />
  <button type="submit">Отправить</button>
</form>

Серверная логика:

export const actions = {
  default: async ({ request }) => {
    const data = await request.formData();
    const email = data.get('email');

    if (!email) {
      return { error: 'Email обязателен' };
    }

    return { success: true };
  }
};

Особенности:

  • formData() — стандартный Web API
  • возвращаемые значения автоматически передаются в страницу
  • поддержка нескольких actions через именованные обработчики

Работа с результатами формы

Результат выполнения action доступен через form в компоненте страницы:

<script>
  export let form;
</script>

{#if form?.error}
  <p>{form.error}</p>
{/if}

{#if form?.success}
  <p>Успешно отправлено</p>
{/if}

Состояние формы:

  • undefined — форма не отправлялась
  • объект с данными — результат выполнения

Прогрессивное улучшение (use:enhance)

SvelteKit предоставляет директиву use:enhance, которая превращает стандартную форму в SPA-поведение:

<script>
  import { enhance } from '$app/forms';
</script>

<form method="POST" use:enhance>
  <input name="name" />
  <button>Сохранить</button>
</form>

Что происходит:

  • предотвращается перезагрузка страницы
  • запрос выполняется через fetch
  • результат обновляет состояние компонента

Дополнительно можно кастомизировать:

<form method="POST" use:enhance={({ result }) => {
  if (result.type === 'success') {
    console.log('Успех');
  }
}}>

Валидация данных

Клиентская валидация

Используются стандартные HTML-атрибуты:

<input type="email" required />
<input minlength="6" />

Серверная валидация

if (!email.includes('@')) {
  return { error: 'Некорректный email' };
}

Важно:

  • серверная валидация обязательна
  • клиентская — только для UX

Сохранение введённых данных

После ошибки форма может потерять введённые значения. Для восстановления:

return {
  error: 'Ошибка',
  values: { email }
};

В компоненте:

<input name="email" value={form?.values?.email ?? ''} />

Работа с несколькими действиями

Можно определять несколько обработчиков:

export const actions = {
  login: async ({ request }) => { ... },
  register: async ({ request }) => { ... }
};

В форме:

<form method="POST">
  <button name="intent" value="login">Войти</button>
  <button name="intent" value="register">Регистрация</button>
</form>

Обработка ошибок HTTP

Можно выбрасывать ошибки:

import { fail } from '@sveltejs/kit';

return fail(400, {
  error: 'Неверные данные'
});

Особенности:

  • fail сохраняет данные формы
  • статус-код влияет на поведение клиента

Redirect после отправки

import { redirect } from '@sveltejs/kit';

throw redirect(303, '/dashboard');

Используется:

  • после успешной авторизации
  • для предотвращения повторной отправки формы

Файлы и multipart формы

<form method="POST" enctype="multipart/form-data">
  <input type="file" name="avatar" />
</form>

На сервере:

const file = data.get('avatar');

Особенности:

  • File объект содержит имя, размер, тип
  • можно использовать потоковую обработку

Использование fetch вручную

Иногда требуется полный контроль:

<script>
  async function submitForm(e) {
    e.preventDefault();

    const formData = new FormData(e.target);

    const res = await fetch('/endpoint', {
      method: 'POST',
      body: formData
    });

    const result = await res.json();
  }
</script>

<form on:submit={submitForm}>

Состояние загрузки

С use:enhance:

<script>
  import { enhance } from '$app/forms';

  let loading = false;
</script>

<form method="POST" use:enhance={() => {
  loading = true;
  return async ({ upd ate }) => {
    await update();
    loading = false;
  };
}}>
  <button disabled={loading}>
    {loading ? 'Загрузка...' : 'Отправить'}
  </button>
</form>

Повторная отправка и идемпотентность

Проблема:

  • пользователь может отправить форму несколько раз

Решения:

  • блокировка кнопки
  • серверные проверки
  • уникальные токены

Работа с cookies и сессиями

export const actions = {
  login: async ({ request, cookies }) => {
    cookies.se t('session', 'token', {
      path: '/',
      httpOnly: true
    });

    return { success: true };
  }
};

Интеграция с UI библиотеками

При использовании UI-библиотек (например, кастомных компонентов):

<Input name="email" bind:value />

Важно:

  • компонент должен проксировать name
  • должен поддерживать value

Иначе formData не получит данные.


Динамические формы

Можно генерировать поля:

{#each fields as field}
  <input name={field.name} />
{/each}

Сервер:

for (const [key, value] of data.entries()) {
  console.log(key, value);
}

Безопасность форм

Ключевые аспекты:

  • CSRF защита (встроена через origin checks)
  • валидация на сервере
  • ограничение размера файлов
  • фильтрация данных

Nested формы и ограничения

HTML не поддерживает вложенные формы. Решения:

  • разделение на компоненты
  • управление через JS

Оптимизация UX

  • мгновенная валидация
  • отображение ошибок рядом с полями
  • сохранение состояния
  • индикаторы загрузки

Расширенные возможности enhance

use:enhance(({ form, data, cancel }) => {
  if (!form.checkValidity()) {
    cancel();
  }
});

Позволяет:

  • отменять отправку
  • изменять данные перед отправкой
  • логировать события

Работа с GET формами

<form method="GET">
  <input name="q" />
</form>

Результат:

  • параметры добавляются в URL
  • удобно для поиска и фильтрации

Связь с load функциями

После отправки формы можно обновить данные:

export const load = async ({ fetch }) => {
  const res = await fetch('/api/data');
  return { data: await res.json() };
};

enhance автоматически вызывает обновление.


Управление фокусом и доступность

После ошибки:

<input autofocus />

или программно:

  • фокус на первое невалидное поле
  • aria-атрибуты для ошибок

Рекомендации по структуре

  • минимальная логика на клиенте
  • максимум обработки на сервере
  • разделение UI и бизнес-логики
  • использование стандартов HTML

Типичные ошибки

  • отсутствие name у input
  • неправильный method
  • отсутствие серверной валидации
  • потеря состояния формы
  • неправильная работа с enhance

Паттерны использования

  1. CRUD формы
  2. Аутентификация
  3. Фильтры и поиск
  4. Загрузка файлов
  5. Многошаговые формы

Многошаговые формы

Состояние хранится:

  • в sessionStorage
  • в URL
  • на сервере

Каждый шаг — отдельная форма или состояние.


Сравнение с клиентскими библиотеками

Преимущества нативного подхода:

  • меньше кода
  • лучшая SEO
  • работа без JS
  • встроенная безопасность

Недостатки:

  • меньше абстракций
  • больше ручного контроля

Расширение через middleware

Можно добавлять обработку:

export const handle = async ({ event, resolve }) => {
  // логика перед формой
  return resolve(event);
};

Интеграция с API

Форма может работать напрямую с API:

await fetch('/api/submit', { method: 'POST' });

Или через actions — предпочтительный способ.


Производительность

  • формы не требуют hydration
  • меньше JS на клиенте
  • быстрый TTFB при SSR

Взаимодействие с store

import { writable } from 'svelte/store';

export const formState = writable({});

Используется для сложных форм.


Итоговая модель

SvelteKit реализует формы как:

  • стандарт HTML
  • серверная логика
  • опциональный JS слой

Это позволяет строить масштабируемые, быстрые и устойчивые интерфейсы без избыточных зависимостей.