How to validate one field of a Zod schema apart from the whole schema

I have a form with multiple inputs.

One is ‘hours’ and one is ‘price’.

I am using NextJS and server side actions.
What I do now is when someone clicks submit

  • Validate the whole form with Zod clientside
  • If valid, then send it to server, validate again and perform database mutation.

The hours has to be between 10 and 100. The scenario I am dealing with is as follows;

  • User fills in 5
  • Presses submit
  • Zod says no and the error ‘Hours should be between 10 and 100’ shows

This works so far. But then the user fills in 50 and goes to the next field. I want a validation to run onBlur so that the error message disappears when it’s valid now. Otherwise it’s confusing when input is 50 but errormessage says Hours should be between 10 and 100.

How to do this? Do I have to make a sperate schema for each field and call that onBlur?

Form:

    <form
              ref={formRef}
              action={async (formData) => {
                const validatedFields = validateFields(formData)

                const state = await createCard(validatedFields)
                if (state.status === 'success') {
                  formRef.current?.reset()
                  setOpen(false)
                  setClientErrorMessage(undefined)
                } else if (state.status === 'error') {
                  console.log(state)
                  setserverErrorMessage(state.message)
                }
              }}
            >
              <div className='mb-4'>
                <Label htmlFor='client_id' className='mb-2'>
                  Client
                </Label>
                <Select name='client_id' required>
                  <SelectTrigger className='w-[240px]'>
                    <SelectValue placeholder='Select client' />
                  </SelectTrigger>
                  <SelectContent>
                    {clients?.map((client) => (
                      <SelectItem key={client.id} value={client.id}>
                        {client.name}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
                {clientErrorMessage?.client_id && (
                  <p className='py-2 text-xs text-red-500'>
                    {clientErrorMessage.client_id}
                  </p>
                )}
              </div>
              <div className='mb-4'>
                <Label htmlFor='hours' className='mb-2'>
                  Hours
                </Label>
                <Input type='number' name='hours' id='hours' required />
                {clientErrorMessage?.hours && (
                  <p className='py-2 text-xs text-red-500'>
                    {clientErrorMessage.hours}
                  </p>
                )}
              </div>
              <div className='mb-4'>
                <Label htmlFor='price' className='mb-2'>
                  Price
                </Label>
                <div className='relative flex items-center max-w-2xl '>
                  <Euro className='absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2 transform' />
                  <Input
                    type='number'
                    name='price'
                    id='price'
                    required
                    className='pl-6'
                  />
                  {clientErrorMessage?.price && (
                    <p className='py-2 text-xs text-red-500'>
                      {clientErrorMessage.price}
                    </p>
                  )}
                </div>
              </div>
              <div className='mb-4 flex flex-col'>
                <div className='flex items-center'>
                  <Switch
                    checked={customEndDate}
                    className='mr-2'
                    onCheckedChange={handleSwitchChange}
                  />
                  <span className=''>Set custom end date</span>
                  <TooltipProvider>
                    <Tooltip>
                      <TooltipTrigger asChild>
                        <InfoCircledIcon className='ml-1' />
                      </TooltipTrigger>
                      <TooltipContent>
                        <p>By default cards are valid for one year</p>
                      </TooltipContent>
                    </Tooltip>
                  </TooltipProvider>
                </div>
                {customEndDate ? (
                  <>
                    <Label htmlFor='ends_at' className='my-2 mr-2'>
                      Valid until
                    </Label>
                    <input
                      aria-label='Date'
                      type='date'
                      id='ends_at'
                      name='ends_at'
                      required
                      defaultValue={formattedDate}
                    />
                  </>
                ) : (
                  <input
                    type='hidden'
                    aria-label='Date'
                    id='ends_at'
                    name='ends_at'
                    required
                    defaultValue={formattedDate}
                  />
                )}
              </div>
              <p aria-live='polite' className='sr-only'>
                {state?.message}
              </p>
              <FormError errorMessage={serverErrorMessage} />
              <DialogClose asChild>
                <Button variant='outline' className='mr-2'>
                  Cancel
                </Button>
              </DialogClose>
              <SubmitButton normal='Add card' going='Adding  card...' />
            </form>

Validatefields:

    const validateFields = (formData: FormData) => {
    const validatedFields = createSchema.safeParse({
      client_id: formData.get('client_id'),
      hours: Number(formData.get('hours')),
      hours_left: Number(formData.get('hours')),
      price: Number(formData.get('price')),
      ends_at: formData.get('ends_at'),
    })

    if (!validatedFields.success) {
      return setClientErrorMessage(validatedFields.error.flatten().fieldErrors)
    }

    return validateFields
  }

Curious to know how to approach this.