# API List

A card of API credentials, each with a copy button and a tooltip.

## When to use

* Show API keys, endpoints and webhook URLs on a developer settings page.
* Let the user copy a long secret or URL in one click.

## How it works

`ApiList` takes no props: the three fields are a constant in the file. Each row truncates its value and has a ghost icon [Button](/components/button) inside a Base UI [Tooltip](/components/tooltip) (`render` prop). Clicking copies the value with `navigator.clipboard.writeText` and shows a check icon for 2 seconds. The example opens the first tooltip with `defaultOpen`.

Dependencies: `lucide-react`, and `npx shadcn@latest add button tooltip` with a Base UI config.

## Example

```tsx
"use client";

import {
  CheckIcon,
  CopyIcon,
  KeyIcon,
  LinkIcon,
  WebhookIcon,
} from "lucide-react";
import { useState } from "react";
import { Button } from "@/shadcn/base/ui/button";
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "@/shadcn/base/ui/tooltip";

const fields = [
  { icon: KeyIcon, label: "API Key", value: "sk_live_9xKqP2mN...8rTv" },
  { icon: LinkIcon, label: "Endpoint", value: "https://api.cnippet.ui/v2" },
  {
    icon: WebhookIcon,
    label: "Webhook",
    value: "https://hooks.cnippet.ui/events",
  },
] as const;

export default function Default() {
  const [copiedId, setCopiedId] = useState<string | null>(null);

  const copy = (value: string, id: string) => {
    navigator.clipboard.writeText(value).catch(() => null);
    setCopiedId(id);
    setTimeout(() => setCopiedId(null), 2000);
  };

  return (
    <div className="flex min-h-25 items-center justify-center">
      <TooltipProvider>
        <div className="w-72 space-y-1.5 rounded-xl border bg-background p-3 shadow-xs">
          {fields.map(({ icon: Icon, label, value }, index) => (
            <div
              className="flex items-center gap-2.5 rounded-lg bg-muted/50 px-3 py-2"
              key={label}
            >
              <Icon
                aria-hidden="true"
                className="size-3.5 shrink-0 text-muted-foreground"
              />
              <div className="min-w-0 flex-1">
                <p className="font-semibold text-[10px] text-muted-foreground uppercase tracking-wide">
                  {label}
                </p>
                <p className="truncate font-mono text-xs">{value}</p>
              </div>
              <Tooltip defaultOpen={index === 0}>
                <TooltipTrigger
                  className="shrink-0"
                  onClick={() => copy(value, label)}
                  render={
                    <Button
                      aria-label={
                        copiedId === label ? "Copied" : `Copy ${label}`
                      }
                      className="size-6"
                      size="icon"
                      variant="ghost"
                    />
                  }
                >
                  {copiedId === label ? (
                    <CheckIcon
                      aria-hidden="true"
                      className="size-3.5 text-emerald-500"
                    />
                  ) : (
                    <CopyIcon aria-hidden="true" className="size-3.5" />
                  )}
                </TooltipTrigger>
                <TooltipContent>
                  {copiedId === label ? "Copied!" : "Copy to clipboard"}
                </TooltipContent>
              </Tooltip>
            </div>
          ))}
        </div>
      </TooltipProvider>
    </div>
  );
}

```

## Code

Copy the component into your project:

```tsx
"use client";

import {
  CheckIcon,
  CopyIcon,
  KeyIcon,
  LinkIcon,
  WebhookIcon,
} from "lucide-react";
import { useState } from "react";
import { Button } from "@/shadcn/base/ui/button";
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "@/shadcn/base/ui/tooltip";

const fields = [
  { icon: KeyIcon, label: "API Key", value: "sk_live_9xKqP2mN...8rTv" },
  { icon: LinkIcon, label: "Endpoint", value: "https://api.cnippet.ui/v2" },
  {
    icon: WebhookIcon,
    label: "Webhook",
    value: "https://hooks.cnippet.ui/events",
  },
] as const;

export function ApiList() {
  const [copiedId, setCopiedId] = useState<string | null>(null);

  const copy = (value: string, id: string) => {
    navigator.clipboard.writeText(value).catch(() => null);
    setCopiedId(id);
    setTimeout(() => setCopiedId(null), 2000);
  };

  return (
    <div className="flex min-h-25 items-center justify-center">
      <TooltipProvider>
        <div className="w-72 space-y-1.5 rounded-xl border bg-background p-3 shadow-xs">
          {fields.map(({ icon: Icon, label, value }) => (
            <div
              className="flex items-center gap-2.5 rounded-lg bg-muted/50 px-3 py-2"
              key={label}
            >
              <Icon
                aria-hidden="true"
                className="size-3.5 shrink-0 text-muted-foreground"
              />
              <div className="min-w-0 flex-1">
                <p className="font-semibold text-[10px] text-muted-foreground uppercase tracking-wide">
                  {label}
                </p>
                <p className="truncate font-mono text-xs">{value}</p>
              </div>
              <Tooltip>
                <TooltipTrigger
                  className="shrink-0"
                  onClick={() => copy(value, label)}
                  render={
                    <Button
                      aria-label={
                        copiedId === label ? "Copied" : `Copy ${label}`
                      }
                      className="size-6"
                      size="icon"
                      variant="ghost"
                    />
                  }
                >
                  {copiedId === label ? (
                    <CheckIcon
                      aria-hidden="true"
                      className="size-3.5 text-emerald-500"
                    />
                  ) : (
                    <CopyIcon aria-hidden="true" className="size-3.5" />
                  )}
                </TooltipTrigger>
                <TooltipContent>
                  {copiedId === label ? "Copied!" : "Copy to clipboard"}
                </TooltipContent>
              </Tooltip>
            </div>
          ))}
        </div>
      </TooltipProvider>
    </div>
  );
}

```
