duhan
Blog

Inserting Shadcn Slider/DatePicker into react-hook-form

I couldn’t find clear examples of using Slider and DatePicker components from Shadcn with react-hook-form. Here’s how I implemented both.

Slider

First, create a small wrapper component around Shadcn’s Slider:

import { useEffect } from "react";
import { Slider } from "@/components/ui/slider";

type Props = {
  value: number;
  onChange: (value: number) => void;
};

export default function Ratio({ value, onChange }: Props) {
  useEffect(() => {
    if (!value) {
      onChange(0.5);
    }
  }, [value, onChange]);

  return (
    <Slider
      min={0}
      max={1}
      step={0.01}
      defaultValue={[value ?? 0.5]}
      onValueChange={(vals) => onChange(vals[0])}
    />
  );
}

Then use it in your form like this:

<FormField
  control={form.control}
  name="slider"
  render={({ field }) => (
    <FormItem>
      <FormLabel>Slider</FormLabel>
      <FormControl>
        <Ratio
          value={field.value}
          onChange={(value) => form.setValue("slider", value)}
        />
      </FormControl>
      <FormMessage />
    </FormItem>
  )}
/>

DatePicker

This one is more complex. I keep the date range as two fields in a single object: startDate and endDate.

Step 1: Create a wrapper around your custom DateRangePicker component.

import { useState } from "react";
import { DateRange } from "react-day-picker";
import DateRangePicker from "@/components/DateRangePicker";

type Props = {
  formValue: {
    startDate: string;
    endDate: string;
  };
  onChange: (value: DateRange | undefined) => void;
};

export default function DateField({ formValue, onChange }: Props) {
  const [date, setDate] = useState<DateRange | undefined>({
    from: formValue?.startDate ? new Date(formValue.startDate) : undefined,
    to: formValue?.endDate ? new Date(formValue.endDate) : undefined,
  });

  function handleSelectDate(selected: DateRange | undefined) {
    setDate(selected);
    onChange(selected);
  }

  return (
    <DateRangePicker
      date={date}
      setDate={handleSelectDate}
      fromDate={new Date()}
    />
  );
}

Step 2: Use it in the form

<FormField
  control={form.control}
  name="dateRange"
  render={({ field }) => (
    <FormItem>
      <FormLabel>Date Range</FormLabel>
      <DateField
        formValue={field.value}
        onChange={(value) => setDateRange(value)}
      />
      <FormMessage />
    </FormItem>
  )}
/>

Step 3: Define the handler

const setDateRange = useCallback((value: DateRange | undefined) => {
  if (!value?.from) {
    form.setValue("dateRange", {
      startDate: "1970-01-01",
      endDate: "1970-01-01",
    });
    return;
  }

  const start = format(value.from, "yyyy-MM-dd");
  const end = value.to ? format(value.to, "yyyy-MM-dd") : start;

  form.setValue("dateRange", {
    startDate: start,
    endDate: end,
  });
}, [form]);

There might be cleaner ways to do this, but this setup works well and integrates smoothly with react-hook-form. Hope it helps!

🔗 Related posts