- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Attachment
- Avatar
- Badge
- Breadcrumb
- Bubble
- Button
- Button Group
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Collapsible
- Combobox
- Command
- Context Menu
- Date Picker
- Dialog
- Drawer
- Dropdown Menu
- Empty
- Field
- Hover Card
- Input
- Input OTP
- Input Group
- Item
- Kbd
- Label
- Marker
- Menubar
- Message
- Message Scroller
- Native Select
- Navigation Menu
- Pagination
- Popover
- Progress
- Radio Group
- Resizable
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Spinner
- Switch
- Table
- Tabs
- Textarea
- Toggle
- Toggle Group
- Tooltip
- New Component
GIN Design is a component registry built on top of the shadcn registry system. It is not an npm package — components are distributed as source files that are copied directly into the consuming project via the shadcn CLI. This page documents how the repository is structured, how components are authored and registered, and how the documentation site works.
Architecture
The repository is a Next.js 16 App Router application that serves two purposes simultaneously: it hosts the documentation site and exposes the component registry as a public JSON API.
| Layer | Technology |
|---|---|
| Framework | Next.js 16, App Router, React 19 |
| Docs engine | Fumadocs (MDX-based) |
| Styling | Tailwind CSS 4, CSS variables |
| Component primitives | Radix UI (@radix-ui/react-*) |
| State management | Jotai (atomWithStorage) |
| Animations | Motion (Framer Motion) |
| Syntax highlighting | Shiki + rehype-pretty-code |
| Notifications | Sonner |
| Audio / haptics | @web-kits/audio, web-haptics |
| Analytics | Vercel Analytics |
| Deployment | Vercel |
Registry System
The registry is defined in registry.json at the root of the repository. Each entry declares a component, its source file, its type, and its dependencies.
{
"name": "gin-design",
"homepage": "https://gin-design.vercel.app",
"items": [
{
"name": "button",
"type": "registry:ui",
"title": "Button",
"description": "...",
"dependencies": ["class-variance-authority", "@radix-ui/react-slot"],
"files": [
{
"path": "registry/mint/button.tsx",
"type": "registry:ui",
"target": "components/ui/button.tsx"
}
]
}
]
}When a user runs npx shadcn add https://gin-design.vercel.app/r/button, the CLI fetches the registry entry, resolves the file, and copies it to the target path inside their project. They own the file completely from that point.
The registry endpoint is served at /r/[name].json via app/r/[name]/route.ts, which reads registry.json and transforms the entries into the format the shadcn CLI expects.
Registry Item Types
Each item in the registry has a prefix that identifies its type. Components have no prefix; all other types use a short letter prefix followed by a dash.
| Prefix | Type | Example | Status |
|---|---|---|---|
| (none) | Component | button | Available |
e- | Element | e-data-table | Available |
b- | Block | b-featured | Available |
c- | Chart | c-bar | Available |
i- | Icons | i-social | Available |
p- | Page | p-dashboard | Coming soon |
Component Authoring
All registry components live in registry/mint/ — the base/default brand theme, source of truth for every other theme. The directory name doesn't match the shadcn style setting (see below); it's just our default brand id.
A registry component is a plain TypeScript/React file with no "use client" directive unless it genuinely requires browser APIs or hooks. Components that only compose JSX render as Server Components and avoid hydration issues.
Standard component structure:
import { cva } from "class-variance-authority";
import type { VariantProps } from "class-variance-authority";
import { Slot } from "@radix-ui/react-slot";
import { cn } from "@/lib/utils";
const componentVariants = cva("base-classes", {
variants: { variant: { default: "..." } },
defaultVariants: { variant: "default" },
});
interface ComponentProps
extends React.ComponentProps<"element">,
VariantProps<typeof componentVariants> {
asChild?: boolean;
}
const Component = ({ className, variant, asChild = false, ...props }: ComponentProps) => {
const Comp = asChild ? Slot : "element";
return <Comp className={cn(componentVariants({ className, variant }))} {...props} />;
};
export { Component, componentVariants };Conventions:
cn()from@/lib/utils— merges Tailwind classes viaclsx+tailwind-mergecva()fromclass-variance-authority— variant logicSlotfrom@radix-ui/react-slot— enables theasChildpattern (renders as any element)data-slot,data-size,data-variant— data attributes for external targeting via CSS
shadcn Configuration
The repository uses the new-york style with the following defaults:
{
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"css": "app/globals.css",
"baseColor": "neutral",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"ui": "@/components/ui",
"utils": "@/lib/utils",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}CSS design tokens are defined as CSS variables in app/globals.css, following the shadcn token convention. All tokens have light and dark mode values:
:root {
--background: ...;
--foreground: ...;
--primary: ...;
--primary-foreground: ...;
--secondary: ...;
--secondary-foreground: ...;
--accent: ...;
--accent-foreground: ...;
--destructive: ...;
--border: ...;
--input: ...;
--ring: ...;
--radius: ...;
}Documentation Site
Documentation pages are MDX files in content/docs/. The Fumadocs engine (fumadocs-mdx) processes them and exposes them via the source loader in lib/source.ts.
The sidebar structure is controlled by meta.json files alongside the MDX files:
{ "title": "Get Started", "pages": ["index", "installation", "documentation"] }Each MDX file has a YAML frontmatter block that declares title, description, and optionally figma (a Figma link for the component's design reference).
MDX components are registered in mdx-components.tsx and are available globally in every page without explicit imports — ComponentPreview, ComponentSource, CommandBox, Accordion, etc.
ComponentPreview
<ComponentPreview name="button">
<Button>Button</Button>
</ComponentPreview>The name prop resolves to registry/mint/{name}.tsx. The component renders the live preview alongside a source code tab that shows the registry file contents.
CommandBox
<CommandBox componentName="button" className="mt-2" />Renders the install command card with npm / pnpm / yarn / vite tabs. On the home and installation pages, componentName is omitted and the card animates through all registered component names. On component pages, componentName is passed explicitly and the name is shown statically.
Markdown Endpoints
Every documentation page is available as machine-readable markdown at its .md URL:
/docs/components/button → HTML page
/docs/components/button.md → Raw markdown
The markdown route is served by app/llms.md/docs/[[...slug]]/route.ts. It uses getLLMText() from lib/source.ts, which extracts the processed markdown content from the MDX page data. This endpoint is used internally by the Copy Page and Open in AI features.
Two additional endpoints make the full library indexable by AI tools:
/llms.txt— lists all documented pages (follows the llms.txt convention)/llms-full.txt— includes the full markdown content of every page in one file
AI Tooling
Each component, element, block, chart, and icon page has a Copy Page button in the header toolbar.
Copy Page
Fetches the .md endpoint for the current page and copies the raw markdown to the clipboard. Useful for pasting into AI tools that don't support URL prompts — local models, IDE chat panels, or any tool where you paste context manually.
Open in AI tool
Opens the page directly in Claude or ChatGPT with a pre-built prompt constructed in components/docs-copy-page.tsx via getPromptUrl():
I'm building with GIN Design — a custom component library (https://gin-design.vercel.app).
Active theme: **{themeLabel}**
Read the full component documentation here: {markdownUrl}
Focus on the **{themeLabel}** Specs section — it contains the exact design tokens,
border radius, colour palette, variant meanings, and conventions for this theme.
Based on that, help me use this component correctly in the {themeLabel} theme.
The model is directed to the .md URL of the page, which includes the full content — usage examples, variants, props, and the Specs block.
Open in v0
Opens the component in v0's sandbox using the registry JSON endpoint (/api/registry/{theme}/{component}), with the theme's CSS variables pre-loaded so the preview renders in the correct brand colours.
Dropdown actions
The Copy Page dropdown also provides:
- View as Markdown — opens the
.mdURL in a new tab - Open in JSON — opens the raw registry entry (
/r/{name}.json) - View in Figma — appears only when the page declares a
figmaURL in its frontmatter
Specs Block
Every component page ends with a ## Specs section — a fenced markdown block designed to be copied and pasted into any AI tool as structured context.
It contains:
- Component name, file path, install target, dependencies
- shadcn config (style, rsc, tsx, cssVariables, iconLibrary, aliases)
- All CSS design tokens used (with light/dark values)
- Tailwind CSS conventions (border-radius, spacing, font scale)
- Variant and size reference with their Tailwind classes
- Prop definitions with types and defaults
- Code conventions (
cn,cva,Slot, focus ring, disabled state)
Figma Integration
Component pages can declare a Figma reference URL in their frontmatter:
---
title: Button
figma: https://www.figma.com/design/{fileId}/{name}?node-id={nodeId}
---When a figma URL is present, a View in Figma item appears in the Copy Page dropdown, linking directly to the component's Figma frame or section.
The figma field is typed in source.config.ts as an optional URL via Zod:
schema: frontmatterSchema.extend({
figma: z.string().url().optional(),
})Figma Diff
Beyond linking, the library exposes a Figma Diff API at app/api/diff/figma/. Connect a Figma file via access token and get a structured diff of what changed in your designs — useful for keeping code and design in step when the design evolves.
Configure via environment variable:
FIGMA_ACCESS_TOKEN=your_token_hereSharing
The share button (top right of each component, element, block, chart, or icon page) provides:
- Copy link — copies the absolute URL to clipboard
- Copy JSON — fetches and copies the raw registry JSON for the component
- Share on Discord — posts the component link directly to the configured Discord channel via webhook (requires
DISCORD_WEBHOOK_URLenv var)
Discord and X sharing are configured in constants/links.ts. Items only appear in the UI when their corresponding value is non-empty.
Package Manager Support
The docs support four package managers: npm, pnpm, yarn, and vite. The selected manager is persisted in localStorage via a Jotai atom (atomWithStorage) in hooks/use-package-manager.ts.
All code block commands render tabs for each manager. The CommandBox component reads the active manager to build the correct install command.
File Structure
├── app/
│ ├── (app)/docs/[[...slug]]/ # Docs page renderer
│ ├── r/[name]/ # Registry JSON endpoint (/r/button.json)
│ ├── api/
│ │ ├── registry/ # Registry API for v0 and AI tools
│ │ ├── diff/figma/ # Figma diff endpoint
│ │ └── share/discord/ # Discord webhook endpoint
│ └── llms.md/docs/[[...slug]]/ # Markdown endpoint (.md URLs)
├── components/
│ ├── command-box.tsx # Install command card
│ ├── docs-copy-page.tsx # Copy Page / Open in AI button
│ ├── docs-share-menu.tsx # Share button
│ ├── docs-toc-footer.tsx # TOC sidebar footer links
│ └── ui/ # App UI components (not registry)
├── content/docs/ # MDX documentation pages
├── registry/mint/ # Registry component source files
├── constants/
│ ├── links.ts # External URLs (GitHub, Discord, X)
│ └── site.ts # Site metadata, registry URL, tokens
├── lib/
│ ├── source.ts # Fumadocs source loader
│ ├── figma-diff.ts # Figma diff logic
│ └── events.ts # Analytics event schema
├── registry.json # Registry manifest
└── source.config.ts # Fumadocs MDX config + frontmatter schema
On This Page