Composition
기본 props로 해결되지 않을 때, Vapor UI 컴포넌트의 구조를 직접 제어하는 방법을 설명합니다.기본 props로 해결되지 않을 때 두 가지 방법으로 커스터마이징할 수 있습니다. Primitive는 Vapor UI 컴포넌트의 내부 구조를 이루는 최소 단위 컴포넌트입니다.
- Primitive가 없는 컴포넌트 (
Button,Badge,TextInput등):renderprop으로 커스터마이징합니다. - Primitive가 있는 컴포넌트 (
Dialog,Sheet,Tabs등): Primitive를 직접 조합해 내부 구조를 제어합니다.
컴포넌트에 Primitive가 있는지 확인하려면 각 컴포넌트 문서의 Props Table에서 XxxPrimitive 항목을 찾으세요.
render prop으로 렌더링 제어하기
render prop은 두 가지 형태로 사용할 수 있습니다.
ReactElement 형태 — HTML 태그나 외부 컴포넌트로 교체할 때 사용합니다.
import { Button, Card, Dialog } from '@vapor-ui/core';
import Link from 'next/link';
// div → article로 교체
<Card.Root render={<article />} />
// Next.js Link로 교체
<Button render={<Link href="/home" />}>홈으로</Button>함수 형태 — 컴포넌트의 내부 state에 따라 렌더링 내용을 동적으로 바꿀 때 사용합니다. 함수는 (props, state) 두 인자를 받습니다.
import { Switch } from '@vapor-ui/core';
import { DarkIcon, LightIcon } from '@vapor-ui/icons';
export default function Example() {
return (
<Switch.Root
render={(props, state) => (
<span {...props}>{state.checked ? <DarkIcon /> : <LightIcon />}</span>
)}
/>
);
}Primitive 직접 조합하기
Primitive를 직접 조합하면 Portal 위치, Overlay 렌더링 순서 등 레이어 구조 전체를 제어할 수 있습니다.
import { Button, Dialog } from '@vapor-ui/core';
export default function Example() {
return (
<Dialog.Root>
<Dialog.Trigger>열기</Dialog.Trigger>
<Dialog.PortalPrimitive>
<Dialog.OverlayPrimitive />
<Dialog.PopupPrimitive>
<Dialog.Header>
<Dialog.Title>제목</Dialog.Title>
</Dialog.Header>
<Dialog.Body>내용</Dialog.Body>
<Dialog.Footer>
<Dialog.Close render={<Button>닫기</Button>} />
</Dialog.Footer>
</Dialog.PopupPrimitive>
</Dialog.PortalPrimitive>
</Dialog.Root>
);
}