변경 사항
변경 사항 (Changelog)
shadcn/ui의 최신 업데이트와 공지 사항을 소개하는 문서예요. 새 CLI, 블록 라이브러리, 여러 신규 컴포넌트와 테마·스타일 기능 등 주요 변화들이 시간순으로 정리되어 있어요. 프로젝트가 어떻게 진화해 왔는지 한눈에 파악하기 좋아요.
출처: 문서
본문
2024년 8월 - npx shadcn init
새 CLI를 사용할 수 있게 됐어요. 완전히 새로 쓴 것으로, 많은 새 기능과 개선이 담겨 있어요. 이제 npx shadcn add로 컴포넌트, 테마, 훅, 유틸 등 더 다양한 것을 설치할 수 있어요.
이는 여러분과 여러분의 LLM이 접근하고 사용할 수 있는 코드를 배포하는 큰 진전이에요.
- 먼저 CLI는 이제 모든 주요 React 프레임워크를 기본 지원해요. Next.js, Remix, Vite, Laravel. 그리고 새 앱에 init하면 기존 Tailwind 파일을 덮어쓰는 대신 업데이트해 줍니다.
- 컴포넌트는 이제 자신의 의존성을 함께 제공해요. 예를 들어 아코디언을 들면, 그것은 자체 Tailwind keyframes를 정의할 수 있어요. 프로젝트에 추가하면
tailwind.config.ts파일을 그에 맞게 업데이트해 줍니다. - URL로 원격 컴포넌트를 설치할 수도 있어요.
npx shadcn add https://acme.com/registry/navbar.json. - init 명령도 개선했어요. 프레임워크를 감지하고, 명령 한 번으로 완전히 새로운 Next.js 앱을 초기화할 수도 있어요.
npx shadcn init. - 여러분 자신의 컴포넌트 레지스트리를 배포할 수 있는 새 스키마를 만들었어요. URL을 지원하기 때문에 비공개 컴포넌트를 배포하는 데도 쓸 수 있어요.
- 그리고 더 나은 오류 처리와 모노레포 지원 같은 몇 가지 추가 업데이트도 있어요.
새 CLI를 오늘 바로 사용해 볼 수 있어요.
npx shadcn init sidebar-01 login-01
프로젝트 업데이트하기
기존 프로젝트를 새 CLI로 업데이트하려면, components.json 파일에 components, utils, ui, lib, hooks에 대한 import 별칭(alias)을 포함하도록 업데이트해 주세요.
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"tailwind": {
// ...
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
}
}
다른 import 별칭 접두어(예: ~)를 쓴다면 @를 여러분의 접두어로 바꾸세요.
2024년 4월 - Lift Mode 도입
블록에 Lift Mode라는 새 모드를 도입하고 있어요.
Lift Mode를 활성화하면 블록 템플릿에서 더 작은 컴포넌트를 자동으로 "리프트(끄집어내)" 복사·붙여넣기 할 수 있어요.
Lift Mode를 사용하면 카드, 버튼, 폼처럼 블록 템플릿을 구성하는 더 작은 컴포넌트를 복사해서 프로젝트에 바로 붙여넣을 수 있어요.
블록 페이지를 방문해 직접 사용해 보세요.
2024년 3월 - 블록 (Blocks) 도입
출시 이후 가장 많이 요청된 기능 중 하나는 레이아웃이었어요: 사이드바가 있는 관리자 대시보드, 마케팅 페이지 섹션, 카드 등.
오늘, 블록을 출시합니다.
블록은 앱을 만드는 데 사용할 수 있는 만들어진 컴포넌트들이에요. 완전히 반응형이고, 접근 가능하며, 조합(composable) 가능해서, shadcn/ui의 다른 컴포넌트들과 같은 원칙으로 만들어졌어요.
대시보드 레이아웃과 인증 페이지부터 시작하며, 앞으로 몇 주에 걸쳐 더 많은 블록을 추가할 계획이에요.
오픈소스
블록은 오픈소스예요. 소스는 GitHub에서 찾을 수 있어요. 프로젝트에 사용하거나, 커스터마이즈하고, 기여할 수 있어요.
블록 요청
"블록 요청(Request a Block)" 기능도 도입하고 있어요. 특정 블록이 보고 싶다면 GitHub에 요청을 만들면 되고, 커뮤니티가 투표하고 빌드할 수 있어요.
v0
v0 계정이 있다면 Edit in v0 기능을 사용해 v0에서 코드를 열고 프롬프트·추가 생성을 진행할 수 있어요.
그게 전부예요. 블록으로 무엇을 만들지 기대되네요.
2024년 3월 - Breadcrumb와 Input OTP
새로운 Breadcrumb 컴포넌트와 Input OTP 컴포넌트를 추가했어요.
Breadcrumb
접근 가능하고 유연한 브레드크럼 컴포넌트. 접힌 항목, 커스텀 구분자, 자체 라우팅 <Link /> 사용을 지원하고, 다른 shadcn/ui 컴포넌트와 조합할 수 있어요.
Input OTP
완전한 기능을 갖춘 Input OTP 컴포넌트. 숫자·영숫자 코드, 커스텀 길이, 복사-붙여넣기를 지원하고 접근 가능해요. Input OTP는 @guilherme_rodz의 input-otp 위에 만들어졌어요.
v0가 있다면 새 컴포넌트들을 생성에 사용할 수 있어요.
2023년 12월 - 새 컴포넌트, CLI 등
shadcn/ui에 새 컴포넌트를 추가하고 CLI를 크게 개선했어요.
새로운 것들의 간단한 개요는 다음과 같아요:
- Carousel - 모션, 스와이프 제스처, 키보드 지원이 있는 캐러셀 컴포넌트.
- Drawer - 모바일에서 멋지게 보이는 드로어 컴포넌트.
- Pagination - 페이지 탐색, 이전·다음 버튼이 있는 페이지네이션 컴포넌트.
- Resizable - 크기 조절 가능한 패널 그룹과 레이아웃을 만드는 리사이저블 컴포넌트.
- Sonner - 마지막으로 필요하게 될 토스트 컴포넌트.
- CLI 업데이트 - 커스텀 Tailwind prefix와
tailwind.config.ts지원.
Carousel
모션, 스와이프 제스처, 키보드 지원이 있는 완전한 기능의 캐러셀 컴포넌트를 추가했어요. Embla Carousel 위에 만들어졌어요.
무한 루프, 자동 재생, 세로 방향 등을 지원해요.
Drawer
오, 드로어 컴포넌트 😍. emilkowalski_의 Vaul 위에 만들어졌어요.
모바일에서 다음 드로어를 열어 보세요. 정말 멋져요!
Pagination
페이지 탐색, 이전·다음 버튼이 있는 페이지네이션 컴포넌트를 추가했어요. 간단하고 유연하며 프레임워크의 <Link /> 컴포넌트와 함께 동작해요.
Resizable
이 <Resizable /> 컴포넌트로 크기 조절 가능한 패널 그룹과 레이아웃을 만드세요.
<Resizable />는 bvaughn의 react-resizable-panels로 만들어졌어요. 마우스, 터치, 키보드를 지원해요.
Sonner
emilkowalski_의 또 하나. 마지막으로 필요하게 될 토스트 컴포넌트. Sonner가 이제 shadcn/ui에서 제공돼요.
CLI 업데이트
가장 많이 요청된 기능 중 하나였어요. 이제 커스텀 Tailwind prefix를 구성할 수 있고, CLI가 컴포넌트를 추가할 때 유틸리티 클래스에 자동으로 prefix를 붙여 줍니다.
즉 Docusaurus, Nextra 등의 기존 프로젝트에 shadcn/ui 컴포넌트를 쉽게 추가할 수 있게 됐어요. 기존 디자인 시스템에 충돌 없이 바로 끼워 넣을 수 있어요. 🔥
<AlertDialog className="tw-grid tw-gap-4 tw-border tw-bg-background tw-shadow-lg" />
cn, cva와 CSS 변수와도 잘 동작해요.
CLI는 이제 tailwind.config.ts도 감지해서 TypeScript 버전의 설정을 자동으로 추가해 줍니다.
그게 전부예요. 즐거운 휴일 보내세요.
2023년 7월 - JavaScript
이 프로젝트와 컴포넌트는 TypeScript로 작성되어 있어요. 프로젝트에도 TypeScript를 사용하는 걸 권장합니다.
다만 cli를 통해 사용할 수 있는 JavaScript 버전의 컴포넌트도 제공해요.
Would you like to use TypeScript (recommended)? no
TypeScript를 사용하지 않으려면 components.json 파일에서 tsx 플래그를 사용하면 돼요.
{
"style": "default",
"tailwind": {
"config": "tailwind.config.js",
"css": "src/app/globals.css",
"baseColor": "zinc",
"cssVariables": true
},
"rsc": false,
"tsx": false,
"aliases": {
"utils": "~/lib/utils",
"components": "~/components"
}
}
import 별칭을 구성하려면 다음 jsconfig.json을 사용하면 돼요:
{
"compilerOptions": {
"paths": {
"@/*": ["./*"]
}
}
}
2023년 6월 - 새 CLI, 스타일 등
오늘 여러분과 공유할 업데이트가 많아요:
- 새 CLI - CLI를 처음부터 다시 작성했어요. 이제 컴포넌트·의존성을 추가하고 import 경로를 구성할 수 있어요.
- 테마 - 테마에 CSS 변수를 쓸지, Tailwind CSS 유틸리티 클래스를 쓸지 선택할 수 있어요.
- 기본 색상 - 프로젝트의 기본 색상을 구성할 수 있어요. 컴포넌트의 기본 색상 팔레트를 만드는 데 사용돼요.
- React Server Components - React Server Components 사용을 해제할 수 있어요. CLI가
use client지시어를 자동으로 추가하거나 제거해 줍니다. - 스타일 - Style 이라는 새 개념을 소개해요. 스타일은 자체 컴포넌트, 애니메이션, 아이콘 등을 갖고 있어요.
- 퇴장 애니메이션 - 모든 컴포넌트에 퇴장 애니메이션을 추가했어요.
- 기타 업데이트 - 새
icon버튼 크기, 업데이트된sheet컴포넌트 등. - 프로젝트 업데이트 - 최신 변경 사항을 받도록 프로젝트를 업데이트하는 방법.
새 CLI
지난 몇 주 동안 새 CLI를 작업해 왔어요. 완전히 새로 쓴 것이며 많은 새 기능과 개선이 포함돼 있어요.
init
npx shadcn-ui@latest init
init 명령을 실행하면 components.json을 구성하기 위해 몇 가지 질문을 받게 됩니다:
Which style would you like to use? › Default
Which color would you like to use as base color? › Slate
Where is your global CSS file? › › app/globals.css
Do you want to use CSS variables for colors? › no / yes
Where is your tailwind.config.js located? › tailwind.config.js
Configure the import alias for components: › @/components
Configure the import alias for utils: › @/lib/utils
Are you using React Server Components? › no / yes
이 파일은 컴포넌트에 대한 모든 정보를 담고 있어요: 어디에 설치할지, import 경로, 스타일링 방법 등.
이 파일로 컴포넌트의 import 경로를 바꾸거나, baseColor를 설정하거나, 스타일링 방법을 바꿀 수 있어요.
{
"style": "default",
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/app/globals.css",
"baseColor": "zinc",
"cssVariables": true
},
"rsc": false,
"aliases": {
"utils": "~/lib/utils",
"components": "~/components"
}
}
즉 이제 src, app 디렉터리를 포함한 어떤 디렉터리 구조에서도 CLI를 사용할 수 있어요.
add
npx shadcn-ui@latest add
add 명령이 훨씬 강력해졌어요. UI 컴포넌트를 추가할 수 있을 뿐 아니라 더 복잡한 컴포넌트도 import 할 수 있게 됐어요(곧 제공 예정).
CLI가 모든 컴포넌트와 의존성을 자동으로 해석하고, 사용자 설정에 맞게 포맷한 뒤 프로젝트에 추가해 줍니다.
diff (실험적)
npx shadcn-ui diff
업스트림 업데이트를 추적하는 데 도움을 주는 diff 명령도 새로 도입하고 있어요.
이 명령으로 업스트림 저장소에서 무엇이 바뀌었는지 확인하고 그에 맞게 프로젝트를 업데이트할 수 있어요.
diff 명령을 실행하면 업데이트가 가능한 컴포넌트 목록을 얻을 수 있어요:
npx shadcn-ui diff
The following components have updates available:
- button
- /path/to/my-app/components/ui/button.tsx
- toast
- /path/to/my-app/components/ui/use-toast.ts
- /path/to/my-app/components/ui/toaster.tsx
그런 다음 diff [component]를 실행해 변경 사항을 확인하세요:
npx shadcn-ui diff alert
const alertVariants = cva(
- "relative w-full rounded-lg border",
+ "relative w-full pl-12 rounded-lg border"
)
CSS 변수 또는 Tailwind 색상으로 테마 구성하기
테마에 CSS 변수를 쓸지, Tailwind CSS 유틸리티 클래스를 쓸지 선택할 수 있어요.
새 컴포넌트를 추가할 때 CLI가 components.json 설정에 따라 올바른 테마 방법을 자동으로 사용해 줍니다.
유틸리티 클래스
<div className="bg-zinc-950 dark:bg-white" />
테마에 유틸리티 클래스를 쓰려면 components.json 파일에서 tailwind.cssVariables를 false로 설정하세요.
{
"tailwind": {
"config": "tailwind.config.js",
"css": "app/globals.css",
"baseColor": "slate",
"cssVariables": false
}
}
CSS 변수
<div className="bg-background text-foreground" />
테마에 CSS 변수 클래스를 쓰려면 components.json 파일에서 tailwind.cssVariables를 true로 설정하세요.
{
"tailwind": {
"config": "tailwind.config.js",
"css": "app/globals.css",
"baseColor": "slate",
"cssVariables": true
}
}
기본 색상 (Base color)
이제 프로젝트의 기본 색상을 구성할 수 있어요. 컴포넌트의 기본 색상 팔레트를 만드는 데 사용돼요.
{
"tailwind": {
"config": "tailwind.config.js",
"css": "app/globals.css",
"baseColor": "zinc",
"cssVariables": false
}
}
gray, neutral, slate, stone, zinc 중에서 선택할 수 있어요.
cssVariables가 true면 globals.css 파일에 기본 색상을 CSS 변수로 설정하고, false면 컴포넌트에 Tailwind CSS 유틸리티 클래스를 인라인으로 넣어 줍니다.
React Server Components
React Server Components를 지원하지 않는 프레임워크를 쓴다면 rsc를 false로 설정해 해제할 수 있어요. 컴포넌트를 추가할 때 use client 지시어를 자동으로 추가하거나 제거해 줍니다.
{
"rsc": false
}
스타일 (Styles)
Style 이라는 새 개념을 도입하고 있어요.
스타일은 시각적 기반이라고 생각하면 돼요: 모양, 아이콘, 애니메이션, 타이포그래피. 스타일은 자체 컴포넌트, 애니메이션, 아이콘 등을 갖고 있어요.
default와 new-york 두 가지 스타일을 제공해요(더 많은 것이 곧 제공될 예정).
default 스타일은 여러분이 익숙한 스타일이에요. 이 프로젝트 시작부터 사용해 온 것이죠. 아이콘에 lucide-react, 애니메이션에 tailwindcss-animate를 사용해요.
new-york 스타일은 새로운 스타일이에요. 더 작은 버튼, 그림자가 있는 카드, 그리고 Radix Icons의 새 아이콘 세트를 제공해요.
init 명령을 실행하면 어떤 스타일을 쓸지 질문을 받고, 이는 components.json 파일에 저장돼요.
{
"style": "new-york"
}
테마 (Theming)
스타일을 기반으로 시작한 다음 CSS 변수 또는 Tailwind CSS 유틸리티 클래스로 테마를 적용해 컴포넌트의 모습을 완전히 바꿀 수 있어요.
퇴장 애니메이션 (Exit animations)
모든 컴포넌트에 퇴장 애니메이션을 추가했어요. 아래 콤보박스를 클릭하면 미묘한 퇴장 애니메이션을 볼 수 있어요.
애니메이션은 유틸리티 클래스로 커스터마이즈할 수 있어요.
기타 업데이트 (Other updates)
Button
- 새 크기
icon을 추가했어요:
Sheet
- 다른 요소들과 맞추기 위해
position을side로 이름을 바꿨어요.
sizeprops를 제거했어요. 반응형 크기에는className="w-[200px] md:w-[450px]"를 사용하세요.
프로젝트 업데이트하기 (Updating your project)
복사·붙여넣기 방식을 따르므로 최신 변경 사항을 받으려면 프로젝트를 수동으로 업데이트해야 해요.
components.json 추가하기
루트에 components.json 파일을 만드세요:
{
"style": "default",
"rsc": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "app/globals.css",
"baseColor": "slate",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}
tailwind.css와 aliases의 값을 프로젝트 구조에 맞게 업데이트하세요.
Button
buttonVariants에 icon 크기를 추가하세요:
const buttonVariants = cva({
variants: {
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
})
Sheet
sheet.tsx의 내용을 다음으로 교체해 주세요:
"use client"
import * as React from "react"
import * as SheetPrimitive from "@radix-ui/react-dialog"
import { cva, type VariantProps } from "class-variance-authority"
import { X } from "lucide-react"
import { cn } from "@/lib/utils"
const Sheet = SheetPrimitive.Root
const SheetTrigger = SheetPrimitive.Trigger
const SheetClose = SheetPrimitive.Close
const SheetPortal = ({
className,
...props
}: SheetPrimitive.DialogPortalProps) => (
<SheetPrimitive.Portal className={cn(className)} {...props} />
)
SheetPortal.displayName = SheetPrimitive.Portal.displayName
const SheetOverlay = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Overlay
className={cn(
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
)}
{...props}
ref={ref}
/>
))
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
const sheetVariants = cva(
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
{
variants: {
side: {
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
bottom:
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
right:
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
},
},
defaultVariants: {
side: "right",
},
}
)
interface SheetContentProps
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
VariantProps<typeof sheetVariants> {}
const SheetContent = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Content>,
SheetContentProps
>(({ side = "right", className, children, ...props }, ref) => (
<SheetPortal>
<SheetOverlay />
<SheetPrimitive.Content
ref={ref}
className={cn(sheetVariants({ side }), className)}
{...props}
>
{children}
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</SheetPrimitive.Close>
</SheetPrimitive.Content>
</SheetPortal>
))
SheetContent.displayName = SheetPrimitive.Content.displayName
const SheetHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-2 text-center sm:text-left",
className
)}
{...props}
/>
)
SheetHeader.displayName = "SheetHeader"
const SheetFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className
)}
{...props}
/>
)
SheetFooter.displayName = "SheetFooter"
const SheetTitle = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Title
ref={ref}
className={cn("text-lg font-semibold text-foreground", className)}
{...props}
/>
))
SheetTitle.displayName = SheetPrimitive.Title.displayName
const SheetDescription = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
SheetDescription.displayName = SheetPrimitive.Description.displayName
export {
Sheet,
SheetTrigger,
SheetClose,
SheetContent,
SheetHeader,
SheetFooter,
SheetTitle,
SheetDescription,
}
position을side로 이름을 바꾸세요.
- <Sheet position="right" />
+ <Sheet side="right" />
감사의 말
이 프로젝트를 사용하고, 피드백을 주고, 기여해 준 모든 분께 감사해요. 정말 고맙습니다 🙏