---
name: vendor-react-component
description: Integrate a third-party React component (React Bits, 21st.dev, a shadcn registry item, a pasted "Integrate the <X /> component" prompt) into an existing shadcn + Tailwind + TypeScript app without breaking its design system. Use whenever a user pastes component source and asks to "add this", "integrate this component", or "adjust it to our design system".
---

# Vendor a React component

Third-party components arrive with their own aliases, animation libraries, colours and assumptions. The job is to make them native to the host project, not to paste them in.

## Workflow

1. **Confirm the host.** `components.json` present? `@/` alias → `src/`? `cn` in `src/lib/utils.ts`? Tailwind config with `darkMode: ["class"]`? If any is missing, set it up first (`npx shadcn@latest init`).
2. **Put it where shadcn expects it.** `src/components/ui/<kebab-name>.tsx`. Named export + default export. One component per file; demos do NOT get copied — they become the usage snippet.
3. **Fix imports before anything else.**
   - `@workspace/ui/lib/utils`, `~/lib/utils` → `@/lib/utils`
   - `motion/react` → `framer-motion` if that is what the project uses (or install `motion` deliberately — never both)
   - `react-icons` → `lucide-react` equivalents when lucide is already a dependency
   - drop `'use client'` in Vite/React Router apps
4. **Type it.** Replace `any`/untyped props with an exported `XProps` interface. Keep prop names from the original so the upstream docs stay valid.
5. **Re-token colours.** Hard-coded `#e5e7eb`, `bg-gray-200`, `text-white` → project tokens (`bg-background`, `text-foreground`, accent colour). A component that hard-codes a page colour for a masking trick must read `hsl(var(--background))` instead.
6. **Contain global side effects.** Inline `<style>` blocks: prefix every class/keyframe with the component name so two instances or two components never collide. Filters with hard-coded SVG ids: hoist the `<defs>` once at app root and reference the id.
7. **Respect the platform.** Add `prefers-reduced-motion` handling, pause canvas/rAF loops when off-screen (`IntersectionObserver`) and when `document.hidden`, clean up every listener/interval in the effect return.
8. **Wire it in** using the usage example from the prompt, adjusted to the page's real data. Keep a `key` on previews that must re-run per selection.
9. **Verify** with the type-checker (`tsc --noEmit`), lint, a production build, and a look at the rendered page at phone and desktop widths. Then add the component to the project's catalog/docs if one exists.

## Traps

- Two Reacts: never add `react` as a dependency of a vendored package; "Invalid hook call" after an install is usually Vite's dep re-optimisation — restart the dev server before debugging.
- Tailwind arbitrary values with parentheses (e.g. `[filter:drop-shadow(…)]`) are often not emitted; use an inline style.
- Framer-motion `whileInView`, WAAPI and rAF do not advance in a background tab — verify via DOM/JS, not screenshots, when automating.
- Components that open the camera/microphone: never per instance; one shared stream behind explicit consent.
