Composable date and time pickers for React Native / Expo.
Drum-scroll columns — no native dependencies, no @elberpg/ex-modal required.
Spanish docs: README_ES.md
| Default — Date | Default — Time |
|---|---|
![]() |
![]() |
Fully themeable via global style objects — no props required.
| Dark ☕ — Date | Dark ☕ — Time | Square ⬛ — Date | Square ⬛ — Time |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
npm install @elberpg/ex-date-pickerEach picker and modal are independent components. You compose them:
<DatePickerModal visible={open} onClose={onClose} onConfirm={() => onConfirm(draft)}>
<DatePicker value={draft} onChange={setDraft} />
</DatePickerModal>This gives full style control over each part independently.
Scroll columns for day, month, and year. Use standalone or inside any modal.
import { DatePicker } from '@elberpg/ex-date-picker';
<DatePicker
value={date}
onChange={setDate}
locale="en-US"
/>| Prop | Type | Default | Description |
|---|---|---|---|
value |
Date | DatePickerValue |
today | Current value |
onChange |
(date: Date) => void |
— | Called on every scroll change |
locale |
string |
'en' |
Locale for month names |
minYear |
number |
current − 100 | Minimum year |
maxYear |
number |
current + 10 | Maximum year |
itemHeight |
number |
from theme | Row height in px |
selectedColor |
string |
from theme | Color of the selected item |
unselectedColor |
string |
from theme | Color of unselected items |
style |
ViewStyle |
— | Container style |
Scroll columns for hour and minute. Supports 12-hour (AM/PM) mode.
import { TimePicker } from '@elberpg/ex-date-picker';
<TimePicker
value={time}
onChange={setTime}
use12h={false}
/>| Prop | Type | Default | Description |
|---|---|---|---|
value |
TimeValue | Date |
now | Current value |
onChange |
(time: TimeValue) => void |
— | Called on every scroll change |
use12h |
boolean |
false |
12-hour mode with AM/PM column |
itemHeight |
number |
from theme | Row height in px |
selectedColor |
string |
from theme | Color of the selected item |
unselectedColor |
string |
from theme | Color of unselected items |
style |
ViewStyle |
— | Container style |
interface TimeValue {
hours: number; // 0–23 (or 1–12 in 12h mode)
minutes: number; // 0–59
}Bottom-sheet using React Native's Modal component. Renders on top of everything including toasts.
import { DatePicker, DatePickerModal } from '@elberpg/ex-date-picker';
const [open, setOpen] = useState(false);
const [date, setDate] = useState(new Date());
const [draft, setDraft] = useState(new Date());
// Open: sync draft with current value
<Pressable onPress={() => { setDraft(date); setOpen(true); }}>
<Text>{date.toLocaleDateString()}</Text>
</Pressable>
<DatePickerModal
visible={open}
onClose={() => setOpen(false)}
onConfirm={() => { setDate(draft); setOpen(false); }}
title="Select date"
>
<DatePicker value={draft} onChange={setDraft} locale="en-US" />
</DatePickerModal>Same API as DatePickerModal but uses position: absolute instead of RN's Modal.
Avoids conflicts with toasts, sheets, and other overlays. Must be placed at root level.
import { DatePicker, DatePickerModalOverlay } from '@elberpg/ex-date-picker';
<DatePickerModalOverlay
visible={open}
onClose={() => setOpen(false)}
onConfirm={() => { setDate(draft); setOpen(false); }}
title="Select date"
>
<DatePicker value={draft} onChange={setDraft} />
</DatePickerModalOverlay>Same API as the date variants. Just swap DatePicker for TimePicker as children.
import { TimePicker, TimePickerModal } from '@elberpg/ex-date-picker';
<TimePickerModal
visible={open}
onClose={() => setOpen(false)}
onConfirm={() => { setTime(draft); setOpen(false); }}
title="Select time"
clearable
onClear={() => { setTime(undefined); setOpen(false); }}
>
<TimePicker value={draft} onChange={setDraft} use12h />
</TimePickerModal>Shared by DatePickerModal, DatePickerModalOverlay, TimePickerModal, TimePickerModalOverlay.
| Prop | Type | Default | Description |
|---|---|---|---|
visible |
boolean |
— | Shows or hides the modal |
onClose |
() => void |
— | Called when the backdrop or Cancel is tapped |
onConfirm |
() => void |
— | Called when Confirm is tapped |
children |
ReactNode |
— | The picker to render inside |
title |
string |
— | Modal title |
confirmLabel |
string |
'Confirm' |
Confirm button text |
cancelLabel |
string |
'Cancel' |
Cancel button text |
clearable |
boolean |
false |
Show a Clear button |
clearLabel |
string |
'Clear' |
Clear button text |
onClear |
() => void |
— | Called when Clear is tapped |
btnConfirm |
({ onPress }) => ReactNode |
— | Replace the Confirm button |
btnCancel |
({ onPress }) => ReactNode |
— | Replace the Cancel button |
btnClear |
({ onPress }) => ReactNode |
— | Replace the Clear button |
There are two separate style objects — one for the picker, one for the modal shell.
import { ExDatePicker } from '@elberpg/ex-date-picker';
ExDatePicker.styles.selectedColor = '#0a84ff';
ExDatePicker.styles.unselectedColor = '#8e8e93';
ExDatePicker.styles.itemHeight = 52;
ExDatePicker.styles.fontFamily = 'Georgia';
ExDatePicker.styles.lineColor = '#3a3a3c';
ExDatePicker.styles.columnLabel = { fontSize: 11, color: '#888', textTransform: 'uppercase' };| Field | Type | Description |
|---|---|---|
selectedColor |
string |
Color of the selected scroll item |
unselectedColor |
string |
Color of unselected scroll items |
itemHeight |
number |
Height of each scroll row in px |
fontFamily |
string |
Font family for scroll item text |
lineColor |
string |
Color of the two selection indicator lines |
columnLabel |
TextStyle |
Column labels (DAY · MONTH · YEAR / HOUR · MIN) |
import { ExDatePickerModal } from '@elberpg/ex-date-picker';
ExDatePickerModal.styles.card = {
backgroundColor: '#1c1c1e',
borderTopLeftRadius: 28,
borderTopRightRadius: 28,
};
ExDatePickerModal.styles.title = { fontSize: 17, fontWeight: '700', color: '#fff' };
ExDatePickerModal.styles.btnConfirm = { backgroundColor: '#0a84ff' };
ExDatePickerModal.styles.btnConfirmText = { fontSize: 15, fontWeight: '600', color: '#fff' };
ExDatePickerModal.styles.backdropColor = 'rgba(0,0,0,0.65)';| Field | Type | Description |
|---|---|---|
card |
ViewStyle |
Main card (background, border radius, padding) |
pill |
ViewStyle |
Drag pill at the top |
title |
TextStyle |
Modal title (fontSize, color, fontFamily…) |
headerBorder |
ViewStyle |
Separator below the title |
btnConfirm |
ViewStyle |
Confirm button container |
btnCancel |
ViewStyle |
Cancel button container |
btnClear |
ViewStyle |
Clear button container |
btnConfirmText |
TextStyle |
Confirm button text |
btnCancelText |
TextStyle |
Cancel button text |
btnClearText |
TextStyle |
Clear button text |
backdropColor |
string |
Backdrop color + opacity (rgba(…)) |
<DatePickerModal
btnConfirm={({ onPress }) => (
<Pressable onPress={onPress} style={{ backgroundColor: '#0a84ff', borderRadius: 12, flex: 1, height: 48, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{ color: '#fff', fontWeight: '700' }}>Done</Text>
</Pressable>
)}
>
<DatePicker value={draft} onChange={setDraft} />
</DatePickerModal>Place modals outside any ScrollView.
// ✅ Correct
return (
<>
<SafeAreaView>
<ScrollView>...</ScrollView>
</SafeAreaView>
<DatePickerModal visible={open} onClose={onClose} onConfirm={onConfirm}>
<DatePicker value={draft} onChange={setDraft} />
</DatePickerModal>
</>
);
// ❌ Wrong — the modal scrolls with the content
return (
<ScrollView>
<DatePickerModal visible={open} .../>
</ScrollView>
);




