Cnippet

CI/CD Build Log Viewer

Component 8

About

A collapsible list of CI/CD build steps with status badges and expandable, color-coded log output.

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds CI/CD Build Log Viewer to your project and fits it to your theme.

Code

How CI/CD Build Log Viewer is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

import { ChevronDownIcon } from "lucide-react";

import { Badge } from "@/components/ui/v-collapsible-9-utils/badge";
import { Card, CardPanel } from "@/components/ui/v-collapsible-9-utils/card";
import {
  Collapsible,
  CollapsiblePanel,
  CollapsibleTrigger,
} from "@/components/ui/v-collapsible-9-utils/collapsible";

type StepStatus = "success" | "failed" | "running" | "pending";

const statusVariant: Record<
  StepStatus,
  "success" | "destructive" | "info" | "outline"
> = {
  failed: "destructive",
  pending: "outline",
  running: "info",
  success: "success",
};

const steps: {
  id: string;
  name: string;
  status: StepStatus;
  duration: string;
  defaultOpen?: boolean;
  log: string[];
}[] = [
  {
    duration: "3s",
    id: "install",
    log: [
      "$ bun install",
      "bun install v1.1.0",
      "Resolving dependencies...",
      "✓ 412 packages installed [3.14s]",
    ],
    name: "Install dependencies",
    status: "success",
  },
  {
    duration: "12s",
    id: "typecheck",
    defaultOpen: true,
    log: [
      "$ tsc --noEmit",
      "src/components/Button.tsx(14,7): error TS2322",
      "  Type 'string' is not assignable to type 'number'.",
      "Found 1 error.",
    ],
    name: "Type check",
    status: "failed",
  },
  {
    duration: "—",
    id: "build",
    log: [],
    name: "Build",
    status: "pending",
  },
];

function Pattern() {
  return (
    <div className="w-full max-w-md space-y-2">
      {steps.map((step) => (
        <Card className="overflow-hidden" key={step.id}>
          <Collapsible
            defaultOpen={step.defaultOpen}
            disabled={step.status === "pending"}
          >
            <CollapsibleTrigger className="w-full">
              <CardPanel className="py-3">
                <div className="flex items-center justify-between gap-3">
                  <div className="flex items-center gap-2.5">
                    <Badge size="sm" variant={statusVariant[step.status]}>
                      {step.status}
                    </Badge>
                    <span className="font-medium text-sm">{step.name}</span>
                  </div>
                  <div className="flex items-center gap-2 text-muted-foreground">
                    <span className="text-xs">{step.duration}</span>
                    {step.status !== "pending" && (
                      <ChevronDownIcon className="size-3.5 in-data-panel-open:rotate-180 transition-transform duration-200" />
                    )}
                  </div>
                </div>
              </CardPanel>
            </CollapsibleTrigger>

            {step.log.length > 0 && (
              <CollapsiblePanel>
                <div className="border-t bg-muted/40 px-4 py-3">
                  <pre className="space-y-0.5 overflow-x-auto text-xs leading-relaxed">
                    {step.log.map((line, i) => (
                      <div
                        className={
                          line.startsWith("$")
                            ? "font-semibold text-foreground"
                            : line.includes("error") || line.includes("Error")
                              ? "text-destructive"
                              : "text-muted-foreground"
                        }
                        key={i}
                      >
                        {line}
                      </div>
                    ))}
                  </pre>
                </div>
              </CollapsiblePanel>
            )}
          </Collapsible>
        </Card>
      ))}
    </div>
  );
}

export default function Default() {
  return (
    <div className="flex min-h-svh w-full items-center justify-center p-6">
      <Pattern />
    </div>
  );
}

Dependencies

More by Cnippet

View all
  • Team

    Team

    1.3K
  • Davincho Hero-1

    Davincho Hero-1

    683
  • Aero Hero-3

    Aero Hero-3

    559
  • Features

    Features

    510
  • Random Letter Swap Navigation

    Random Letter Swap Navigation

    487
  • Services with Animated Hover Modal

    Services with Animated Hover Modal

    465
  • Blogs

    Blogs

    396
  • Aero Hero-1

    Aero Hero-1

    356

Explore more

Part of Cnippet — browse the full library on 21st.dev.