Why I Ditched Flatpickr: My #1 Calendar Pick for 2025
Tired of fighting with Flatpickr? I was too. Discover why I switched and my #1 calendar component pick for modern web development in 2025. A deep dive.
Alex Carter
Senior Frontend Engineer passionate about building performant, accessible, and beautiful user interfaces.
Let's have a little chat about a tool that’s probably in your `package.json` right now, or at least has been at some point: Flatpickr. For years, it was my trusty sidekick. Lightweight, powerful, and dependency-free, it was the Swiss Army knife of date pickers. It just worked. But as we head into 2025, the way we build for the web has fundamentally changed. And for me, “it just works” is no longer enough.
My relationship with Flatpickr started to feel... strained. It felt like I was trying to fit a square peg into a round, component-shaped hole. After one too many late nights fighting with CSS overrides and React lifecycle hooks, I knew it was time for a change.
This isn't a hit piece on Flatpickr. It’s a fantastic library that served an era of the web perfectly. This is the story of why I moved on, and what I found waiting for me on the other side—my definitive #1 calendar pick for 2025 and beyond.
The Cracks Begin to Show: My Issues with Flatpickr
The friction didn't appear overnight. It was a slow burn, a collection of small annoyances that grew into a major development bottleneck. Here’s what pushed me over the edge.
The React Wrapper Nightmare
Flatpickr is a vanilla JavaScript library that directly manipulates the DOM. Modern frameworks like React, Vue, and Svelte operate on a declarative model with a virtual DOM. Mixing the two is like mixing oil and water. It can be done, but it’s messy.
Every time I needed a date picker, I had to write (or copy) a wrapper component. This usually involved a combination of `useRef` to get a handle on the DOM element, and a `useEffect` hook to initialize and destroy the Flatpickr instance. Syncing state between the Flatpickr instance and my React state felt like a fragile, error-prone chore.
It looked something like this:
// A simplified, but still clunky, Flatpickr wrapper in React
import React, { useRef, useEffect } from 'react';
import flatpickr from 'flatpickr';
import 'flatpickr/dist/flatpickr.min.css';
const FlatpickrWrapper = ({ value, onChange }) => {
const inputRef = useRef(null);
const fpInstance = useRef(null);
useEffect(() => {
if (inputRef.current) {
fpInstance.current = flatpickr(inputRef.current, {
defaultDate: value,
onChange: (selectedDates) => {
onChange(selectedDates[0]);
},
// ... more config
});
}
// Cleanup function to destroy the instance
return () => {
if (fpInstance.current) {
fpInstance.current.destroy();
}
};
}, []); // The dependency array here is tricky to get right!
// How do you sync props changes back to the instance? More effects!
return ;
};
This boilerplate gets old, fast. It felt like I was fighting my framework instead of working with it.
Styling and Theming Headaches
My second major gripe was styling. I’m all-in on Tailwind CSS. It’s a utility-first approach that keeps my styles consistent and my HTML semantic. Flatpickr, however, comes with its own opinionated stylesheet.
Want to change the color of the selected day? You’re not adding a `bg-blue-500` class. You’re diving into your global CSS file to write highly specific selectors to override the defaults. Get ready for a battle of specificity and a slew of `!important` tags. It completely breaks the utility-first workflow and decouples the styling from the component itself.
The Shift in Development Philosophy
Beyond the technical issues, there's been a philosophical shift in the frontend world. We've moved away from monolithic, “black box” UI libraries and toward composable, “headless” primitives.
We don't want a library to give us a finished, hard-to-change component anymore. We want libraries that give us the building blocks—the logic, the accessibility, the state management—and let us have full control over the final look and feel. Flatpickr, for all its strengths, represents the old way.
My Criteria for a Modern Date Picker
My search for a replacement was guided by a clear set of principles, born from the frustrations above:
- Framework-Native: It must be built for React, thinking in components, props, and state from the ground up. No more wrappers.
- Headless or Unstyled First: It should provide the logic, but I control the markup and styling. This is a must for working with tools like Tailwind CSS.
- Accessibility (A11y) as a Priority: It must be built with WAI-ARIA standards in mind, ensuring it’s usable by everyone, with proper keyboard navigation and screen reader support out of the box.
- Composable and Extensible: I want to be able to build on it, wrap it, and extend it without fighting the library's internals.
My #1 Pick for 2025: The Composable Approach
After exploring various options, I realized the best solution wasn't a single, drop-in replacement. It was a change in approach. My #1 pick is a stack of tools that work together in perfect harmony:
The winner is the combination of `react-day-picker` and `date-fns`, brought together seamlessly by unstyled, accessible primitives like Radix UI, and perfectly exemplified by `shadcn/ui`'s Calendar component.
This isn't just a library; it's a modern, composable system for building date-related UIs. Let's break down why this stack is so powerful.
Why This Stack Wins
`react-day-picker`: The Powerful Core
At the heart of this stack is `react-day-picker`. It is the engine that handles all the complex date logic. It’s a headless library, meaning it gives you all the functionality—selecting days, ranges, handling months, localization—but makes zero assumptions about your UI. It's incredibly powerful, well-maintained, and built for React from day one.
`date-fns`: The Modern Utility Belt
To handle date formatting and manipulation, `date-fns` is the clear choice. It’s the spiritual successor to Moment.js, but with a crucial advantage: it’s tree-shakable. You only import the functions you need, keeping your bundle size lean. Its API is simple, it works with native JavaScript Date objects, and it’s immutable, preventing a whole class of bugs.
`shadcn/ui` & Radix: The Accessible, Unstyled Shell
This is the final, magical piece. How do you present the calendar to the user? In a popover, of course. Building an accessible, robust popover from scratch is hard. This is where Radix UI comes in.
Radix provides a set of unstyled, accessible UI primitives for things like popovers, dialogs, and dropdowns. It handles all the tricky parts: focus management, keyboard navigation, and ARIA attributes.
`shadcn/ui` takes this concept to its logical conclusion. It's not a component library you install from npm. Instead, you use a CLI to copy pre-built components—which use `react-day-picker`, `date-fns`, Radix, and Tailwind CSS—directly into your project. You get a beautiful, fully-functional, and accessible date picker that is 100% yours to modify.
Look how clean this is:
// The modern, clean way with shadcn/ui
"use client";
import { useState } from "react";
import { format } from "date-fns";
import { Calendar as CalendarIcon } from "lucide-react";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
export function DatePicker() {
const [date, setDate] = useState<Date>();
return (
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-[280px] justify-start text-left font-normal",
!date && "text-muted-foreground"
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{date ? format(date, "PPP") : <span>Pick a date</span>}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar
mode="single"
selected={date}
onSelect={setDate}
initialFocus
/>
</PopoverContent>
</Popover>
);
}
No wrappers. No CSS overrides. Just clean, composable React components that I can style with the tools I already use. It's a breath of fresh air.
It's Not Goodbye, It's Thank You
Ditching Flatpickr wasn’t about finding a library that was simply “better.” It was about aligning my tools with the modern philosophy of web development. The future is composable, accessible, and gives developers full control over the end product.
The stack of `react-day-picker`, `date-fns`, and primitives from Radix/`shadcn/ui` doesn't just give me a superior date picker; it makes me a better, more efficient developer. It lets me work *with* my framework, not against it.
So, thank you, Flatpickr, for all the years of reliable service. But for 2025 and beyond, my calendar is set with a new, more flexible approach.
What's your go-to solution for date pickers in modern projects? I'd love to hear about it in the comments below!