# coss vs shadcn/Radix assumptions Use this guide when adapting snippets that were originally written with shadcn/Radix mental models. ## Core idea coss is close to shadcn ergonomically, but its primitives and composition model are aligned to Base UI patterns. ## High-impact differences - Do not assume every shadcn pattern translates 1:1. - Verify trigger and popup composition from coss docs before coding. - Apply `asChild` -> `render` only on coss parts that explicitly support `render`. - Prefer coss component names and exports as documented (`DialogPopup`, `MenuPopup`, `SelectPopup`, etc.). - Some legacy aliases may exist, but primary coss names should be preferred in new examples. - Prefer styled coss exports by default (for example `Slider`, `SliderValue`) and use `*Primitive` only for advanced/custom composition. - When only Base UI helpers are needed (`useRender`, `mergeProps`, `CSPProvider`, `DirectionProvider`), prefer `@coss/ui/base-ui/*` re-exports over direct `@base-ui/react` dependency. - For Select migration, replace children-only option derivation with an `items`-first pattern where possible, then map options consistently in `SelectPopup`. - For OTP fields, migrate off the `input-otp` package to coss `@coss/otp-field`: rename components (`OTPField`, `OTPFieldInput`, `OTPFieldSeparator`), use `length` and `onValueChange`, and drop `InputOTPGroup` / slot `index` (see example below). ## Practical migration examples Use these snippets as fast conversion templates when migrating shadcn/Radix code. ### Composition: `asChild` -> `render` ```tsx // shadcn/Radix ``` ```tsx // coss/Base UI }>Open ``` ### Menu actions: `onSelect` -> `onClick` ```tsx // shadcn/Radix Open ``` ```tsx // coss/Base UI Open ``` ### Select: `items`-first + placeholder on `SelectValue` ```tsx // shadcn/Radix ``` ```tsx // coss/Base UI const items = [ { label: "Next.js", value: "next" }, { label: "Vite", value: "vite" }, ]; ``` ### Toggle Group: `type` -> `multiple` ```tsx // shadcn/Radix Daily Weekly ``` ```tsx // coss/Base UI Daily Weekly ``` ### Slider: scalar single-value usage in coss ```tsx // shadcn/Radix ``` ```tsx // coss/Base UI ``` ### Accordion: `type/collapsible` -> coss defaults ```tsx // shadcn/Radix ... ``` ```tsx // coss/Base UI ... ``` ### OTP Field: `input-otp` package → `@coss/otp-field` coss wraps [Base UI OTP Field](https://base-ui.com/react/components/otp-field) (`OTPFieldPreview`). Remove the `input-otp` dependency and align with the new names and root props. ```tsx // shadcn / input-otp ``` ```tsx // coss ``` - `InputOTPGroup` is not used; optional `size="lg"` lives on `OTPField`. - Render one `OTPFieldInput` per character in order; do not pass `index`. ## Migration checklist 1. Confirm the exact coss imports from docs. 2. Confirm child structure requirements (trigger/header/panel/footer/items/groups). 3. Confirm prop names and semantics from the coss docs page. 4. Validate with at least one coss particle example. ## Per-component migration notes For the full component registry, see `../component-registry.md`. ## Anti-patterns - Copy/paste shadcn examples and only change import path. - Using undocumented props because they exist in other ecosystems. - Omitting required subcomponents in overlays/forms because the source snippet did.