Skip to content

Instantly share code, notes, and snippets.

@adityacodepublic
Last active July 9, 2025 12:42
Show Gist options
  • Select an option

  • Save adityacodepublic/783a335c57791b9c7ab53b45517d2348 to your computer and use it in GitHub Desktop.

Select an option

Save adityacodepublic/783a335c57791b9c7ab53b45517d2348 to your computer and use it in GitHub Desktop.

react hook form controller component that returns useController, or register return based on controlled boolean prop (default: true )

requires your form wrapped in FormProvider

import type {
  FieldPath,
  FieldValues,
  ControllerProps,
  UseControllerProps,
  UseFormRegisterReturn,
  ControllerRenderProps,
} from "react-hook-form";
import { useController, useFormContext } from "react-hook-form";

type ControlledProps<
  TFieldValues extends FieldValues,
  TName extends FieldPath<TFieldValues>,
  TTransformedValues,
> = {
  controlled: true;
} & ControllerProps<TFieldValues, TName, TTransformedValues>;

type UncontrolledProps<
  TFieldValues extends FieldValues,
  TName extends FieldPath<TFieldValues>,
  TTransformedValues,
> = {
  controlled: false;
  render: ({
    field,
  }: {
    field: UseFormRegisterReturn<TName>;
  }) => React.ReactNode;
} & UseControllerProps<TFieldValues, TName, TTransformedValues>;

// Unified type
type CustomControllerProps<
  TFieldValues extends FieldValues,
  TName extends FieldPath<TFieldValues>,
  TTransformedValues,
> =
  | ControlledProps<TFieldValues, TName, TTransformedValues>
  | UncontrolledProps<TFieldValues, TName, TTransformedValues>;

type renderField<
  TFieldValues extends FieldValues,
  TName extends FieldPath<TFieldValues>,
> = UseFormRegisterReturn<TName> | ControllerRenderProps<TFieldValues, TName>;

function Controller<
  TFieldValues extends FieldValues = FieldValues,
  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
  TTransformedValues = TFieldValues,
>(props: CustomControllerProps<TFieldValues, TName, TTransformedValues>) {
  const { register } = useFormContext<TFieldValues>();

  if (props.controlled === false) {
    return props.render({ field: register(props.name, props.rules) });
  }

  return props.render(
    useController<TFieldValues, TName, TTransformedValues>(props)
  );
}

export { Controller, type renderField };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment