Published on

Fixing Shadcn/UI Tooltip Triggering Form Validation

Authors
  • Name
    Ripal & Zalak
    Twitter

When building forms in React with Shadcn/UI, react-hook-form, and zod, you may encounter an issue where clicking a button wrapped with a Tooltip component triggers form validation. This guide explains how to resolve this problem effectively.

The Problem

Consider the following scenario: a button wrapped in a Tooltip component updates a form field when clicked, but also triggers validation on unrelated fields. Here's an example:

export default function JobForm() {
  const formSchema = z.object({
    jobName: z.string().min(1).max(50),
    button: z.string().optional(),
  })

  const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      jobName: '',
      button: '',
    },
  })

  function onSubmit(values: z.infer<typeof formSchema>) {
    console.log(values)
  }

  return (
    <div>
      <Form {...form}>
        <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
          <FormField
            control={form.control}
            name="jobName"
            render={({ field }) => (
              <FormItem>
                <FormLabel>Job Name</FormLabel>
                <FormControl>
                  <Input placeholder="Input job name" {...field} />
                </FormControl>
                <FormMessage />
              </FormItem>
            )}
          />
          <Controller
            name="button"
            control={form.control}
            render={({ field }) => (
              <FormItem>
                <FormLabel>Button</FormLabel>
                <FormControl>
                  <TooltipProvider>
                    <Tooltip>
                      <TooltipTrigger asChild>
                        {' '}
                        {/* Add asChild here */}
                        <Button
                          type="button"
                          className="mr-2"
                          onClick={() => form.setValue('button', 'Button 1')}
                        >
                          Button 1
                        </Button>
                      </TooltipTrigger>
                      <TooltipContent>
                        <p>"This is Button 1"</p>
                      </TooltipContent>
                    </Tooltip>
                  </TooltipProvider>
                </FormControl>
                <FormMessage />
              </FormItem>
            )}
          />
          <Button type="submit">Submit</Button>
        </form>
      </Form>
    </div>
  )
}

The Cause

By default, the TooltipTrigger component renders as a button element. This behavior conflicts with the Button component inside it, causing form validation when clicked, even if the button's type is set to button.

The Solution

To prevent this, use the asChild prop on the TooltipTrigger. This allows the TooltipTrigger to render its child directly without adding an additional button element.

Here’s the fixed code:

<TooltipTrigger asChild>
  <Button type="button" className="mr-2" onClick={() => form.setValue('button', 'Button 1')}>
    Button 1
  </Button>
</TooltipTrigger>

Why It Works

The asChild prop ensures that the TooltipTrigger doesn’t create its own DOM element. Instead, it uses the child component (in this case, the Button) as the trigger, eliminating the conflict.

Additional Tips

  1. Always Specify Button Type: Ensure all buttons in your form have a type attribute to explicitly define their behavior (button, submit, or reset).
  2. Validate on Submit Only: Adjust validation settings in react-hook-form to trigger validation only on submit if required.
  3. Test with Developer Tools: Inspect the DOM to confirm there are no unexpected nested elements around your button.

Conclusion

Using asChild with TooltipTrigger resolves the issue of unwanted form validation in Shadcn/UI forms. This small but impactful change ensures a smoother user experience and maintains expected form behavior.