Mischief

45 / Docs

Component Preview

A framed example with a tab for the source beside it, and the live one still running when you switch back.

Hold Button

Installation

Copy the source into your project, or keep it behind a package.

npx shadcn@latest add Tinkerers-Labs/mischief-ui/component-preview
import { ComponentPreview } from "mischief-ui/component-preview"
Also installs
  • lucide-react

Or paste it in yourself. The source imports the shared cn helper from @/lib/utils, so point that at your own copy.

registry/default/component-preview/component-preview.tsx
"use client" import * as React from "react"import { Check, Clipboard } from "lucide-react" import { cn } from "@/lib/utils" export type ComponentPreviewProps = Omit<  React.HTMLAttributes<HTMLDivElement>,  "children" | "title"> & {  /** The living example. */  children: React.ReactNode  /** Source for the second tab. Without it there is only the preview. */

Usage

export function Example() {
  return (
    <ComponentPreview code={source} title="Hold Button">
      <HoldButton onComplete={remove}>Delete</HoldButton>
    </ComponentPreview>
  )
}

The example keeps running

Switching to the source hides the preview rather than unmounting it. Anything the reader had set up -- a slider moved, a tab chosen, a form half filled -- is still there when they come back, which is the whole reason to put the source behind a tab instead of below the example.

It also means the example is still alive while hidden. A preview that streams, animates, or polls goes on doing so behind the source tab, so give it a way to stop if that would be wasteful.

Where the source comes from

The code you pass is a string, and nothing extracts it from the example for you. Two copies of the same snippet drift, so read the file at build time rather than retyping it beside the component.

// A server component can simply read the file it is showing.
const code = await readFile("components/examples/volume.tsx", "utf8")

return (
  <ComponentPreview code={code} title="Volume">
    <VolumeExample />
  </ComponentPreview>
)

Omit code entirely and the tabs disappear, leaving a framed example. That is the right shape for something that has no source worth showing.

API

childrenReactNodeThe living example.
codestringSource for the second tab. Without it there is only the preview.
titleReactNodeShown at the start of the toolbar.
defaultView"preview" | "code"Which tab opens first. Defaults to "preview".
previewLabel, codeLabelstring, stringRenames the two tabs.
actionsReactNodeExtra toolbar controls, such as a restart button.
align"center" | "start"How the example sits in its frame.
frameClassNamestringClasses for the preview frame itself.

Accessibility

The two views are a real tablist: Left and Right Arrow move between tabs, only the selected tab is in the tab order, and each panel is labelled by its tab. The preview panel is hidden rather than unmounted while the source shows, so anything set up in the example is still there on the way back. Copying announces itself through a polite live region, and a refused clipboard is reported rather than silently doing nothing.