MIT Licensedv0.1

Your component playground.
Auto-generated.

jc reads your TypeScript prop interfaces and generates an interactive showcase. No stories to write. No config to maintain.

How it works

src/components/ui/actions/icon-button.tsx

tsx
import type { ReactNode } from 'react'
import type { LucideIcon } from 'lucide-react'

/**
 * A button with a leading icon and text label.
 *
 * @example <IconButton icon={Heart}>Like</IconButton>
 * @example <IconButton icon={Settings} rounded>Settings</IconButton>
 * @example <IconButton icon={Trash2} disabled>Delete</IconButton>
 */
export interface IconButtonProps {
  children: ReactNode
  icon: LucideIcon
  rounded?: boolean
  disabled?: boolean
}

/** A button with a leading icon and text label. */
export function IconButton({ children, icon: Icon, rounded, disabled }: IconButtonProps) {
  return (
    <button
      type="button"
      disabled={disabled}
      className={`inline-flex items-center gap-2 px-3 h-9 bg-surface-raised
        border border-border text-fg text-sm hover:bg-surface-overlay transition-colors
        ${rounded ? 'rounded-full' : 'rounded-md'}
        ${disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}`}
    >
      <Icon size={16} />
      <span>{children}</span>
    </button>
  )
}

Generated showcase

bash
$ bunx jc extract
Scanning src/components/ui/**/*.tsx...

  actions/command-button.tsx   CommandButton   7 props  2 presets
  actions/icon-button.tsx      IconButton      3 props  3 presets
  data-display/stat-card.tsx   StatCard        6 props  3 presets
  forms/text-field.tsx         TextField       8 props  4 presets
  feedback/callout.tsx         Callout         4 props  4 presets
  ...

 16 components extracted in 280ms
 Written to src/jc/generated/meta.json
 Written to src/jc/generated/registry.ts

No stories to write

Your TypeScript interfaces are the source of truth. No .stories.ts, no boilerplate, no duplication.

No separate process

Runs alongside your dev server. One command extracts metadata, your existing bundler serves the page.

Smart defaults

Faker-powered prop values, icon pickers, light/dark themes, responsive viewports. Works out of the box.

Extraction time
280-40%
ms
Config files
0
Type accuracy
100+12%
%

Features

Built for real-world component libraries

JSDoc-driven presets

@example blocks become one-click presets. Wrapper components, default props, children — parsed from JSDoc you already write.

tsx
/**
 * @example
 * <Button variant="primary" size="lg">
 *   Save changes
 * </Button>
 */

Fixture plugins

Register icon libraries or custom components as fixtures. Matching props get a visual picker automatically.

tsx
const icons = defineFixtures({
  name: 'lucide',
  fixtures: [
    { key: 'star', label: 'Star',
      render: () => <Star /> },
  ],
})

Shareable URLs

Component and prop state is serialized into the URL. Share a link — it loads exactly as you left it.

bash
/showcase?component=Button#s=eyJwIjp7
  InZhcmlhbnQiOiJwcmltYXJ5In19

Comparison

Storybook vs. jc

Storybookjc
Config per componentstories.tsx + meta + argTypesNone
Separate build processYes (webpack/vite)No
Type extractionManual argTypesAutomatic from TS
Icon/component pickersCustom addonsBuilt-in fixtures
URL state sharingLimitedFull prop serialization
Bundle size~2MB~80KB

button.stories.tsx

tsx
import type { Meta, StoryObj } from '@storybook/react'
import { Button } from './button'

const meta: Meta<typeof Button> = {
  title: 'UI/Button',
  component: Button,
  argTypes: {
    variant: {
      control: 'select',
      options: ['default', 'primary', 'ghost'],
    },
    size: {
      control: 'select',
      options: ['sm', 'md', 'lg'],
    },
    disabled: { control: 'boolean' },
  },
}

export default meta
type Story = StoryObj<typeof Button>

export const Primary: Story = {
  args: { children: 'Click me', variant: 'primary' },
}

with jc — nothing to write

bash
$ bunx jc extract
tsx
// showcase/page.tsx
import { createShowcasePage } from 'jc/next'
import meta from '@/jc/generated/meta.json'
import { registry } from '@/jc/generated/registry'

export default createShowcasePage({ meta, registry })

Setup

Three steps, under a minute

1 — install

bash
bun add jc

2 — extract

bash
bunx jc extract

3 — render

tsx
import { createShowcasePage } from 'jc/next'
import meta from '@/jc/generated/meta.json'
import { registry } from '@/jc/generated/registry'

export default createShowcasePage({ meta, registry })

FAQ

Common questions

Maxime De Pachtere

Creator

Built because writing stories for every component felt like busywork. If your types already describe your props, why repeat yourself?

Tests passing
327+11
Bundle size
80-60%
KB
Dependencies
2
reactfaker
Open issues
0

See it in action

Explore the live showcase — every component on this page is in there, extracted automatically.

Get notified about releases