Comment valider des nombres et booléens

Valide facilement nombres et booléens dans tes formulaires React avec Zod + Conform : UX fluide, typage strict, valeurs par défaut et zéro string vide côté serveur.

4 min read
Déverrouillez votre potentiel

avec React Router 7

Vous en avez marre de...

❌ perdre du temps à chercher des informations éparpillées
❌ ne pas avoir de retour sur votre progression
Assistant IA spécialisé

Posez vos questions 24/7 à notre IA experte en React Router 7

Quiz interactifs

Validez vos acquis avec des quiz personnalisés et un feedback instantané

9 modules
45 leçons
Accès à vie
299.00
-50%

Valider des nombres et booléens avec Zod + Conform

Jusqu’ici, on a validé uniquement des chaînes de caractères : e-mail, mot de passe, prénom… Mais les formulaires « réels » contiennent aussi :

  • des nombres (âge, quantité, prix, pourcentage) ;
  • des booléens (case à cocher « actif », « j’accepte les CGV », etc.).

Voyons comment intégrer ces deux types en gardant la DX/UX type-safe offerte par Zod et Conform.


1. Étendre le schéma Zod

Le schéma reste la source de vérité. On y ajoute age en number et active en boolean :

app/schemas/user.ts
1
import { z } from "zod"
2
3
export const userCreateSchema = z.object({
4
action: z.literal("create-user"),
5
firstName: z.string().min(2, "2 caractères mini"),
6
lastName: z.string().min(2),
7
age: z.number({ required_error: "L’âge est requis" }),
8
active: z.boolean().default(false), // ← coche facultative
9
})
10
11
export const userUpdateSchema = userCreateSchema.extend({
12
action: z.literal("update-user"),
13
})
14
15
export const userActionSchema = z.discriminatedUnion(
16
"action",
17
[userCreateSchema, userUpdateSchema],
18
)

Points clés :

  • z.number() assure qu’on reçoit bien un nombre (pas une string vide ou NaN).
  • default(false) force active à false si la checkbox n’est pas cochée : on ne stocke plus null/undefined.

2. Rappels HTML : tout arrive comme string

Un <input type="number" value="42" /> ou une <input type="checkbox" checked …> envoient toujours des strings dans le FormData :

1
firstName=Alice
2
age=42 ← "42" (string)
3
active=on ← "on" (string) ou… rien du tout si décochée

C’est Zod qui transforme "42"number et "on"boolean.


3. Champ <input type="number">

Rien de spécial côté React : on réutilise notre Field générique.

app/routes/users.$userSlug.tsx {55-68}
1
<Field
2
labelProps={{ children: "Âge" }}
3
inputProps={{
4
...getInputProps(fields.age, { type: "number" }),
5
min: 0,
6
}}
7
errors={fields.age.errors}
8
/>
  • getInputProps transmet automatiquement defaultValue={user?.age}.
  • Si l’utilisateur tape « abc », Conform bloque l’envoi ; si l’on reçoit "123", Zod cast en 123.

4. Champ checkbox réutilisable

Une checkbox nécessite un composant un peu plus riche :

  • convertir « on / undefined » en booléen ;
  • gérer aria-invalid ;
  • afficher les erreurs.
app/components/CheckboxField.tsx
1
import { useInputControl } from "@conform-to/react"
2
import { useId } from "react"
3
import { Checkbox } from "./ui/checkbox" // composant Radix stylé
4
import { ErrorList } from "./ErrorList"
5
6
export function CheckboxField({ labelProps, buttonProps, errors }: {...}) {
7
const id = buttonProps.id ?? useId()
8
const input = useInputControl({
9
key: buttonProps.key,
10
name: buttonProps.name,
11
formId: buttonProps.form,
12
initialValue: buttonProps.defaultChecked ? "on" : undefined,
13
})
14
15
return (
16
<div className="flex gap-2">
17
<Checkbox
18
{...buttonProps}
19
id={id}
20
checked={input.value === "on"}
21
onCheckedChange={(state) => input.change(state.valueOf() ? "on" : "")}
22
aria-invalid={errors?.length ? true : undefined}
23
aria-describedby={errors?.length ? `${id}-error` : undefined}
24
type="button"
25
/>
26
<label htmlFor={id} {...labelProps} />
27
<ErrorList id={`${id}-error`} errors={errors} />
28
</div>
29
)
30
}

Pourquoi ne pas utiliser un simple <input type="checkbox" /> ? Parce qu’on veut une case stylée, contrôlée et accessible sans dupliquer le boilerplate.


5. Brancher la checkbox dans le formulaire

app/routes/users.$userSlug.tsx {90-104}
1
<CheckboxField
2
labelProps={{ children: "Actif" }}
3
buttonProps={{
4
...getInputProps(fields.active, { type: "checkbox" }),
5
}}
6
errors={fields.active.errors}
7
/>

Si la case est décochée :

  • aucune key active n’arrive dans le FormData
  • Zod applique le default(false)
  • la base de données reçoit active = false.

6. Mettre à jour le backend… sans douleur

Grâce aux types inférés :

app/server/users.server.ts {48-61}
1
export async function addUser({
2
firstName, lastName, age, active // ← tout est déjà tipé
3
}: z.infer<typeof userCreateSchema>) {
4
const slug = createSlug({ firstName })
5
/* … */
6
const newUser: User = {
7
id: db.users.length + 1,
8
firstName,
9
lastName,
10
slug,
11
age,
12
active,
13
}
14
db.users.push(newUser)
15
return { slug }
16
}

Aucun changement manuel : dès qu’on ajoute un champ dans le schéma, la signature de la fonction se met à jour et TypeScript protège le serveur.


7. Résultat : UX & DX solides

  • Validation client immédiate (Conform) Saisie « -1 » ⇒ message rouge sous le champ
  • Validation serveur (Zod + superRefine) Slug déjà pris ⇒ erreur renvoyée, focus sur le prénom
  • Pas de undefined ou de string vide : la donnée arrive prête à persister.

Points clés

  1. Tout arrive en string dans FormData. C’est Zod qui caste vers number ou boolean.
  2. z.number() + z.boolean().default(false) couvrent 90 % des cas métier.
  3. Une checkbox décochée n’envoie rien : définis une valeur par défaut.
  4. useInputControl facilite la conversion "on"boolean.
  5. Grâce aux types générés, le serveur n’a aucun patch manuel à prévoir.
Premium
Quiz interactif
Testez vos connaissances et validez votre compréhension du module avec notre quiz interactif.
1

Comprendre les concepts fondamentaux

Quelle est la principale différence entre les composants client et serveur dans React ?

Les composants client s'exécutent uniquement dans le navigateur
Les composants serveur peuvent utiliser useState
Les composants client sont plus rapides
Il n'y a aucune différence significative
2

Optimisation des performances

Quelle technique est recommandée pour éviter les rendus inutiles dans React ?

Utiliser React.memo pour les composants fonctionnels
Ajouter plus d'états locaux
Éviter d'utiliser les props
Toujours utiliser les class components
3

Architecture des données

Quel hook permet de gérer les effets de bord dans un composant React ?

useEffect
useState
useMemo
useContext
4

Gestion des erreurs

Comment implémenter la gestion des erreurs pour les requêtes API dans React ?

Utiliser try/catch avec async/await
Ignorer les erreurs
Toujours afficher un message d'erreur
Rediriger l'utilisateur
5

Déploiement et CI/CD

Quelle est la meilleure pratique pour déployer une application React en production ?

Utiliser un service CI/CD comme GitHub Actions
Copier les fichiers manuellement via FTP
Envoyer le code source complet
Ne jamais mettre à jour l'application

Débloquez ce quiz et tous les autres contenus premium en achetant ce cours