shadcn-composition-patterns

An agent skill that makes your coding agent build components the way shadcn builds its own.

Setup

Install the skill:

npx skills add AlexDemzz/skills --skill shadcn-composition-patterns -g

There’s nothing to configure. On each run, the agent reads your project’s base from the package.json beside components.json, in your ui package if you have a monorepo. Any @base-ui dependency means Base UI, otherwise radix-ui or @radix-ui/* means Radix. The base decides how a part becomes a link.

Then it lists your components directory before writing anything. If a primitive or a compound already covers the need, it stops and points you to it.

What it does

Your agent builds every component as a compound: one file, one prefixed family of parts, each part a single element you style and replace from the call site.

import { cn } from "@/lib/utils";
function Navbar({ className, ...props }: React.ComponentProps<"nav">) {  return (    <nav      className={cn("flex items-center gap-4 px-4 py-3", className)}      data-slot="navbar"      {...props}    />  );}
function NavbarTitle({ className, ...props }: React.ComponentProps<"h1">) {  return (    <h1      className={cn("text-base font-semibold", className)}      data-slot="navbar-title"      {...props}    />  );}
export { Navbar, NavbarTitle };

Hand it an existing file instead and it grades that file against the same laws, part by part.

When to reach for it

Every time a React component is created, changed or audited in a project with a components.json. Give it a component to build, or a file to review.

LawWhat it asks
Compound, prefixed named exports

One root plus parts, each a top-level export: NavbarTitle, never Navbar.Title.

Each part is one element

data-slot, className through cn and props spread last. Spacing between parts lives on the parent.

Variants at the call site

A one-off look is the consumer’s className. A cva variant needs several consumers.

Polymorphic root, navigation-agnostic

The consumer picks the element: render on Base UI, asChild on Radix. No auto-<Link>.

Styling from outside targets data-slot

[&_[data-slot=item-title]]:font-semibold on the root’s className.

Common questions

It’s working if

After a build, you get one file of prefixed parts, such as Navbar and NavbarTitle, already used in one place in your app with its own className. Lint and typecheck pass on the file.

After a review, you get every law per part, pass or fail, with the offending line and the fix.

Where it fits

shadcn-composition-patterns is the shadcn layer on top of vercel-composition-patterns, which covers the React theory: why compounds, why no boolean props, lifting state into a provider.

It lives in my skills repository on GitHub (opens in a new tab), next to design-to-shadcn, which skins shadcn primitives from a reference design, and grokbot-notify, which messages you in your Grok Bot chat when your agent finishes, gets blocked or needs your decision.