1. Server Actions и формы
В мире фронтенда формы — это как хлеб и масло: без них не обходится ни один сайт, где есть хоть какая-то интерактивность. Раньше (и до сих пор в большинстве проектов) отправка формы означала или "перезагрузку" страницы, или вызов fetch/axios и возню с обработкой состояния, ошибок, загрузки и так далее.
Next.js 15 с App Router и Server Actions предлагает совершенно новый опыт: теперь можно отправлять форму на сервер, буквально передав функцию (Server Action) как обработчик, и получать результат обратно — без ручного fetch, без возни с REST, и с полной интеграцией в реактивную модель Next.js.
Это не просто "ещё один способ", а целая философия: теперь сервер и клиент работают как единое целое, а форма становится не просто HTML-тегом, а настоящим мостом между пользователем и серверной логикой.
Вспоминаем Server Action
Server Action — это асинхронная функция, объявленная с директивой 'use server', которую можно вызывать прямо из формы. Она выполняется на сервере, имеет полный доступ ко всем серверным возможностям (БД, секреты, файловая система), и возвращает результат обратно в компонент.
// app/actions.js
'use server';
export async function addTodo(formData) {
// Здесь можно обращаться к базе, отправлять письма и т.д.
const title = formData.get('title');
// ...добавить todo в БД
}
Как связать форму и Server Action?
Всё просто: передаём функцию в атрибут action у тега <form>:
<form action={addTodo}>
<input name="title" />
<button type="submit">Добавить</button>
</form>
Теперь при отправке формы Next.js сам сериализует данные, отправит их на сервер, вызовет вашу функцию, и обновит компонент.
Внимание: Это работает только в Server/Client Components в папке app/, и требует Next.js 15.
2. Минимальный пример: форма добавления задачи
Давайте попробуем реализовать простейшую форму для добавления задачи в список дел.
Шаг 1. Создаём Server Action
// app/actions.js
'use server';
export async function addTodo(formData) {
const title = formData.get('title');
// Здесь могла бы быть запись в базу данных
console.log('Добавлена задача:', title);
// Можно вернуть что-то, если нужно
}
Шаг 2. Используем Server Action в форме
// app/page.jsx
import { addTodo } from './actions';
export default function HomePage() {
return (
<main>
<h1>Список задач</h1>
<form action={addTodo}>
<input name="title" placeholder="Новая задача" required />
<button type="submit">Добавить</button>
</form>
</main>
);
}
Что происходит?
— При отправке формы данные попадут в функцию addTodo на сервере.
— Форма не перезагрузит страницу (если это не Client Component).
— После выполнения Server Action компонент перерисуется (если есть связанные данные).
3. Как работает formData и что можно передавать
В Server Action первым аргументом всегда приходит объект FormData, который содержит все поля формы.
- formData.get('имя_поля') — получить значение поля (строка или файл).
- Можно использовать несколько полей, чекбоксы, select, file input и т.д.
Пример:
export async function handleForm(formData) {
const name = formData.get('name');
const email = formData.get('email');
// ...работаем с этими данными
}
4. Пример с несколькими полями
Давайте добавим больше полей: имя пользователя и email.
// app/actions.js
'use server';
export async function registerUser(formData) {
const name = formData.get('name');
const email = formData.get('email');
// Здесь могла бы быть регистрация пользователя
console.log('Регистрация:', name, email);
}
// app/register/page.jsx
import { registerUser } from '../actions';
export default function RegisterPage() {
return (
<form action={registerUser}>
<input name="name" placeholder="Имя" required />
<input name="email" type="email" placeholder="Email" required />
<button>Зарегистрироваться</button>
</form>
);
}
5. Как обновлять список после отправки формы
В реальных приложениях после отправки формы нужно обновить список задач, пользователей и т.д. В Next.js 15 App Router это делается автоматически, если вы используете Server Components и загружаете данные на сервере.
Пример: список задач
// app/actions.js
'use server';
let todos = [];
export async function addTodo(formData) {
const title = formData.get('title');
todos.push({ title });
}
// app/page.jsx
import { addTodo } from './actions';
async function getTodos() {
// В реальном мире — запрос к БД
return todos;
}
export default async function HomePage() {
const todos = await getTodos();
return (
<main>
<h1>Список задач</h1>
<form action={addTodo}>
<input name="title" placeholder="Новая задача" required />
<button type="submit">Добавить</button>
</form>
<ul>
{todos.map((todo, idx) => (
<li key={idx}>{todo.title}</li>
))}
</ul>
</main>
);
}
Важно:
— После отправки формы страница перерисуется (SSR), и список обновится.
— Если используете базу данных, данные будут актуальны.
6. Полезные нюансы
Как обработать ошибки и показать пользователю результат
Server Action может выбросить ошибку или вернуть значение. Для обработки ошибок и состояния формы рекомендуется использовать хуки useFormState и useFormStatus (см. следующую лекцию), но базовую обработку можно сделать так:
// app/actions.js
'use server';
export async function addTodo(formData) {
const title = formData.get('title');
if (!title || title.length < 3) {
throw new Error('Название задачи слишком короткое');
}
todos.push({ title });
}
В компоненте можно отобразить ошибку через отдельный стейт или с помощью useFormState (подробнее об этом — в следующей лекции).
Особенности и ограничения
- Server Action можно использовать только в App Router (папка app/), не работает в pages/.
- Форма должна быть обычной HTML-формой, не кастомной обработкой через onSubmit.
- Можно отправлять файлы! Просто используйте <input type="file" name="avatar" />, а на сервере получите файл через formData.
- Если нужна интерактивность до отправки (валидация, динамические поля), используйте Client Component или гибридный подход.
- Server Action нельзя передавать в props обычным компонентам — только в action формы.
Форма с загрузкой файла (аватар)
// app/actions.js
'use server';
export async function uploadAvatar(formData) {
const file = formData.get('avatar');
if (!file) throw new Error('Файл не выбран');
// Здесь file — это Blob (или File)
// Можно сохранить файл на диск, в облако и т.д.
console.log('Загружен файл:', file.name, file.size);
}
// app/profile/page.jsx
import { uploadAvatar } from '../actions';
export default function ProfilePage() {
return (
<form action={uploadAvatar} encType="multipart/form-data">
<input type="file" name="avatar" accept="image/*" />
<button>Загрузить аватар</button>
</form>
);
}
Как работает обновление страницы и SSR
- Когда вы отправляете форму с Server Action:
- Next.js выполняет функцию на сервере.
- После завершения действия происходит "перезапрос" страницы (SSR), и компонент получает свежие данные.
- Это похоже на старый добрый POST-Redirect-GET, но без ручных редиректов и сложностей.
- Если форма находится внутри Client Component, состояние можно контролировать вручную.
Советы и best practices
- Используйте Server Actions для логики, связанной с сервером: работа с БД, валидация, отправка писем.
- Давайте полям формы осмысленные имена: чтобы на сервере легко получать значения через formData.get('name').
- Если нужна обратная связь пользователю (ошибка, успех) — используйте useFormState или Client Component.
- Не храните важные данные в глобальных переменных на сервере (как в наших примерах с массивом): используйте БД!
- Для сложных форм можно комбинировать Server Actions с Client Components.
7. Типичные ошибки при работе с формами и Server Actions
Ошибка №1: Не указываете атрибут name у input.
Если у поля формы нет атрибута name, оно не попадёт в formData. Проверьте, что у каждого input есть name!
Ошибка №2: Пытаетесь передать Server Action в onSubmit.
Server Action работает только через action формы: <form action={serverAction}>, а не через <form onSubmit={serverAction}>.
Ошибка №3: Используете Server Action вне папки app/
Server Actions поддерживаются только в App Router (директория app/), а не в старом pages/.
Ошибка №4: Ожидаете, что состояние формы обновится автоматически в Client Component.
Если компонент клиентский, обновление состояния после Server Action нужно реализовать вручную.
Ошибка №5: Не обрабатываете ошибки на сервере.
Если Server Action выбрасывает ошибку, пользователь увидит "ничего" или дефолтную ошибку. Используйте useFormState или возвращайте статус.
Ошибка №6: Пытаетесь получить поля формы через event.target.value.
В Server Action нет доступа к event — только к formData, который приходит первым аргументом.
Ошибка №7: Забыли добавить encType="multipart/form-data" для загрузки файлов.
Без этого браузер не отправит файл корректно.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ