Skip to content

DevTools in production #212

Open
Open

Description

@spacecat

Hi,

Stupid question maybe but what happens with the DevTools component that I have in my forms when I deploy to production?

I have "@hookform/devtools": "^4.3.1", in my devDependencies in package.json but won't my app break in production wherever I'm referencing @hookform/devtools?

Activity

  1. ViewableGravy commented on Aug 27, 2024

    @ViewableGravy

    What you might consider doing is a similar approach with most other libraries and their dev tools that should be lazy loaded;

    ie. Tanstack Router

    So in the case of this particular package, you can likely accomplish the same thing with the below code (untested)

    export const HookFormDevtools =
      process.env.NODE_ENV === 'production'
        ? () => null // Render nothing in production
        : React.lazy(() =>
            // Lazy load in development
            import('@hookform/devtools').then((res) => ({
              default: res.DevTool
            })),
          )

    Note: I am not a maintainer of this library, this just seems like a common approach that would work in the case of these dev tools, similar to others.

  2. ahkhanjani commented on Nov 18, 2024

    @ahkhanjani

    @spacecat I think it should be mentioned in the readme. As for what happens, if you take a look at rollup/writeCjsEntryFile.js file the bundler handles this case:

    if (process.env.NODE_ENV !== 'development') {
      module.exports = {
        DevTool: () => null,
      };
    } else {
      ${baseLine}.${formatName}.development.js')
    }

    So there's no devtools in prod. No need for manual checking.

  3. spacecat commented on Nov 20, 2024

    @spacecat
    Author

    @spacecat I think it should be mentioned in the readme. As for what happens, if you take a look at rollup/writeCjsEntryFile.js file the bundler handles this case:

    if (process.env.NODE_ENV !== 'development') {
      module.exports = {
        DevTool: () => null,
      };
    } else {
      ${baseLine}.${formatName}.development.js')
    }

    So there's no devtools in prod. No need for manual checking.

    @ahkhanjani I agree that it should be mentioned in the docs. I don't think it is ATM, however? I haven't checked recently....

  4. spacecat commented on Jan 31, 2025

    @spacecat
    Author

    @bluebill1049 got any directions for how to go about this?

  5. pcostanz commented on Aug 13, 2025

    @pcostanz

    @spacecat I use this

    import React, { lazy, Suspense } from 'react';
    
    import { Form as FormProvider } from '@wherever-your-shadcn-form-is-located';
    
    import { Debug } from '@/lib/debug'; // an internal debug conditional render wrapper
    
    import type { Control, FieldValues, UseFormReturn } from 'react-hook-form';
    
    const DevTool = lazy(() =>
      import('@hookform/devtools').then((module) => ({ default: module.DevTool }))
    );
    
    /**
     * Props for the Form component
     * Extends the Form component props and adds debug-specific functionality
     */
    interface FormProps<TFieldValues extends FieldValues = FieldValues> {
      /** The form object from useForm() - can be passed as a prop or spread */
      form?: UseFormReturn<TFieldValues>;
      /** Children to render inside the form */
      children?: React.ReactNode;
      /** Spread props from the form object (for compatibility with Form component) */
      [key: string]: unknown;
    }
    
    /**
     * A thin wrapper around the Form (FormProvider) component from @surface/components that
     * conditionally enables the react-hook-form DevTool.
     *
     * This component uses the useDebug hook to determine whether to show the DevTool.
     * The DevTool is dynamically imported to avoid bundling it in production builds
     * when debug mode is disabled.
     *
     * NOTE: There are documented issues with the watch function in react-hook-form
     * when DevTools is enabled. Use the useWatch hook in all cases to avoid this.
     *
     * This component can be used as a drop-in replacement for Form:
     * - With form prop: <Form form={form}>...</Form>
     * - With spread props: <Form {...form}>...</Form>
     *
     * @see {@link useDebug} - Hook that manages debug state
     * @see {@link Form} - The underlying form component
     */
    export const Form = <TFieldValues extends FieldValues = FieldValues>({
      form,
      children,
      ...spreadProps
    }: FormProps<TFieldValues>) => {
      // Determine the form object - prioritize form prop over spread props
      const formObject = form || (spreadProps as UseFormReturn<TFieldValues>);
      const control = formObject?.control;
    
      if (
        !formObject ||
        !control ||
        !formObject.watch ||
        !formObject.setValue ||
        !formObject.handleSubmit
      ) {
        console.warn(
          'Form: No valid form object provided - missing required properties'
        );
        return <>{children}</>;
      }
    
      return (
        <>
          <FormProvider {...formObject}>{children}</FormProvider>
          <Debug>
            <Suspense fallback={null}>
              <DevTool control={control as Control<FieldValues>} />
            </Suspense>
          </Debug>
        </>
      );
    };
    
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions