重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
safe-action-forms by next-safe-action/skills
npx skills add https://github.com/next-safe-action/skills --skill safe-action-forms| 方式 | 适用场景 |
|---|---|
useAction + 原生表单 | 简单表单,无需复杂的验证界面 |
useHookFormAction (RHF 适配器) | 具有字段级错误、变更/失焦时验证的复杂表单 |
useHookFormOptimisticAction | 需要乐观 UI 更新的 RHF 表单 |
"use client";
import { useAction } from "next-safe-action/hooks";
import { submitContact } from "@/app/actions";
export function ContactForm() {
const { execute, result, isPending } = useAction(submitContact);
return (
<form
onSubmit={(e) => {
e.preventDefault();
const fd = new FormData(e.currentTarget);
execute({
name: fd.get("name") as string,
email: fd.get("email") as string,
message: fd.get("message") as string,
});
}}
>
<input name="name" required />
<input name="email" type="email" required />
<textarea name="message" required />
{result.validationErrors && (
<p>{result.validationErrors.email?._errors?.[0]}</p>
)}
{result.serverError && <p>{result.serverError}</p>}
{result.data && <p>Message sent!</p>}
<button type="submit" disabled={isPending}>
{isPending ? "Sending..." : "Send"}
</button>
</form>
);
}
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
"use client";
import { useHookFormAction } from "@next-safe-action/adapter-react-hook-form/hooks";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { submitContact } from "@/app/actions";
const schema = z.object({
name: z.string().min(1, "Name is required"),
email: z.string().email("Invalid email"),
message: z.string().min(10, "Message must be at least 10 characters"),
});
export function ContactForm() {
const { form, handleSubmitWithAction, action } = useHookFormAction(
submitContact,
zodResolver(schema),
{
actionProps: {
onSuccess: () => toast.success("Message sent!"),
},
}
);
return (
<form onSubmit={handleSubmitWithAction}>
<input {...form.register("name")} />
{form.formState.errors.name && <p>{form.formState.errors.name.message}</p>}
<input {...form.register("email")} />
{form.formState.errors.email && <p>{form.formState.errors.email.message}</p>}
<textarea {...form.register("message")} />
{form.formState.errors.message && <p>{form.formState.errors.message.message}</p>}
{action.result.serverError && <p>{action.result.serverError}</p>}
<button type="submit" disabled={action.isPending}>
{action.isPending ? "Sending..." : "Send"}
</button>
</form>
);
}
| 包 | 入口点 | 导出内容 |
|---|---|---|
@next-safe-action/adapter-react-hook-form | 默认 | mapToHookFormErrors, 类型 |
@next-safe-action/adapter-react-hook-form/hooks | Hooks | useHookFormAction, useHookFormOptimisticAction, useHookFormActionErrorMapper |
每周安装量
57
代码仓库
首次出现
2026年3月6日
安全审计
安装于
cursor56
gemini-cli56
amp56
github-copilot56
codex56
kimi-cli56
| Approach | When to Use |
|---|---|
useAction + native form | Simple forms, no complex validation UI |
useHookFormAction (RHF adapter) | Complex forms with field-level errors, validation on change/blur |
useHookFormOptimisticAction | RHF forms with optimistic UI updates |
"use client";
import { useAction } from "next-safe-action/hooks";
import { submitContact } from "@/app/actions";
export function ContactForm() {
const { execute, result, isPending } = useAction(submitContact);
return (
<form
onSubmit={(e) => {
e.preventDefault();
const fd = new FormData(e.currentTarget);
execute({
name: fd.get("name") as string,
email: fd.get("email") as string,
message: fd.get("message") as string,
});
}}
>
<input name="name" required />
<input name="email" type="email" required />
<textarea name="message" required />
{result.validationErrors && (
<p>{result.validationErrors.email?._errors?.[0]}</p>
)}
{result.serverError && <p>{result.serverError}</p>}
{result.data && <p>Message sent!</p>}
<button type="submit" disabled={isPending}>
{isPending ? "Sending..." : "Send"}
</button>
</form>
);
}
"use client";
import { useHookFormAction } from "@next-safe-action/adapter-react-hook-form/hooks";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { submitContact } from "@/app/actions";
const schema = z.object({
name: z.string().min(1, "Name is required"),
email: z.string().email("Invalid email"),
message: z.string().min(10, "Message must be at least 10 characters"),
});
export function ContactForm() {
const { form, handleSubmitWithAction, action } = useHookFormAction(
submitContact,
zodResolver(schema),
{
actionProps: {
onSuccess: () => toast.success("Message sent!"),
},
}
);
return (
<form onSubmit={handleSubmitWithAction}>
<input {...form.register("name")} />
{form.formState.errors.name && <p>{form.formState.errors.name.message}</p>}
<input {...form.register("email")} />
{form.formState.errors.email && <p>{form.formState.errors.email.message}</p>}
<textarea {...form.register("message")} />
{form.formState.errors.message && <p>{form.formState.errors.message.message}</p>}
{action.result.serverError && <p>{action.result.serverError}</p>}
<button type="submit" disabled={action.isPending}>
{action.isPending ? "Sending..." : "Send"}
</button>
</form>
);
}
| Package | Entry Point | Exports |
|---|---|---|
@next-safe-action/adapter-react-hook-form | Default | mapToHookFormErrors, types |
@next-safe-action/adapter-react-hook-form/hooks | Hooks | useHookFormAction, useHookFormOptimisticAction, useHookFormActionErrorMapper |
Weekly Installs
57
Repository
First Seen
Mar 6, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
cursor56
gemini-cli56
amp56
github-copilot56
codex56
kimi-cli56
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
125,600 周安装
Java Spring开发最佳实践指南:Spring Boot 3.x微服务架构与REST API设计
217 周安装
system-info 系统信息技能 | 获取操作系统、Python版本、目录列表的脚本工具
219 周安装
Microsoft Teams自动化指南:使用Rube MCP与Composio工具包实现团队协作自动化
53 周安装
Hyvä主题Tailwind CSS编译工具 - Magento 2前端开发与生产构建自动化
221 周安装
交互式作品集设计指南:30秒吸引招聘者,提升个人品牌与转化率
216 周安装
参考文档创建器 - 基于技术栈智能生成ADR、指南和手册的自动化工具
220 周安装