Skip to content
Star

Multi-Step Form Wizard con Validación de Esquemas Zod

Los flujos de registro empresarial, configuración de proyectos o checkouts complejos suelen dividirse en múltiples pasos para no saturar al usuario. Sin embargo, un desafío crítico es asegurar que cada paso valide sus campos de forma estricta antes de permitir avanzar, evitando estados inválidos en la base de datos.

Esta receta enseña a orquestar PxFormWizard en conjunto con Zod para una validación robusta y tipada por cada etapa del formulario.


Demostración Interactiva

Prueba avanzar sin completar los campos requeridos: PxFormWizard detendrá la navegación, mostrará una alerta descriptiva y focalizará el error. Al completar el último paso, se capturará el payload final.

Wizard de Registro con Validación por Paso

Patrones y Buenas Prácticas

1. safeParse en lugar de parse

Al validar formularios reactivos, evita que Zod arroje excepciones no controladas (throw). Utiliza safeParse():

ts
const result = accountSchema.safeParse(data)
if (!result.success) {
  // Retorna el primer mensaje de error para mostrar al usuario
  return result.error.issues[0]?.message
}
return true

2. Guardado de Progreso en localStorage

Para evitar frustraciones si el usuario recarga la página por accidente o pierde la conexión, puedes sincronizar el modelo del Wizard con useStorage de @vueuse/core:

ts
import { useStorage } from '@vueuse/core'

const formState = useStorage('praxis_onboarding_draft', {
  fullName: '',
  email: '',
  orgName: '',
  role: '',
  plan: 'pro'
})

// Al completar con éxito, limpias el borrador:
function onWizardComplete() {
  localStorage.removeItem('praxis_onboarding_draft')
}

3. Validación Asíncrona (Unicidad de Usuario o Subdominio)

Si requieres verificar si un nombre de usuario o subdominio ya existe en el servidor antes de permitir pasar al paso 2, la función validate de cada paso puede ser asíncrona:

ts
validate: async (data) => {
  const isAvailable = await checkOrgAvailability(data.orgName)
  if (!isAvailable) {
    return 'Este nombre de organización ya se encuentra en uso'
  }
  return true
}

Built with VitePress