create-component
Back to list

PrototyperAI/create-component

Create Component

GitHub

Create new Prototyper UI components from scratch, including Base UI primitives, registry entries, examples, and MDX documentation.

4 installsFrontendArchitectureCraft0 demos

Real-world examples not captured yet

This skill shows a gallery of live HTML demos (CDN libraries welcome — GSAP, Three.js, etc.) rendered inline via srcdoc.

Skill markdown
# Prototyper UI — Creating Components

End-to-end guide for building new components in the Prototyper UI library. Templates first, then decision trees, then architecture patterns, then registration and documentation.

---

## 1. Copy-Paste Templates

These four templates cover every component shape. Pick one, fill in the blanks.

### Template A: Simple Component (no sub-components)

Used by: Button, Toggle

```tsx
"use client";

import { ComponentName as ComponentNamePrimitive } from "@base-ui/react/component-name";
import { cva, type VariantProps } from "class-variance-authority";

import { cn } from "@/lib/utils";

const componentNameVariants = cva(
  [
    "inline-flex items-center justify-center gap-2 whitespace-nowrap",
    "rounded-md text-sm font-medium",
    "transition-[color,background-color,border-color,box-shadow,opacity] duration-150 ease-smooth",
    "motion-reduce:transition-none",
    "focus-visible:focus-ring",
    "disabled:status-disabled",
    "no-highlight",
    "[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
  ],
  {
    variants: {
      variant: {
        default:
          "bg-primary text-primary-foreground hover-only:hover:bg-primary-hover",
        outline: "border border-border bg-surface hover-only:hover:bg-accent",
      },
      size: {
        default: "h-9 px-4",
        sm: "h-8 px-3 text-xs",
        lg: "h-10 px-6",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  },
);

function ComponentName({
  className,
  variant,
  size,
  ...props
}: ComponentNamePrimitive.Props & VariantProps<typeof componentNameVariants>) {
  return (
    <ComponentNamePrimitive
      data-slot="component-name"
      className={cn(componentNameVariants({ variant, size }), className)}
      {...props}
    />
  );
}

export { ComponentName, componentNameVariants };
```

### Template B: Compound Visual Component (Root + sub-components sharing context)

Used by: Switch, Slider, Meter, Progress

```tsx
"use client";

import * as React from "react";
import { ComponentName as ComponentNamePrimitive } from "@base-ui/react/component-name";

import { cn } from "@/lib/utils";

// --- Context (only when sub-components need shared state) ---

type ComponentNameSize = "sm" | "md" | "lg";
type ComponentNameContextValue = { size: ComponentNameSize };
const ComponentNameCtx = React.createContext<ComponentNameContextValue>({
  size: "md",
});

// --- Root ---

function ComponentName({
  className,
  children,
  size = "md",
  ...props
}: ComponentNamePrimitive.Root.Props & { size?: ComponentNameSize }) {
  return (
    <ComponentNameCtx value={{ size }}>
      <ComponentNamePrimitive.Root
        data-slot="component-name"
        className={cn("group", className)}
        {...props}
      >
        {children}
      </ComponentNamePrimitive.Root>
    </ComponentNameCtx>
  );
}

// --- Sub-component: Track ---

function ComponentNameTrack({
  className,
  ...props
}: ComponentNamePrimitive.Track.Props) {
  const { size } = React.useContext(ComponentNameCtx);
  return (
    <ComponentNamePrimitive.Track
      data-slot="component-name-track"
      className={cn(
        "relative overflow-hidden rounded-full bg-muted",
        size === "sm" && "h-1",
        size === "md" && "h-2",
        size === "lg" && "h-3",
        className,
      )}
      {...props}
    />
  );
}

// --- Sub-component: Indicator ---

function ComponentNameIndicator({
  className,
  ...props
}: ComponentNamePrimitive.Indicator.Props) {
  return (
    <ComponentNamePrimitive.Indicator
      data-slot="component-name-indicator"
      className={cn(
        "h-full bg-primary",
        "transition-[width,background-color] duration-300 ease-smooth",
        "motion-reduce:transition-none",
        className,
      )}
      {...props}
    />
  );
}

export { ComponentName, ComponentNameTrack, ComponentNameIndicator };
```

### Template C: Positioned Overlay (Portal → Positioner → Popup)

Used by: Popover, Tooltip, Menu, Select, Combobox

```tsx
"use client";

import { ComponentName as ComponentNamePrimitive } from "@base-ui/react/component-name";

import { cn } from "@/lib/utils";

// --- Root (pass-through) ---

function ComponentName(props: ComponentNamePrimitive.Root.Props) {
  return <ComponentNamePrimitive.Root data-slot="component-name" {...props} />;
}

// --- Trigger ---

function ComponentNameTrigger({
  className,
  ...props
}: ComponentNamePrimitive.Trigger.Props) {
  return (
    <ComponentNamePrimitive.Trigger
      data-slot="component-name-trigger"
      className={cn(className)}
      {...props}
    />
  );
}

// --- Content (wraps Portal → Positioner → Popup internally) ---

function ComponentNameContent({
  className,
  align = "center",
  alignOffset = 0,
  side = "bottom",
  sideOffset = 4,
  ...props
}: ComponentNamePrimitive.Popup.Props &
  Pick<
    ComponentNamePrimitive.Positioner.Props,
    "align" | "alignOffset" | "side" | "sideOffset"

…

More from PrototyperAI