November 2026
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
已选择:无
import { useState } from "react";
import { DatePicker } from "@lark-ui/core";
/**
* 选择单个日期。
*/
export function DatePickerSingleDemo() {
const [date, setDate] = useState<Date | undefined>();
return (
<div className="flex flex-col gap-4">
<DatePicker
className="p-3"
mode="single"
selected={date}
onChange={(d) => {
if (d) {
setDate(d);
}
}}
/>
<p className="text-sm text-lark-subtle">
已选择:{date ? date.toLocaleDateString() : "无"}
</p>
</div>
);
}安装
统一导出
import { DatePicker, type DateRange } from "@lark-ui/core";按需导出
import {
DatePicker,
type DateRange,
} from "@lark-ui/core/components/date-picker";用法
日期选择器(DatePicker)支持三种选择模式:single、multiple 和 range。
import { useState } from "react";
import { DatePicker } from "@lark-ui/core";
export default function Example() {
const [date, setDate] = useState<Date>();
return <DatePicker mode="single" selected={date} onChange={setDate} />;
}示例
单选日期
选择单个日期。这是日期选择器最常见的用法。
November 2026
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
已选择:无
import { useState } from "react";
import { DatePicker } from "@lark-ui/core";
/**
* 选择单个日期。
*/
export function DatePickerSingleDemo() {
const [date, setDate] = useState<Date | undefined>();
return (
<div className="flex flex-col gap-4">
<DatePicker
className="p-3"
mode="single"
selected={date}
onChange={(d) => {
if (d) {
setDate(d);
}
}}
/>
<p className="text-sm text-lark-subtle">
已选择:{date ? date.toLocaleDateString() : "无"}
</p>
</div>
);
}多选日期
选择多个独立日期。使用 max 限制最多可选择的数量。
November 2026
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
已选择:0 个日期
import { useState } from "react";
import { DatePicker } from "@lark-ui/core";
/**
* 选择多个日期,最多可选 5 个。
*/
export function DatePickerMultipleDemo() {
const [dates, setDates] = useState<Date[] | undefined>();
return (
<div className="flex flex-col gap-4">
<DatePicker
className="p-3"
mode="multiple"
selected={dates}
onChange={setDates}
max={5}
/>
<p className="text-sm text-lark-subtle">
已选择:{dates?.length ?? 0} 个日期
</p>
</div>
);
}日期范围选择
选择一段连续的日期范围。搭配 numberOfMonths={2} 可获得并排视图,效果更好。多月视图会隐藏各月份之外的日期,避免面板之间出现重复日期。设置 showOutsideDays 可以显示这些日期。
November 2026
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
December 2026
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
范围:无
import { useState } from "react";
import { DatePicker, DateRange } from "@lark-ui/core";
/**
* 选择日期范围,同时显示两个月。
*/
export function DatePickerRangeDemo() {
const [range, setRange] = useState<DateRange | undefined>();
return (
<div className="flex flex-col gap-4">
<DatePicker
className="p-3"
mode="range"
selected={range}
onChange={setRange}
numberOfMonths={2}
/>
<p className="text-sm text-lark-subtle">
范围:
{range?.from
? `${range.from.toLocaleDateString()} - ${range.to?.toLocaleDateString() ?? "..."}`
: "无"}
</p>
</div>
);
}带最小/最大值限制的范围选择
使用 min 和 max 属性限制范围长度(以天/晚为单位)。
November 2026
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
import { useState } from "react";
import { DatePicker, DateRange } from "@lark-ui/core";
/**
* 日期范围选择,最短 3 晚、最长 7 晚。
*/
export function DatePickerRangeMinMaxDemo() {
const [range, setRange] = useState<DateRange | undefined>();
return (
<div className="flex flex-col gap-4">
<DatePicker
className="p-3"
mode="range"
selected={range}
onChange={setRange}
min={3}
max={7}
footer={<span className="text-xs text-lark-subtle">选择 3-7 晚</span>}
/>
</div>
);
}月份和年份下拉选择
设置 captionLayout="dropdown" 可直接跳转到指定月份和年份。使用
startMonth 和 endMonth 限定年份范围。
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
import { useState } from "react";
import { DatePicker } from "@lark-ui/core";
/**
* 通过月份和年份下拉框直接跳转到指定日期。
*/
export function DatePickerDropdownCaptionDemo() {
const [date, setDate] = useState<Date | undefined>();
return (
<DatePicker
className="p-3"
mode="single"
selected={date}
onChange={setDate}
captionLayout="dropdown"
startMonth={new Date(2020, 0)}
endMonth={new Date(2030, 11)}
/>
);
}搭配气泡卡片使用
与 Popover 组件组合,可以创建一个下拉式日期选择器。
import { useState } from "react";
import { DatePicker, Popover, Button } from "@lark-ui/core";
import { CalendarDotsIcon } from "@phosphor-icons/react";
/**
* 与 Popover 组合的日期选择器,呈现下拉式交互。
*/
export function DatePickerPopoverDemo() {
const [date, setDate] = useState<Date | undefined>();
return (
<Popover>
<Popover.Trigger
render={<Button variant="outline" icon={CalendarDotsIcon} />}
>
{date ? date.toLocaleDateString() : "选择日期"}
</Popover.Trigger>
<Popover.Content className="p-3">
<DatePicker mode="single" selected={date} onChange={setDate} />
</Popover.Content>
</Popover>
);
}搭配气泡卡片选择日期范围
在气泡卡片中展示两个月的日期范围选择器。
import { useState } from "react";
import { DatePicker, Popover, Button, DateRange } from "@lark-ui/core";
import { CalendarDotsIcon } from "@phosphor-icons/react";
/**
* 与 Popover 组合的日期范围选择器,呈现下拉式交互。
*/
export function DatePickerRangePopoverDemo() {
const [range, setRange] = useState<DateRange | undefined>();
const formatRange = () => {
if (!range?.from) return "选择日期范围";
if (!range.to) return range.from.toLocaleDateString();
return `${range.from.toLocaleDateString()} – ${range.to.toLocaleDateString()}`;
};
return (
<Popover>
<Popover.Trigger
render={<Button variant="outline" icon={CalendarDotsIcon} />}
>
{formatRange()}
</Popover.Trigger>
<Popover.Content className="p-3">
<DatePicker
mode="range"
selected={range}
onChange={setRange}
numberOfMonths={2}
/>
</Popover.Content>
</Popover>
);
}带预设的日期范围
将日期选择器与预设选项结合,便于快速选择。
import { useState } from "react";
import { DatePicker, Popover, Button, DateRange } from "@lark-ui/core";
import { CalendarDotsIcon } from "@phosphor-icons/react";
/**
* 带预设选项(位于 Popover 中)的日期范围选择器。
*/
export function DatePickerRangeWithPresetsDemo() {
const [range, setRange] = useState<DateRange | undefined>();
const [month, setMonth] = useState<Date>(new Date());
const today = new Date();
const presets = [
{
label: "今天",
range: { from: today, to: today },
},
{
label: "最近 7 天",
range: {
from: new Date(today.getTime() - 6 * 24 * 60 * 60 * 1000),
to: today,
},
},
{
label: "最近 30 天",
range: {
from: new Date(today.getTime() - 29 * 24 * 60 * 60 * 1000),
to: today,
},
},
{
label: "最近 90 天",
range: {
from: new Date(today.getTime() - 89 * 24 * 60 * 60 * 1000),
to: today,
},
},
{
label: "本月",
range: {
from: new Date(today.getFullYear(), today.getMonth(), 1),
to: new Date(today.getFullYear(), today.getMonth() + 1, 0),
},
},
{
label: "上个月",
range: {
from: new Date(today.getFullYear(), today.getMonth() - 1, 1),
to: new Date(today.getFullYear(), today.getMonth(), 0),
},
},
];
const handlePresetClick = (preset: { range: DateRange }) => {
setRange(preset.range);
// Navigate calendar to show the start of the range
if (preset.range.from) {
setMonth(preset.range.from);
}
};
const isPresetActive = (preset: { range: DateRange }) => {
if (!range?.from || !range?.to || !preset.range.from || !preset.range.to)
return false;
// Compare dates only (ignore time)
const sameFrom =
range.from.toDateString() === preset.range.from.toDateString();
const sameTo = range.to.toDateString() === preset.range.to.toDateString();
return sameFrom && sameTo;
};
const formatRange = () => {
if (!range?.from) return "选择日期范围";
if (!range.to) return range.from.toLocaleDateString();
return `${range.from.toLocaleDateString()} – ${range.to.toLocaleDateString()}`;
};
return (
<Popover>
<Popover.Trigger
render={<Button variant="outline" icon={CalendarDotsIcon} />}
>
{formatRange()}
</Popover.Trigger>
<Popover.Content className="p-0">
<div className="flex">
<div className="flex flex-col gap-1 border-r border-lark-hairline p-2 text-sm">
{presets.map((preset) => {
const isActive = isPresetActive(preset);
return (
<button
key={preset.label}
type="button"
onClick={() => handlePresetClick(preset)}
className={`rounded-md px-3 py-1.5 text-left whitespace-nowrap ${
isActive
? "bg-lark-contrast text-lark-inverse"
: "text-lark-subtle hover:bg-lark-control"
}`}
>
{preset.label}
</button>
);
})}
</div>
<div className="p-3">
<DatePicker
mode="range"
selected={range}
onChange={setRange}
month={month}
onMonthChange={setMonth}
numberOfMonths={2}
/>
</div>
</div>
</Popover.Content>
</Popover>
);
}禁用日期与用量限制
使用 disabled 属性让某些日期不可选择,使用 footer 显示用量信息。
November 2026
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
import { useState } from "react";
import { DatePicker } from "@lark-ui/core";
/**
* 含禁用日期的日期选择器,页脚展示使用次数限制。
*/
export function DatePickerDisabledWithFooterDemo() {
const [dates, setDates] = useState<Date[] | undefined>();
const today = new Date();
// Example: some dates are already used/unavailable
const unavailableDates = [
new Date(today.getFullYear(), today.getMonth(), 5),
new Date(today.getFullYear(), today.getMonth(), 12),
new Date(today.getFullYear(), today.getMonth(), 18),
new Date(today.getFullYear(), today.getMonth(), 25),
];
const selectedCount = dates?.length ?? 0;
const maxDays = 5;
return (
<DatePicker
className="p-3"
mode="multiple"
selected={dates}
onChange={setDates}
max={maxDays}
disabled={unavailableDates}
fixedWeeks
footer={
<p className="w-full pt-2 text-xs text-lark-subtle">
已选择 {selectedCount}/{maxDays} 天。灰色日期不可用。
</p>
}
/>
);
}完整的气泡卡片示例
下面是一个完整示例,演示如何将 DatePicker 与 Popover 组合使用:
import { useState } from "react";
import { DatePicker, Popover, Button } from "@lark-ui/core";
import { CalendarDotsIcon } from "@phosphor-icons/react";
export function DatePickerDropdown() {
const [date, setDate] = useState<Date>();
return (
<Popover>
<Popover.Trigger
render={<Button variant="outline" icon={CalendarDotsIcon} />}
>
{date ? date.toLocaleDateString() : "Pick a date"}
</Popover.Trigger>
<Popover.Content className="p-3">
<DatePicker
mode="single"
selected={date}
onChange={(d) => setDate(d)}
/>
</Popover.Content>
</Popover>
);
}API 参考
DatePicker 会将所有属性透传给
react-day-picker
。主要属性包括:
mode—"single" | "multiple" | "range"— 选择模式(必填)selected— 当前选中的日期onChange— 选择变化时的回调numberOfMonths— 展示的月份数量disabled— 不可选择的日期min/max— 最小/最大选择限制footer— 渲染在日历下方的内容locale— 用于国际化的 date-fns localeclassName— 额外的 CSS 类名
完整的 API 请参阅
react-day-picker 文档
。
与 react-day-picker 的差异
为了与其他 Lark 表单组件保持一致,DatePicker 使用 onChange 而不是 react-day-picker 的 onSelect。完整的类型推断能力得以保留——TypeScript 会根据 mode 属性正确收窄回调签名。