Reports, bookings and analytics all need the same control: pick a start date and an end date. Most users don't want to click through a calendar for "last month", so a good range picker also offers presets. This tutorial builds one with oks-ui's FormFieldSet of type datepicker.
import { FormFieldSet } from "oks-ui/form-field-set";
import "oks-ui/form-field-set.css";1. A single date first
With type="datepicker", the value is a string in YYYY-MM-DD format:
const [date, setDate] = useState("");
<FormFieldSet
type="datepicker"
name="dueDate"
label="Due date"
value={date}
onChange={(v) => setDate(String(v ?? ""))}
/>2. Turn it into a range
Add range and the value becomes an object with a start and an end:
type Range = { start: string; end: string };
const [period, setPeriod] = useState<Range>({ start: "", end: "" });
<FormFieldSet
type="datepicker"
name="period"
label="Report period"
range
monthsToShow={2}
value={period}
onChange={(v) => setPeriod(v as Range)}
/>monthsToShow={2} puts two months side by side, which makes ranges across a month boundary much easier to pick.
3. Add presets
showPresets adds a list of common ranges next to the calendar. Choose which ones with presets:
<FormFieldSet
type="datepicker"
name="period"
label="Report period"
range
showPresets
presets={["today", "thisWeek", "lastWeek", "thisMonth", "lastMonth", "last3Months", "custom"]}
value={period}
onChange={(v) => setPeriod(v as Range)}
/>The available presets are today, yesterday, thisWeek, lastWeek, fortnight, thisMonth, lastMonth, last3Months, thisYear, lastYear and custom. custom switches to the calendar for a free choice. Presets are for ranges only; a single-date picker ignores them.
4. Limit what can be picked
Three props keep users away from impossible dates:
minDate/maxDate— nothing before or after. A report can't cover the future; a booking can't start in the past.blockedDates— individual dates that can't be chosen, such as public holidays.
const today = new Date();
<FormFieldSet
type="datepicker"
name="period"
label="Report period"
range
showPresets
maxDate={today}
blockedDates={["2026-12-25", "2027-01-01"]}
value={period}
onChange={(v) => setPeriod(v as Range)}
/>Dates accept either a Date object or a YYYY-MM-DD string.
5. One trigger or two
By default a range opens from a single field showing both dates. For forms where start and end are separate questions — "check-in" and "check-out" — use two triggers:
<FormFieldSet type="datepicker" name="stay" label="Your stay" range rangeTrigger="dual" />6. Adding a time
For schedules, add withTime. The value then includes the time as YYYY-MM-DDTHH:mm:
<FormFieldSet type="datepicker" name="publishAt" label="Publish at" withTime />Useful extras
clearableadds a clear button, withonClearif you need to react to it.displayFormat="iso"shows dates as2026-09-29instead of the default readable format.- Inside an oks-ui
Form, dropvalueandonChange— the form collects the value under the field'sname, andvalidation={{ rules: { required: true } }}makes sure both dates are filled in.
Filtering data by the range
Because the value is plain strings in YYYY-MM-DD form, comparing dates is simple string comparison:
const inRange = orders.filter(
(o) => (!period.start || o.date >= period.start) && (!period.end || o.date <= period.end),
);See the date picker component page for every option.



