[shadcn] calendar build error - React DayPicker 9.4.3 migration
shadcn calendar를 사용할 일이 있었는데 github bugfix가 19시간 전이라 신기해서 찍어두었습니다.
기술이 바뀌고 마이그레이션하는 건 늘 있는 일이지만 작업 중 겪고 있는 이슈가 현재진행형으로 해결된 건 처음입니다.
어떤 이슈인가요?
shadcn에서 calendar를 사용하면 빌드할 때 사용하지 않는 props로 인해 lint 에러가 발생합니다.
하단은 calendar의 기본 코드입니다.
...
components={{
IconLeft: ({ ...props }) => <ChevronLeft className="h-12 w-12" />,
IconRight: ({ ...props }) => <ChevronRight className="h-12 w-12" />,
}}
...
이전 프로젝트를 했을 때는 { ...props } 를 아예 지워 버려 해결했던 기억이 있는데요.
같은 이슈로 불편함을 겪은 사람들이 shadcn github에 bug issue 등록을 해두었고, 그에 대한 답변이 달린 상태입니다.
[bug]: Building production package fails due to Calendar component · Issue #5600 · shadcn-ui/ui
Describe the bug Running bun run build (or npm run build) produces this error, and fails to build: Type error: Object literal may only specify known properties, and 'IconLeft' does not exist in typ...
github.com
어떤 것들이 바뀌었나요?
함께 마이그레이션 문서를 확인하며 적용해 보겠습니다. 코드는 shadcn calendar가 기준입니다.
Upgrading to v9 | React DayPicker
This release includes important updates related to accessibility, styles, internationalization and performance. See the changelog for the full list of changes.
daypicker.dev
가장 먼저 사용하는 React DayPicker를 최신 버전으로 받아줍시다.
npm install react-day-picker@latest
props로 인해 에러가 발생했던 만큼, 컴포넌트 chevron을 불러오는 방식에 변화가 생겼습니다. calendar의 components 코드를 변경해 줍시다.
...
components={{
Chevron: (props) => {
if (props.orientation === 'left') {
return <ChevronLeftIcon className="h-4 w-4" {...props} />;
}
return <ChevronRightIcon className="h-4 w-4" {...props} />;
},
}}
...
다음으로 마이그레이션 문서에 나와있는 대로 ClassNames를 변경해야 하는데요. 주된 변경사항은 다음과 같습니다.
Old Name | New Name |
button | button_previous, button_next |
button_reset | button_previous, button_next |
caption | month_caption |
caption_between | Removed |
caption_dropdowns | dropdowns |
caption_end | Removed |
caption_start | Removed |
cell | day - ⚠️ The previous day element is now day_button. |
day_disabled | disabled |
day_hidden | hidden |
day_outside | outside |
day_range_end | range_end |
day_range_middle | range_middle |
day_range_start | range_start |
day_selected | selected |
day_today | today |
dropdown_icon | chevron |
dropdown_month | months_dropdown |
dropdown_year | years_dropdown |
head | Removed |
head_cell | weekday |
head_row | weekdays |
multiple_months | Removed. Use data-multiple-months data attribute. |
nav_button | button_previous, button_next |
nav_button_next | button_next |
nav_button_previous | button_previous |
nav_icon | chevron, button_next, button_previous |
row | week |
table | month_grid |
tbody | weeks |
tfoot | footer |
vhidden | Removed |
weeknumber | week_number |
with_weeknumber | Removed. Use data-week-numbers data attribute. |
이대로 Calendar의 ClassNames를 변경하면...
...
.....
.........

UI가 조금 틀어집니다. (제 프로젝트 기준)
복사 붙여넣기를 선호하실 분들을 위해 제 코드를 첨부합니다.
하단 코드는 tailwind의 :has rounded 코드가 작동하지 않는 중인데, 일정이 급한 프로젝트를 하는 중이라 그대로 놔두었으니 그 부분은 감안해주시면 감사하겠습니다.
뭐 곧 shadcn이 calendar를 업데이트 해주겠죠 🤗
'use client';
import * as React from 'react';
import { ChevronLeftIcon, ChevronRightIcon } from 'lucide-react';
import { DayPicker } from 'react-day-picker';
import { cn } from '@/lib/utils';
import { buttonVariants } from '@/components/ui/button';
export type CalendarProps = React.ComponentProps<typeof DayPicker>;
function Calendar({ className, classNames, showOutsideDays = true, ...props }: CalendarProps) {
return (
<DayPicker
showOutsideDays={showOutsideDays}
className={cn('p-3', className)}
classNames={{
months: 'relative flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0',
month: 'space-y-4',
month_caption: 'flex justify-center pt-1 relative items-center',
caption_label: 'text-sm font-semibold',
nav: 'z-50 absolute top-1 right-0',
nav_button: cn(
buttonVariants({ variant: 'outline' }),
'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100'
),
nav_button_previous: 'absolute left-1',
nav_button_next: 'absolute right-1',
month_grid: 'w-full border-collapse space-y-1',
weekdays: 'flex',
weekday: 'text-muted-foreground w-9 font-normal text-[0.8rem]',
week: 'flex w-full mt-2',
day: 'day-cell h-9 w-9 text-center text-sm p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20',
day_button: 'day-button h-9 w-9 p-0 font-normal aria-selected:opacity-100',
range_end: 'day-range-end',
selected:
'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground',
today: 'bg-accent text-accent-foreground',
outside: 'day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground',
disabled: 'text-muted-foreground opacity-50',
range_middle: 'aria-selected:bg-accent aria-selected:text-accent-foreground',
hidden: 'invisible',
...classNames,
}}
components={{
Chevron: (props) => {
if (props.orientation === 'left') {
return <ChevronLeftIcon className="h-4 w-4" {...props} />;
}
return <ChevronRightIcon className="h-4 w-4" {...props} />;
},
}}
{...props}
/>
);
}
Calendar.displayName = 'Calendar';
export { Calendar };
짜잔~ 여기까지 하면 완성입니다.
[출처]
Calendar
A date field component that allows users to enter and edit date.
ui.shadcn.com
Styling | React DayPicker
DayPicker comes with a minimal style inspired by MacOS date pickers, designed to be extended and customized.
daypicker.dev