Munch · Shared component library
Date Range Picker
A calendar component that turned out to be a product.
- My role
- Sole owner — design, implementation, accessibility, shared-library release
- Stack
- ReactTypeScriptdate-fns-tzTailwind CSSStorybook
The problem
Reporting across the platform needed date selection — and nothing off the shelf fit. The requirement wasn't “pick a day.” It was: pick a range, pick a time on each end, do it correctly in South African time regardless of where the browser thinks it is, work on a phone, and stay keyboard-navigable.
This was the first component I owned end to end. It had to be right, because every report in the system would depend on it.
What I built
- Dual-calendar range selection with hover preview, so the range you're about to select is visible before you commit to it.
- Time selection on both ends of the range, with previously chosen times preserved when the date changes — a small detail that removes a genuinely annoying re-entry step.
- Quick-select presets (Today, Yesterday, Last 7 Days, Last 30 Days) for the 90% case, with the full calendar there for the rest.
- Month and year dropdowns, so navigating back a year isn't twelve clicks.
- Explicit timezone handling via date-fns-tz — dates are pinned to Africa/Johannesburg rather than inherited from the browser, so a franchisee's report reads the same whether they open it in Pretoria or on holiday abroad.
- A responsive layout that collapses to a single calendar with a mobile-appropriate quick-select on small screens.
- An accessibility pass on ARIA roles and keyboard navigation after an initial review flagged gaps.
What was hard
Timezones. The naive approach — manually calculating offsets — falls apart at DST boundaries and when the browser's local time disagrees with the business's time. The working solution was to stop fighting JavaScript's Date object: read the date components in the target timezone, apply the change there, and convert back to UTC through date-fns-tz rather than doing arithmetic by hand.
The other hard part was architectural. It's tempting to build a date picker as one component. It isn't one — it's a controlled input, a calendar, a time select, a month/year navigator, and a quick-select panel, all sharing state. Splitting it into sub-components with a single source of truth is what made it maintainable.
Outcome
Shipped as a shared library component and used across reporting and admin surfaces. Supports both single-date and range modes from the same API.