ð Radix ãã Base UI ãžç§»è¡ãã â shadcn/ui ã§å®éã«ã€ãŸãã5ç®æãšæé
RadixããBase UIãžã®migration(ç§»è¡)ã§å®éã«èžãèœãšã穎ããã³ãŒãä»ãã§è§£èª¬ãããrefã»åã»data-testidã»ã¢ãã¡ãŒã·ã§ã³ã»tree-shakingã®å¯ŸåŠãšãããã±ãŒãžåã@base-ui/reactã«å€ãã£ãä»¶ãç§»è¡ããªãå€æåºæºãŸã§(2026幎8ææç¹)ã
ååãshadcn/uiã®ããã©ã«ããBase UIã«å€ãã£ãè©±ãæžããããã®ãšãã¯ãæ¥ãã§ç§»è¡ããªããŠãããã§ç· ãããä»åã¯éã§ãå®éã«æãåãããŠç§»è¡ãããšãã«äœãèµ·ããããæžãã
çµè«ããèšããšãasChildãrenderã«æ©æ¢°çœ®æããã ãã§ã¯çµãããªããrefãåãdata-testidãã¢ãã¡ãŒã·ã§ã³ããã³ãã«æ§æââ5ç®æã§éãã«å£ããããããå£ãæ¹ãã©ãããåãã§ãã¯ã¯éãã®ã«å®è¡æã ãå€ããšãããã€ã§ãå°å³ã«åä»ã ã£ãã
Radix ãš Base UIãç§»è¡ååŸã®å·®å
å ã«å šäœåã衚ã«ããŠããã
| é ç® | Radix | Base UI |
|---|---|---|
| ããã±ãŒãž | ã³ã³ããŒãã³ãåäœã§åå²(@radix-ui/react-dialogãªã©) | @base-ui/reactã«çµ±å(v1.0.0ã§æ§å@base-ui-components/reactããæ¹å) |
| å·®ãèŸŒã¿æ¹ | asChild | render |
| ã³ã³ãã³ãé 眮 | Contentãé
眮ãå
容ãæã€ | Positioner(é
眮)ãšContent(å
容)ãåé¢ |
| ã©ãã« | Labelåäœã§çœ®ãã | Groupã§å²ãå¿
èŠããã |
| ref | forwardRefãããã°èªåã§ç¹ãã | æž¡ãèŠçŽ èªäœãrefãåãåããå¿ èŠããã |
| data-testid | asChildãåèŠçŽ ã«ãã®ãŸãŸåŒãç¶ã | renderã«æç€ºããªããšæ¶ãã |
| ã¢ãã¡ãŒã·ã§ã³å¶åŸ¡ | data-state="open"ãªã©ã®åäžå±æ§ | data-open/data-closed(ã¢ãã¡ãŒã·ã§ã³çš)ãšdata-starting-style/data-ending-style(ãã©ã³ãžã·ã§ã³çš)ãåããã |
| import | ããã±ãŒãžåäœ | ãµããã¹åäœ(@base-ui/react/popoverãªã©) |
ãããããå®éã«ã³ã³ããŒãã³ãã1ã€ãã€ç§»è¡ãããšãã«èžãã èœãšã穎ãé çªã«æžãã
1. ããã±ãŒãžåãããããå€ã
äžçªæåæãããã®ãããã ã£ãã@base-ui-components/reactã§ã€ã³ã¹ããŒã«ããããšããŠæ°ã¥ããã
2025幎12æ11æ¥ãªãªãŒã¹ã®v1.0.0ã§ãããã±ãŒãžã@base-uiorgã«ç§»ç®¡ãããååã@base-ui-components/reactãã@base-ui/reactã«å€ãã£ãŠããããªãªãŒã¹ããŒãã¯ããã**ç Žå£ç倿Ž(breaking change)**ãšããŠåé¡ããŠããŠãéæšå¥šã®ãŸãŸäœµåããããšããæžãæ¹ã¯ããŠããªãã
# æ§å(v1.0.0 ã§çœ®ãæãããã)
npm install @base-ui-components/react
# çŸåš
npm install @base-ui/react
ãããäžã®ãã¥ãŒããªã¢ã«ãAIã®åçã¯ãå¹³æ°ã§æ§åã®ãŸãŸåºãŠããããšããããç§»è¡ãå§ããåã«ããŸãããã ã確èªããŠãããšäºæ ããªãã
2. ref: renderã«æž¡ããèŠçŽ ã¯èªåã§refãåãåããå¿ èŠããã
asChildã¯RadixåŽãforwardRefãèŠã€ããŠèªåã§ç¹ãã§ããããrenderã¯ããã§ã¯ãªããæž¡ãèŠçŽ èªäœãrefãåãåããæ§é ã«ãªã£ãŠããå¿
èŠãããã
// Radix â MyButtonã«forwardRefãããã°èªåã§ç¹ãã
<Dialog.Trigger asChild>
<MyButton>éã</MyButton>
</Dialog.Trigger>
// Base UI â MyButtonåŽã§refãåãåããªããšãã¯ãªãã¯ã¯éãã®ã«ãã©ãŒã«ã¹ç®¡çã ãåããªã
<Dialog.Trigger render={<MyButton />}>
éã
</Dialog.Trigger>
ãã©ãŒã«ã¹å¶åŸ¡ãã¢ãã¡ãŒã·ã§ã³é£æºã§refã䜿ã£ãŠããã³ã³ããŒãã³ãããç§»è¡ãããšããã®ãºã¬ãæ©ãã«èžãã§åŠç¿ã§ãããåŸåãã«ãããšãçŽãã¹ãç®æãç©ã¿äžãã£ãŠããæ°ã¥ãããšã«ãªãã
3. å: render propã¯ã³ã³ããŒãã³ãã®namespaceåãšåã¿åããã
Base UIã¯åã«å³ãããåã³ã³ããŒãã³ãã¯Component.Root.Propsã®ããã«namespaceã§åãæã£ãŠããŠãrender颿°ã®ç¬¬2åŒæ°ã«ã¯ç¶æ
(state)ãæž¡ã£ãŠããã
import { Popover } from "@base-ui/react/popover";
<Popover.Positioner
render={(props, state) => (
<div {...props} data-open={state.open}>
{/* ... */}
</div>
)}
/>
Radixã§ã¯çŽ éãããŠãã埮åŠãªåã®ãºã¬ããBase UIã§ã¯ã³ã³ãã€ã«ãšã©ãŒãšããŠè¡šé¢åããããšããããç§»è¡äžã«åãšã©ãŒãå¢ããããã©ã€ãã©ãªã®ããã§ã¯ãªãå ã ãã£ããºã¬ãèŠããããã«ãªã£ããšèããã»ããããã
4. data-testid: æç€ºããªããšæ¶ãã
asChildã¯data-testidããã®ãŸãŸåèŠçŽ ã«åŒãç¶ãã§ããããrenderã¯æž¡ããèŠçŽ èªäœãpropsãåãåãæ§é ãªã®ã§ãrender={<Button />}ãšæžããã ãã§ã¯data-testidãæ¶ããã
// data-testidãæ¶ãã
<Dialog.Trigger render={<Button />}>éã</Dialog.Trigger>
// æç€ºçã«æããçŽã
<Dialog.Trigger render={<Button data-testid="dialog-trigger" />}>éã</Dialog.Trigger>
getByRoleããŒã¹ã®ãã¹ãã¯ARIA屿§ããã®ãŸãŸä¿ãããã®ã§åœ±é¿ãåãã«ããããgetByTestIdã«äŸåãããã¹ãã¹ã€ãŒãã¯ã³ã³ããŒãã³ãç§»è¡ã®ãã³ã«1ä»¶ãã€èœã¡ããç§»è¡å¯Ÿè±¡ã®ã³ã³ããŒãã³ãé
äžã§data-testidã䜿ã£ãŠããç®æãå
ã«æŽãåºããŠãããšæ
ãŠãã«æžãã
5. ã¢ãã¡ãŒã·ã§ã³: data-stateäŸåã®CSSã¯ãã®ãŸãŸåããªã
ãããäžçªæžãçŽããå€ãã£ãç®æã ãRadixã¯CSSã¢ãã¡ãŒã·ã§ã³åãã«data-state="open"ã®ãããªåäžå±æ§ãåºãèšèšã ã£ãããBase UIã¯çšéå¥ã«å±æ§ãåãããŠããã
data-open/data-closedâ CSSã¢ãã¡ãŒã·ã§ã³(@keyframes)åãdata-starting-style/data-ending-styleâ CSSãã©ã³ãžã·ã§ã³åã
/* CSSãã©ã³ãžã·ã§ã³: ééã®éäžã§ãã£ã³ã»ã«ãããŠãèªç¶ã«æ»ã */
.Popup {
transition: transform 150ms, opacity 150ms;
&[data-starting-style],
&[data-ending-style] {
opacity: 0;
transform: scale(0.9);
}
}
Motionã®ãããªã©ã€ãã©ãªãšçµã¿åãããå Žåã¯ãã¢ã³ããŠã³ããAnimatePresenceã«ä»»ããããkeepMountedã§èŠçŽ ãDOMã«æ®ãããŸãŸopenã®ç¶æ
ã§ã¹ã¿ã€ã«ãåãæ¿ããããéžã¶å¿
èŠããããåè
ã¯ããããªãŒããŒã®ããã«éãããDOMããæ¶ãããã±ãŒã¹ãåŸè
ã¯DOMã«æ®ãããŸãŸã¢ãã¡ãŒã·ã§ã³ã ãåãæ¿ãããã±ãŒã¹åãã ã
ã¡ãªã¿ã«ãPopoverã»Tooltipã»Navigation Menuã®Positionerã§Firefoxã ãããŠã³ãæã®ãã©ã³ãžã·ã§ã³ã厩ããäžå ·åã以åãã£ãŠããããv1.0.0ã§ä¿®æ£ãããŠãã(Popoverã¯ããã«ãã¥ãŒããŒãè¿œåŸæã®ãã©ã³ãžã·ã§ã³ãé£ã¶äžå ·åãåãããŒãžã§ã³ã§çŽã£ãŠãã)ãã¢ãã¡ãŒã·ã§ã³ãå€ãªåããããå Žåãã³ãŒããããŸãããŒãžã§ã³ãçã£ãã»ããæ©ãããšãããã
6. tree-shaking: importã®æžãæ¹ã§å€ãã
ååã®èšäºã§ãBase UIã¯1ããã±ãŒãžã«ãŸãšãŸã£ãŠããåãRadixã»ã©ããªãŒã·ã§ã€ãã³ã°ãåããªããšãã話ãããããã³ãã«ãµã€ãºã®å·®èªäœã¯èšæž¬æžã¿ãªã®ã§ç¹°ãè¿ããªãããæžãæ¹ã§ããçšåºŠã¯åãè¿ããã
// ããã±ãŒãžçŽäžããåŒã¶(äžèŠãªã³ã³ããŒãã³ããŸã§å·»ã蟌ã¿ããã)
import { Popover } from "@base-ui/react";
// ãµããã¹ããåŒã¶(ã³ã³ããŒãã³ãåäœã§importã§ãã)
import { Popover } from "@base-ui/react/popover";
ç§»è¡ã®ã€ãã§ã«importæããµããã¹åœ¢åŒã«æããŠãããšããã«ãåŸã®ãµã€ãºã§æããã«ããããã³ãã«ã¢ãã©ã€ã¶ã§ç§»è¡ååŸã1åãã€èšæž¬ããŠãããšãããšã§ããªããéããªã£ãæ°ããããã§æ©ãŸãªããŠæžãã
ç§»è¡ã®æé
ãããŸã§ããå®éã«æãåããé çªã«äžŠã¹çŽãã
- ããã±ãŒãžåã確èªããã
@base-ui/reactã«ãªã£ãŠããã(ãªã£ãŠããªããã°ãŸãããã ãçŽã) - ã³ã³ããŒãã³ãåäœã§1ã€ãã€ç§»è¡ãããrefã䜿ã£ãŠããç®æ(ãã©ãŒã«ã¹å¶åŸ¡ã»ã¢ãã¡ãŒã·ã§ã³é£æº)ããçæãããšåŸåãæ¥œã«ãªã
- åãšã©ãŒã朰ãããšã©ãŒãå¢ãããå ã ãã£ããºã¬ãèŠããããã«ãªã£ããšæãã
data-testidãæããçŽããäºåã«å¯Ÿè±¡ã³ã³ããŒãã³ãé äžã®äœ¿çšç®æãæŽãåºããŠãã- ã¢ãã¡ãŒã·ã§ã³ãä»ãçŽãã
data-open/data-closedãšdata-starting-style/data-ending-styleãçšéã§äœ¿ãåãã - importããµããã¹åœ¢åŒã«æãããã«ããµã€ãºãç§»è¡ååŸã§èšæž¬ãã
1ã³ã³ããŒãã³ããç§»ããŠããã¹ããéããã³ãããããããã®å°ãããµã€ã¯ã«ã§åãã®ãçµå±äžçªæ©ãã
ç§»è¡ããªã倿ããããã
ãããŸã§æžããŠãããŠãªãã ããä»ããå šéšç§»è¡ããå¿ èŠããããããžã§ã¯ãã¯ããå€ããªããšæãã
- Comboboxã»Autocompleteã»Number Fieldã䜿ãäºå®ããªã(v1.0.0ã®ãªãªãŒã¹ããŒãã§æãå ¥ã£ãŠããé¡ã¶ãã§ãBase UIåŽãåãå ¥ããŠããé åãRadixåŽã®å¯Ÿå¿ç¶æ³ã¯åèªã§ç¢ºèªããŠã»ãã)
- ãã³ãã«ãµã€ãºãã·ãã¢ã§ãtree-shakingã®å·®ã蚱容ã§ããªã
getByTestIdã«äŸåãããã¹ãã倧éã«ãããæžãæãã³ã¹ããç§»è¡ã®ã¡ãªãããäžåã- ä»ã®Radixæ§æãå®å®ããŠããŠãä»ã«åªå ãã¹ãäœæ¥ããã
Radixã¯å»æ¢ãããŠããªããæ°èŠãããžã§ã¯ãã¯Base UIãçŽ çŽã«éžãã§ãããšæãããæ¢åãããžã§ã¯ãã¯ãComboboxç³»ã®æ©èœãèŠãããã«ãªã£ãããããã ãéšåå°å ¥ããããããã®æž©åºŠæã§ãååã ãšæããå šéšç§»è¡ãããããªããã®äºæã§æ©ãå¿ èŠã¯ãªãã