Create new Prototyper UI components from scratch, including Base UI primitives, registry entries, examples, and MDX documentation.
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
- Animate UIPrototyper UI animation conventions for writing, reviewing, or debugging transition code, overlay enter/exit, press feedback, and prefers-reduced-motion.
- Build PrimitiveBuild foundational UI primitives from scratch with strong ARIA, keyboard, focus, and state handling.
- Prototyper UIPrototyper UI component library (Tailwind CSS v4 + Base UI) for installing components, customizing themes, and using component documentation.