Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
React Hook Form と Zod によるフォームバリデーション
Search
SAW
November 07, 2025
Programming
0
29
React Hook Form と Zod によるフォームバリデーション
フロントエンド勉強会 in 大阪 の発表資料です。
SAW
November 07, 2025
Tweet
Share
More Decks by SAW
See All by SAW
PHP で form-data を POST 以外のメソッドで受け取るには?
azuki
0
49
PHP で学ぶ OAuth 入門
azuki
2
950
EditorConfig を使ってみよう
azuki
1
92
Symfony でサクッと作る REST API サーバー
azuki
1
220
Vite の Library Mode を使って Vue のコンポーネントをライブラリ化する
azuki
1
300
Laravel や Symfony で手っ取り早く OpenAPI のドキュメントを作成する
azuki
2
370
Provide/Inject で TypeScript の恩恵を受ける方法
azuki
3
160
GraphQL はいいぞ! ~Laravel で学ぶ GraphQL 入門~
azuki
1
390
OSS contributor への第一歩を踏み出すまでの物語
azuki
2
350
Other Decks in Programming
See All in Programming
それ、本当に安全? ファイルアップロードで見落としがちなセキュリティリスクと対策
penpeen
7
2.1k
re:Invent 2025 のイケてるサービスを紹介する
maroon1st
0
160
AgentCoreとHuman in the Loop
har1101
3
100
Go コードベースの構成と AI コンテキスト定義
andpad
0
160
ELYZA_Findy AI Engineering Summit登壇資料_AIコーディング時代に「ちゃんと」やること_toB LLMプロダクト開発舞台裏_20251216
elyza
2
1k
愛される翻訳の秘訣
kishikawakatsumi
3
370
組み合わせ爆発にのまれない - 責務分割 x テスト
halhorn
1
180
Implementation Patterns
denyspoltorak
0
150
チームをチームにするEM
hitode909
0
450
gunshi
kazupon
1
140
今こそ知るべき耐量子計算機暗号(PQC)入門 / PQC: What You Need to Know Now
mackey0225
3
270
0→1 フロントエンド開発 Tips🚀 #レバテックMeetup
bengo4com
0
480
Featured
See All Featured
The #1 spot is gone: here's how to win anyway
tamaranovitovic
1
890
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
170
The Cult of Friendly URLs
andyhume
79
6.8k
Applied NLP in the Age of Generative AI
inesmontani
PRO
4
2k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.3k
Marketing Yourself as an Engineer | Alaka | Gurzu
gurzu
0
110
Noah Learner - AI + Me: how we built a GSC Bulk Export data pipeline
techseoconnect
PRO
0
81
Effective software design: The role of men in debugging patriarchy in IT @ Voxxed Days AMS
baasie
0
190
Producing Creativity
orderedlist
PRO
348
40k
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
98
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.3k
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
Transcript
3FBDU)PPL'PSNͱ;PEʹΑΔ ϑΥʔϜόϦσʔγϣϯ ϑϩϯτΤϯυษڧձJOେࡕ 4"8
$(whoami) w ࢯ໊Ճ౻फҰ ࡀ w ϋϯυϧωʔϜ4"8 w 9 چ5XJUUFS
!B[VLJ@FBUFS w ؔͷ*5ΤϯδχΞίϛϡχςΟͷ͔͠ ࣗশ w େࡕࡏॅɾѪग़ w ಘҙ8FCΞϓϦέʔγϣϯ։ൃ w -BSBWFM 7VF w ॴଐ༗ݶձࣾΞϦʔϓ 2 ඇৗ༻ίοΫͷதʹ খ͞ͳίοΫ͞Μೖ͍ͬͯͳ͍ ࠓͷ໎ݴ
ϑΥʔϜόϦσʔγϣϯͷඞཁੑ w ϢʔβʔͷೖྗϛεΛϑΥʔϜͷૹ৴લʹݕग़ w ೖྗඞਢͷ߲͕ೖྗ͞Ε͍ͯͳ͍ w ϝʔϧΞυϨε༣ศ൪߸ͳͲͷೖྗܗ͕ࣜਖ਼͘͠ͳ͍ w ೖྗ͞Εͨจࣈ͕ࢦఆͷൣғʹऩ·͍ͬͯͳ͍ w
Ͳͷ߲ʹΤϥʔ͕͋Δ͔Ϣʔβʔʹࢹ֮తʹೝࣝͤ͞Δ w ೖྗΤϥʔͷ͋Δ߲ʹରͯ͠Τϥʔ༰ͷϝοηʔδΛදࣔ w ϢʔβʔΤϥʔϝοηʔδͷ༰Λ֬ೝͯ͠ଈ࠲ʹೖྗΛमਖ਼Ͱ͖Δ 3
ϑΥʔϜόϦσʔγϣϯͷ࣮ݱ w ϑΥʔϜͷ֤ϑΟʔϧυͷೖྗΛͦΕͧΕνΣοΫ w ֤ϑΟʔϧυʹରͯ͠όϦσʔγϣϯϧʔϧΛఆٛͯ͠ద༻ w ֤ϑΟʔϧυͷঢ়ଶ ݕূ݁ՌͳͲ ͷཧ͕ඞཁ w
Τϥʔ͕͋ΔϑΟʔϧυʹରͯ͠ΤϥʔϝοηʔδΛదʹදࣔ w ϑΟʔϧυͷݕূ݁Ռʹج͍ͮͯΤϥʔͷදࣔɾඇදࣔͷঢ়ଶͷཧ͕ඞཁ w ϑΥʔϜͷཧϥΠϒϥϦΛར༻͢Δ͜ͱͰ࣮Λ؆ૉԽ w ϑΥʔϜͷঢ়ଶཧ w όϦσʔγϣϯͱΤϥʔϝοηʔδͷද੍ࣔޚ 4
3FBDU)PPL'PSNͷ֓ཁ
3FBDU)PPL'PSNͱ w 3FBDUͰϑΥʔϜΛ؆୯ʹѻ͑ΔΑ͏ʹ͢ΔϥΠϒϥϦ w 3FBDUͷ)PPLTʹΑͬͯϑΥʔϜͷঢ়ଶཧόϦσʔγϣϯ͕Մೳ w ϑΥʔϜͷೖྗঢ়ଶͷอ࣋ w όϦσʔγϣϯͱΤϥʔϝοηʔδͷදࣔ w
ϑΥʔϜͷૹ৴ॲཧ 6
3FBDU)PPL'PSNͷ͍ํ w ϑΥʔϜϑΟʔϧυΛ3FBDU)PPL'PSNͰཧରͱͯ͠ొ w ϑΥʔϜૹ৴࣌ͷॲཧΛࢦఆ w ϑΥʔϜͷόϦσʔγϣϯͱΤϥʔͷදࣔ 7
ϑΥʔϜσʔλͷܕఆٛ w ϑΥʔϜͰѻ͏σʔλͷܕΛఆٛ w ఆٛͨ͠ܕΛuseForm()ͷܕҾʹࢦఆՄೳ w ϑΥʔϜϑΟʔϧυͷͷܕࢦఆ͕ՄೳʹͳΔ 8 import {
useForm } from 'react-hook-form'; interface FormInput { name: string; email: string; age: number; } useForm<FormInput>();
ϑΥʔϜϑΟʔϧυͷొ w VTF'PSNIPPL͔Βregister()Λऔಘ w ϑΥʔϜϑΟʔϧυΛ3FBDU)PPL'PSNͷཧରʹొ w register()ͷҾʹೖྗϑΥʔϜʹࢦఆ͢ΔҰҙͳ໊લΛࢦఆ w useForm()ͰܕҾΛࢦఆͨ͠߹ͦͷܕͷϓϩύςΟ໊ͷΈʹ੍͞ΕΔ w
ϑΥʔϜཁૉͰregister()Λ༻ 9 const { register } = useForm<FormInput>(); return ( <form> <input {...register('name')} /> <!-- 略 --> </form> );
ϑΥʔϜͷૹ৴ॲཧ w VTF'PSNIPPL͔ΒhandleSubmit()Λऔಘ w formཁૉͷonSubmitʹhandleSubmit()Λࢦఆ w handleSubmit()ͷҾʹϑΥʔϜૹ৴࣌ʹ࣮ߦ͢ΔؔΛࢦఆ 10 const {
register, handleSubmit } = useForm(); const onSubmit = (data: FormInput) => { // 送信時の処理 }; return ( <form onSubmit={handleSubmit(onSubmit)}> <!-- 略 --> </form> );
ϑΥʔϜͷόϦσʔγϣϯ w register()ͷୈҾʹόϦσʔγϣϯϧʔϧΛࢦఆ w ඞਢ࠷খ࠷େͳͲΛࢦఆՄೳ w ΤϥʔϝοηʔδͷઃఆՄೳ 11 const {
register } = useForm<FormInput>(); return ( <form> <input {...register('name', { required: { value: true, message: '必須項目です', } })} /> <!-- 略 --> </form> );
Τϥʔϝοηʔδͷදࣔ w VTF'PSNIPPL͔ΒformStateΛऔಘͯ͠errorsΛऔΓग़͢ w errorsʹͯ͢ͷϑΥʔϜϑΟʔϧυͷΤϥʔΛแؚ w register()Ͱࢦఆͨ͠ϑΟʔϧυ໊ͱerrorsͷϓϩύςΟ໊͕ରԠ 12 const {
/* 略 */ formState: { errors } } = useForm(); return ( <form> <input {...register('name', { /* 略 */ }) /> {errors.name && (<span>{errors.name.message}</span>)} </form> );
;PEͷ֓ཁ
;PEͱ w 5ZQF4DSJQU fi STUͳόϦσʔγϣϯϥΠϒϥϦ w ܕʹج͍ͮͯεΩʔϚΛఆٛ w จࣈྻͷΑ͏ͳ୯७ͳͷ͔Βෳࡶͳߏ·ͰॊೈʹఆٛՄೳ w
ఆٛͨ͠εΩʔϚʹج͍ͮͯೖྗΛݕূ w ಛఆͷϥΠϒϥϦϑϨʔϜϫʔΫʹґଘ͠ͳ͍ w 3FBDU)PPL'PSNҎ֎ͷϑΥʔϜϥΠϒϥϦʹར༻Մೳ w 7VFͳͲͷ3FBDUҎ֎ͷϑϨʔϜϫʔΫͰར༻Մೳ 14
;PEͷ͍ํ w ;PEͰόϦσʔγϣϯ͢ΔͨΊͷεΩʔϚΛఆٛ w όϦσʔγϣϯϧʔϧͳͲ߹Θͤͯఆٛ w ఆٛͨ͠εΩʔϚΛར༻ͯ͠ೖྗΛݕূ w Τϥʔϝοηʔδͷઃఆ 15
εΩʔϚͷఆٛ w ;PEͷϝιουΛར༻ͯ͠εΩʔϚͷܕΛఆٛ w string()number()ͳͲͰجຊܕΛදݱ w object()ͰΦϒδΣΫτΛදݱ w ҾͷΦϒδΣΫτͰ͞Βʹ;PEͷεΩʔϚͷܕఆ͕ٛՄೳ w
ೖΕࢠߏͷεΩʔϚͷఆٛՄೳ w array()ͰྻΛදݱ 16 import * as z from 'zod'; const UserSchema = z.object({ name: z.string(), email: z.email(), age: z.number(), });
ೖྗͷݕূ w parse()Λར༻ͯ͠εΩʔϚͷఆ͔ٛΒೖྗΛݕূ w όϦσʔγϣϯΤϥʔ͕͋Δ߹ZodError͕UISPX͞ΕΔ w safeParse()Λར༻ͯ͠ݕূ݁ՌͷΦϒδΣΫτΛऔಘ w safeParse()ͰZodErrorUISPX͞Εͳ͍ w
ݕূ݁ՌͷΦϒδΣΫτΛฦ٫ w ฦ٫ܕݕূޭɾࣦഊͷ݁ՌͷVOJPOܕ 17 try { UserSchema.parse({ name: 'foo', age: 31 }); } catch (error) { if (error instanceof z.ZodError) { // エラー処理 } }
Τϥʔϝοηʔδͷઃఆ w εΩʔϚఆٛͷؔͷҾͰΤϥʔϝοηʔδΛࢦఆՄೳ w ZodErrorͷmessageʹࢦఆͨ͠Τϥʔϝοηʔδ͕ઃఆ͞ΕΔ 18 z.email('Invalid email address'); z.number().positive('value
should be positive');
3FBDU)PPL'PSN ;PEͷ όϦσʔγϣϯ
3FBDU)PPL'PSNͱ;PEΛΈ߹ΘͤΔ w @hookform/resolversΛΠϯετʔϧ w useForm()ͷresolverʹzodResolverΛࢦఆ w @hookform/resolvers/zod͔ΒJNQPSU w useForm()ͷܕҾʹ;PEͷεΩʔϚ͔Βੜ͞ΕΔܕΛࢦఆ w
ϑΥʔϜσʔλͷܕΛ;PEͷεΩʔϚఆٛͱ࿈ಈͤ͞Δ 20
εΩʔϚఆ͔ٛΒܕΛੜ w ;PEͷinfer<>ͰεΩʔϚఆ͔ٛΒਪ͞ΕͨܕΛੜ w infer<>ͷܕҾʹ;PEͷεΩʔϚఆٛͷܕใΛࢦఆ w typeofԋࢉࢠͰܕใΛऔಘ w ੜ͞ΕͨܕϑΥʔϜͷೖྗܕͱͯ͠ར༻Մೳ 21
const UserSchema = z.object({ name: z.string(), email: z.email(), age: z.number(), }); type UserType = z.infer<typeof UserSchema>;
3FBDU)PPL'PSNʹ;PEΛઃఆ w useForm()ͷΦϓγϣϯͷresolverʹzodResolver()Λࢦఆ w zodResolver()ͷҾʹ;PEͰఆٛͨ͠εΩʔϚΦϒδΣΫτΛࢦఆ w useForm()ͷܕҾʹ;PEͰੜͨ͠ܕΛࢦఆ w register()ͷόϦσʔγϣϯϧʔϧͷઃఆෆཁ w
;PEͷεΩʔϚఆٛʹج͍ͮͯࣗಈతʹόϦσʔγϣϯ͞ΕΔ 22 import { zodResolver } from '@hookform/resolvers/zod'; const { /* 略 */ } = useForm<UserType>({ resolver: zodResolver(UserSchema), });
/FYUKTͰར༻͢Δࡍͷҙ w 3FBDU)PPL'PSNΫϥΠΞϯταΠυͰͷΈಈ࡞ w 3FBDU)PPL'PSNΛར༻͢Δίϯϙʔωϯτʹ'use client'͕ඞཁ w αʔόʔίϯϙʔωϯτͰར༻͠Α͏ͱ͢ΔͱϏϧυΤϥʔ͕ൃੜ 23 'use
client'; import { useForm } from "react-hook-form";
·ͱΊ w 3FBDU)PPL'PSNͷجૅతͳ͍ํʹ͍ͭͯઆ໌ w ;PEʹ͍ͭͯͷجૅతͳ͍ํʹ͍ͭͯઆ໌ w 3FBDU)PPL'PSNͱ;PEΛΈ߹Θͤͨར༻ํ๏ʹ͍ͭͯઆ໌ 24
͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠
એ w ϑϩϯτΤϯυΧϯϑΝϨϯεؔ։࠵༧ఆ w w ॴϚΠυʔϜ͓͓͔͞' w
ҰൠࢀՃνέοτ ·͍Ͳνέοτ ൃചத