Commands

Copy-ready CLI command blocks for React documentation. Display install commands with one-click copy, or switch between npm, pnpm, yarn, and bun with tabbed package-manager UI.

Introduction

The Commands component helps you show terminal and install instructions on documentation pages, blogs, and component libraries. Use Command for a single copy-to-clipboard CLI line, or MultiChoiceCommand when readers may use npm, pnpm, yarn, or bun.

Both variants are built with React, Tailwind CSS, and Lucide icons. They support light and dark themes, include accessible copy buttons, and fit naturally into shadcn-style docs.

Demo

Command

A minimal block for one install or shell command. Click the copy icon to copy the full string to the clipboard.

Open in
npm install lucide-react

MultiChoiceCommand

Tabbed package-manager selector. Pass a packageName to auto-generate install commands, or pass custom commands per manager (for example, npx shadcn@latest add URLs).

Open in
npm install lucide-react

Usage

import { Command, MultiChoiceCommand } from "@/components/ui/commands";

// Single command
<Command INSTALL_COMMAND="npm install my-package" />

// Auto-generated install commands from package name
<MultiChoiceCommand packageName="my-package" />

// Custom commands per package manager
<MultiChoiceCommand
  commands={{
    npm: "npx shadcn@latest add https://example.com/r/MyComponent.json",
    pnpm: "pnpm dlx shadcn@latest add https://example.com/r/MyComponent.json",
    yarn: "yarn dlx shadcn@latest add https://example.com/r/MyComponent.json",
    bun: "bunx --bun shadcn@latest add https://example.com/r/MyComponent.json",
  }}
/>

Props

Command

PropTypeDescription
INSTALL_COMMANDstringThe CLI or shell command shown in the block (required).
classNamestringOptional Tailwind classes merged onto the root container.

MultiChoiceCommand

Use either packageName or commands — not both.

PropTypeDescription
packageNamestringPackage name used to build default install strings (npm install, pnpm add, etc.).
commandsRecord<PackageManager, string>Explicit command string for each package manager tab (npm, pnpm, yarn, bun).

Installation

To install the Commands component, run one of the following in your project directory:

npx shadcn@latest add https://ahs-lab.vercel.app/r/Commands.json
pnpm dlx shadcn@latest add https://ahs-lab.vercel.app/r/Commands.json

Code

"use client";
import { cn } from '@/lib/utils';
import { Check, Copy, Terminal } from 'lucide-react';
import React, { useState } from 'react';

export const Command = ({ INSTALL_COMMAND , className }: { INSTALL_COMMAND: string ,className?:string }) => {
  const [copied, setCopied] = useState(false);

  const handleCopy = async () => {
    await navigator.clipboard.writeText(INSTALL_COMMAND);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };

  return (
    <div className={cn(className,"flex items-center gap-3 rounded-md border border-neutral-200 dark:border-neutral-800 bg-neutral-50 dark:bg-neutral-900 px-3 py-2.5")}>
      <Terminal className="h-3.5 w-3.5 shrink-0 text-neutral-400 dark:text-neutral-500" />
      <code className="flex-1 text-xs text-neutral-600 dark:text-neutral-300 font-mono truncate">
        {INSTALL_COMMAND}
      </code>
      <button
        onClick={handleCopy}
        aria-label="Copy command"
        className={cn(
          "shrink-0 rounded p-1 transition-colors duration-150 cursor-pointer",
          copied
            ? "text-green-500"
            : "text-neutral-400 hover:text-neutral-700 dark:hover:text-neutral-200"
        )}
      >
        {copied ? <Check className="h-3.5 w-3.5" /> : <Copy className="h-3.5 w-3.5" />}
      </button>
    </div>
  );
};

type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun';

const DEFAULT_COMMANDS: Record<PackageManager, (pkg: string) => string> = {
  npm:  (pkg) => `npm install ${pkg}`,
  pnpm: (pkg) => `pnpm add ${pkg}`,
  yarn: (pkg) => `yarn add ${pkg}`,
  bun:  (pkg) => `bun add ${pkg}`,
};

const TABS: PackageManager[] = ['npm', 'pnpm', 'yarn', 'bun'];

type MultiChoiceCommandProps =
  | { packageName: string; commands?: never }
  | { commands: Record<PackageManager, string>; packageName?: never };

export const MultiChoiceCommand = ({ packageName, commands }: MultiChoiceCommandProps) => {
  const [active, setActive] = useState<PackageManager>('npm');

  const getCommand = (pm: PackageManager) =>
    commands ? commands[pm] : DEFAULT_COMMANDS[pm](packageName!);

  return (
    <div className="mt-4 w-fit min-w-64 rounded-md border border-neutral-200 dark:border-neutral-800 overflow-hidden">
      <div className="flex border-b border-neutral-200 dark:border-neutral-800 bg-neutral-100 dark:bg-neutral-900 px-1">
        {TABS.map((pm) => (
          <button
            key={pm}
            onClick={() => setActive(pm)}
            className={cn(
              "px-3 py-1.5 text-xs font-mono transition-colors duration-150 border-b-2 -mb-px cursor-pointer",
              active === pm
                ? "border-neutral-800 dark:border-neutral-200 text-neutral-900 dark:text-neutral-100"
                : "border-transparent text-neutral-400 hover:text-neutral-700 dark:hover:text-neutral-300"
            )}
          >
            {pm}
          </button>
        ))}
      </div>
      <div className="bg-neutral-50 dark:bg-neutral-900">
        <Command INSTALL_COMMAND={getCommand(active)} className="border-none" />
      </div>
    </div>
  );
};