JDS.

Search docs

Find a page or component

Get Started

Add JDS to a new or existing React project.

Prerequisites

  • React 19 with Tailwind CSS v4.
  • A components.json from shadcn init. Next.js App Router is assumed in examples, but nothing depends on it.

Add the registry

Register the @jds namespace in components.json so the CLI knows where to fetch components from.

components.json
{
  "registries": {
    "@jds": "https://jds-ruddy.vercel.app/r/{name}.json"
  }
}

Install

  1. Add the style

    Installs the neutral light and dark themes, status colors, motion keyframes, and the shared icon and motion utilities. To start from your own look, pick a color, base, radius, font and orb in the Customize menu in the header, then choose Use this theme in your app: one command installs the style with your theme.

    pnpm dlx shadcn@latest add @jds/style
  2. Add components

    Each component page lists its own command. Dependencies, including other JDS components, come along.

    pnpm dlx shadcn@latest add @jds/prompt-input @jds/message
  3. Set the default theme

    JDS is designed dark first. With next-themes, set defaultTheme="dark" and keep light available. Both themes are complete.

Manual installation

  • Open a component page and switch to the Code tab.
  • Copy the source into the path shown, for example components/ai/prompt-input.tsx.
  • Copy lib/icons.ts and lib/motion.ts once. Every component imports them.
  • Install the npm dependencies listed on the page.

Fonts

Components read three variables: --font-sans for UI text, --font-mono for code and keys, and --font-heading for titles (aliased to sans by default). This site uses Geist and Geist Mono.

app/layout.tsx
import { Geist, Geist_Mono } from "next/font/google"

const sans = Geist({ variable: "--font-sans", subsets: ["latin"] })
const mono = Geist_Mono({ variable: "--font-mono", subsets: ["latin"] })

<html className={`${sans.variable} ${mono.variable}`}>

Next.js starters name the variable --font-geist-sans. Rename it to --font-sans or text falls back to the system font.

Using with the AI SDK

PromptInput status and ToolCall states use the AI SDK's strings, so values pass straight through.

const { messages, status, sendMessage, stop } = useChat()

{messages.map((m) => (
  <Message key={m.id} from={m.role}>
    <MessageContent>
      {m.parts.map((part, i) => {
        if (part.type === "text") return <Response key={i} isAnimating={status === "streaming"}>{part.text}</Response>
        if (part.type === "reasoning") return (
          <Reasoning key={i} isStreaming={part.state === "streaming"}>
            <ReasoningTrigger />
            <ReasoningContent>{part.text}</ReasoningContent>
          </Reasoning>
        )
        if (part.type.startsWith("tool-")) return (
          <ToolCall key={i}>
            <ToolCallHeader name={part.type.slice(5)} state={part.state} />
          </ToolCall>
        )
      })}
    </MessageContent>
  </Message>
))}

<PromptInput status={status} onSubmit={({ text }) => sendMessage({ text })}>
  <PromptInputTextarea />
  <PromptInputToolbar>
    <PromptInputTools />
    <PromptInputSubmit onStop={stop} />
  </PromptInputToolbar>
</PromptInput>

Working with coding agents

The registry is plain JSON at https://jds-ruddy.vercel.app/r/{name}.json. With the namespace in components.json, the shadcn MCP server can search and install JDS components from your agent.

  • /llms.txt indexes every page, with a Markdown link per component.
  • /llms-full.txt has every component's docs and source in one file.
  • Copy page, at the top of every page, copies it as Markdown for pasting into a chat.