You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NextJS+Zod:如何让shadcn Form验证通过时输入框变绿

解决shadcn Form组件验证通过时添加绿色样式的方法

shadcn的Form组件基于React Hook Form实现,我们可以借助React Hook Form提供的表单状态,判断输入框验证状态后动态添加自定义样式类。

步骤1:定义验证通过的样式类

先在项目的CSS文件中添加自定义绿色样式:

.input-success {
  border-color: #10b981; /* 绿色边框 */
  box-shadow: 0 0 0 1px #10b981; /* 可选:添加绿色外发光效果 */
}

步骤2:在FormField中判断验证状态并添加类

通过FormField的children函数,获取表单的touchedFields(是否触碰过输入框)、errors(是否有验证错误)以及输入框的值,判断是否满足验证通过的条件,再动态给FormInput添加自定义类。

示例代码:

import { Form, FormField, FormInput, FormLabel, FormMessage } from '@/components/ui/form'
import { useForm } from 'react-hook-form'

type FormValues = {
  email: string
  password: string
}

export function AuthForm() {
  const form = useForm<FormValues>()

  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit((values) => console.log(values))} className="space-y-6">
        {/* 邮箱输入框 */}
        <FormField
          control={form.control}
          name="email"
          rules={{ 
            required: '邮箱不能为空', 
            pattern: { value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: '请输入有效的邮箱格式' } 
          }}
          children={({ field, formState }) => {
            // 验证通过条件:已触碰输入框 + 无错误 + 输入内容不为空
            const isSuccess = formState.touchedFields.email && !formState.errors.email && field.value.trim() !== ''
            
            return (
              <div className="space-y-2">
                <FormLabel htmlFor="email">邮箱</FormLabel>
                <FormInput
                  id="email"
                  {...field}
                  className={isSuccess ? 'input-success' : ''}
                />
                <FormMessage />
              </div>
            )
          }}
        />

        {/* 密码输入框 */}
        <FormField
          control={form.control}
          name="password"
          rules={{ required: '密码不能为空', minLength: { value: 6, message: '密码至少6位' } }}
          children={({ field, formState }) => {
            const isSuccess = formState.touchedFields.password && !formState.errors.password && field.value.trim() !== ''
            
            return (
              <div className="space-y-2">
                <FormLabel htmlFor="password">密码</FormLabel>
                <FormInput
                  id="password"
                  type="password"
                  {...field}
                  className={isSuccess ? 'input-success' : ''}
                />
                <FormMessage />
              </div>
            )
          }}
        />

        <button type="submit" className="w-full py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700">提交</button>
      </form>
    </Form>
  )
}

关键逻辑说明

  • formState.touchedFields[字段名]:确保输入框已经被用户触碰过(比如点击输入后离开),避免页面刚加载就显示绿色样式
  • !formState.errors[字段名]:确认当前输入没有验证错误
  • field.value.trim() !== '':确保输入框有实际内容(可选,若你的业务允许空值验证通过,可去掉此判断)

扩展:封装可复用的成功状态输入框

如果多个输入框都需要这个效果,可以封装一个自定义组件来复用逻辑:

import { FormInput, FormInputProps } from '@/components/ui/form'
import { FieldValues, UseFormReturn } from 'react-hook-form'

type SuccessFormInputProps<T extends FieldValues> = FormInputProps & {
  form: UseFormReturn<T>
  fieldName: keyof T
}

export function SuccessFormInput<T extends FieldValues>({ form, fieldName, ...props }: SuccessFormInputProps<T>) {
  const { touchedFields, errors } = form.formState
  const fieldValue = form.watch(fieldName)
  const isSuccess = touchedFields[fieldName] && !errors[fieldName] && String(fieldValue).trim() !== ''

  return <FormInput {...props} className={`${isSuccess ? 'input-success' : ''} ${props.className || ''}`} />
}

使用时只需传入表单实例和字段名:

<FormField
  control={form.control}
  name="username"
  rules={{ required: '用户名不能为空' }}
  children={({ field }) => (
    <div className="space-y-2">
      <FormLabel htmlFor="username">用户名</FormLabel>
      <SuccessFormInput
        form={form}
        fieldName="username"
        id="username"
        {...field}
      />
      <FormMessage />
    </div>
  )}
/>

内容的提问来源于stack exchange,提问作者Beres

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 10:52:36