figma guide
Designing date pickers and calendar UI in Figma: patterns, states, and handoff
Design date pickers, range selectors, and calendar grids in Figma with input states, presets, timezone notes, and Dev Mode specs for forms and filters.
- Published
- Updated
- Jun 13, 2026
- Read time
- 9 min
- Level
- Beginner
Quick answer
Date pickers in Figma combine a text input, calendar popover, and optional presets—single date, date range, or month/year-only modes. Build a DatePicker component with variants for mode=single | range | month, state=default | open | disabled | error, and size=sm | md. Pair the calendar grid with forms, dropdowns, and tables filters. Document date format, min/max bounds, disabled dates, timezone handling, and keyboard behavior in Dev Mode—engineering cannot infer these from a static calendar frame. Start from the Figma guides hub for component basics.
Who this is for
- Product designers shipping booking flows, reports, filters, and scheduling screens.
- Design system teams replacing inconsistent date fields copied from random Dribbble shots.
- Engineers implementing locale-aware parsing, range selection, and accessible calendar grids.
Picker types compared
| Type | User selects | Best for | Avoid when |
|---|---|---|---|
| Single date | One day | Birthdays, due dates, appointments | Selecting a week-long vacation with two taps |
| Date range | Start + end | Reports, bookings, analytics filters | Single-day events |
| Month picker | Month + year | Billing cycles, statements | Day-level precision needed |
| Date + time | Day + hour/minute | Meetings, deadlines | Date-only reporting |
| Inline calendar | Always visible grid | Availability, scheduling dashboards | Compact filter bars |
| Preset chips | ”Last 7 days”, “This month” | Analytics, admin filters | Legal contracts needing exact dates |
Verdict: default to popover + input for forms; use inline calendar only when the grid is the primary interaction (booking, availability). Combine presets with a range picker for search and filter bars.
Component anatomy
| Part | Purpose | Spec tip |
|---|---|---|
| Input field | Shows formatted date(s) | Placeholder: locale format (MM/DD/YYYY vs DD/MM/YYYY) |
| Calendar icon | Opens popover | Optional on mobile if tap opens native picker |
| Popover panel | Month grid + nav | Min width ~280px; shadow/elevation token |
| Month header | Prev/next + month label | Chevron buttons 32–40px hit area |
| Weekday row | Mon–Sun labels | Use locale start-of-week |
| Day cell | Selectable date | 36–40px square; today ring |
| Range highlight | In-between days | surface/selected-subtle between endpoints |
| Footer actions | Clear, Today, Apply | Required for range + filter contexts |
| Preset list | Quick ranges (optional) | Left column in wide popover |
DatePicker
├── Variant: mode=single | range | month
├── Variant: state=default | open | disabled | error
├── Variant: size=sm | md
├── Layer: Input (reuse TextField from forms)
│ ├── Value text
│ └── Calendar icon button
├── Layer: Popover (absolute below input)
│ ├── Presets (optional, vertical list)
│ ├── CalendarHeader (prev | "June 2026" | next)
│ ├── WeekdayRow (7 labels)
│ ├── DayGrid (6 rows × 7 cols)
│ └── Footer (Clear | Today | Apply)
└── Layer: Helper / error text
Bind day text styles to your typography scale. Use semantic color tokens for selected, hover, disabled, and today states.
Day cell states
| State | Visual | Notes |
|---|---|---|
| Default | Neutral text on surface | Pointer on hover |
| Hover | Subtle background | Not on disabled days |
| Selected | Solid brand fill; inverse text | Single or range endpoint |
| In range | Light fill between endpoints | Range mode only |
| Today | Ring or dot; not same as selected | Can be both today + selected |
| Outside month | Muted text | Still clickable or not—document |
| Disabled | Muted; no pointer | Past dates, blackout days |
| Focus | Visible focus ring | Keyboard grid navigation |
Reuse interactive components for day cells. Selected and today must differ when they overlap—e.g., filled selected + outer ring for today.
Date range selection flow
| Step | UI | Spec |
|---|---|---|
| 1 | User opens picker | Popover below input |
| 2 | First click | Sets start; hover previews range |
| 3 | Second click | Sets end; closes or waits for Apply |
| 4 | Invalid order | Auto-swap start/end or block—pick one |
| 5 | Same-day range | Allow or treat as single day—document |
| 6 | Apply (filters) | Commits to table query |
Show partial range in the input after first click (Jun 3 – …). For analytics, pair with chips showing active range on the filter bar.
Input field and formatting
| Concern | Design decision | Handoff note |
|---|---|---|
| Display format | Jun 13, 2026 vs 13/06/2026 | locale prop; do not hardcode in mocks only |
| Placeholder | Matches format | Select date vs MM/DD/YYYY |
| Manual typing | Allowed or picker-only | Validation on blur |
| Clear button | Icon inside input | Resets value + closes popover |
| Error state | Red border + message | ”Date unavailable” vs “Invalid format” |
| Min / max | Disable out-of-range days | minDate, maxDate in spec |
Align field heights with your form input sizes—sm for dense tables, md for standard forms.
Presets for filters and reports
| Preset | Range logic | Typical use |
|---|---|---|
| Today | Start = end = today | Ops dashboards |
| Last 7 days | Rolling 7 days | Support queues |
| Last 30 days | Rolling 30 | Analytics default |
| This month | 1st → last day of current month | Billing |
| Last month | Previous calendar month | Finance reports |
| Custom | Opens full picker | Power users |
Layout: presets as a left column (140–160px) when popover is wide; on mobile, use a bottom sheet with preset list above the grid.
Month and year navigation
| Control | Pattern | Accessibility |
|---|---|---|
| Prev / next month | Chevron in header | aria-label="Previous month" |
| Month label click | Opens month/year dropdown | Faster jump than 12 next clicks |
| Year stepper | In month picker mode | For birth year far in past |
| Decade view | Optional for DOB | Grid of years |
Prototype at least: default month, month dropdown open, and year picker frames. Note whether future months are disabled (e.g., scheduling only).
Timezone and locale (do not skip)
| Topic | What to document | Why |
|---|---|---|
| Timezone | UTC storage vs local display | ”Jun 13” shifts for global users |
| Start of week | Sunday vs Monday | Grid column order |
| Fiscal calendar | Non-standard quarters | Enterprise reporting |
| Holidays | Disabled dates array | Booking products |
| 12h vs 24h | If time is included | Pair with time sub-input |
Add a sticky note on booking flows: “All times shown in America/Vancouver” or use the user’s local zone—pick one product-wide rule.
Pairing with other patterns
| Need | Pattern | Link |
|---|---|---|
| Filter bar | Date range + search | Reports, admin lists |
| Booking confirmation | Modal summary | Show selected range |
| Empty results | Empty state | ”No data for this period” |
| Loading data | Skeleton on table | After Apply |
| Validation errors | Inline alert | Conflicting dates |
Accessibility checklist
| Requirement | Implementation |
|---|---|
| Label | Visible label or aria-label on input |
| Grid role | role="grid" with role="gridcell" per day |
| Selected | aria-selected="true" |
| Disabled days | aria-disabled="true" |
| Live region | Announce month change on nav |
| Keyboard | Arrow keys move; Enter selects; Esc closes |
| Focus trap | Optional inside popover until Apply |
| Color | Selected state not color-only |
Run accessibility plugins on selected vs today contrast.
Prototyping limits
Figma cannot compute real dates or locale. For demos:
- Build frames: closed, open single, open range (partial), month dropdown.
- Wire calendar icon → open; Apply → closed with filled input.
- Use sticky comments for dynamic behavior (disabled weekends, min date = today).
Organize flows with sections per screen.
Handoff to engineering
| Deliverable | Where it lives |
|---|---|
mode values | single, range, month |
| Date format strings | Per locale table |
| minDate / maxDate | Component description |
| Disabled date rules | Function or list spec |
| First day of week | 0 (Sun) or 1 (Mon) |
| Range apply behavior | Auto-close vs explicit Apply |
| Timezone policy | UTC vs local |
| Mobile behavior | Native picker vs custom popover |
| Popover placement | Flip above if no room below |
Include date pickers in your Dev Mode handoff checklist. Publish under Patterns / Forms in your team library.
Real-world examples
Analytics filter bar
Compact range input with presets (7d, 30d, Custom). Apply refreshes table below. Active range shown as a chip next to search.
Hotel booking
Inline two-month calendar; range highlight; disabled past dates; checkout min-stay rules in component description. Price per night on day cells (optional tier).
Employee DOB (HR form)
Single date; month/year dropdown for fast year jump; max date = today; manual typing allowed with validation message.
Common mistakes
| Mistake | Consequence | Fix |
|---|---|---|
| One date format in mocks | Wrong locale in production | Document locale + examples |
| Today = selected same style | Users lose orientation | Ring + fill combo |
| No Apply on range filters | Table updates on every click | Explicit Apply for data queries |
| Tiny day cells (<32px) | Miss taps on mobile | 36–40px minimum |
| Ignoring timezone | Off-by-one day bugs | Policy note on every booking flow |
| 6×7 grid with wrong start day | Misaligned weekdays | Match locale week start |
| Range without hover preview | Confusing second click | Show in-between highlight on hover |
| Popover clipped in modals | Unusable picker | Flip placement or use modal layer spec |
FAQ
Popover vs inline calendar?
Popover saves space in forms and filters. Inline works when picking dates is the main task (booking, scheduling).
Native mobile date picker vs custom?
Native is faster to ship and familiar on iOS/Android. Custom when you need range, presets, or brand calendar styling—document which screens use which.
Show two months side by side?
Common for travel range selection. Ensure popover min-width (~560px) or full-width on mobile sheet.
Date picker in a table cell?
Use sm size; popover opens upward if near viewport bottom. Consider filter row above table instead of per-cell pickers.
Bottom line
Design date pickers as a system: input, popover, calendar grid, presets, and clear locale/timezone rules—not a pretty month screenshot. Document modes, bounds, disabled logic, and Apply behavior in Dev Mode. Continue with forms, search filters, tables, and the tutorials hub.
§ Keep reading