klyv-ui

Log Viewer

Component 4

About

A searchable, scrollable log tail panel with level filters, line-wrap toggle, and an auto-follow mode that pauses when you scroll up and offers a jump-to-latest button.

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

Code

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

Usage.tsx

"use client";

import {
  LogViewer,
  type LogViewerLine,
  type LogViewerLevel,
} from "@/components/ui/log-viewer";
import * as React from "react";

const SOURCES = ["api", "auth", "billing", "scheduler", "worker"] as const;

function rid() {
  return Math.random().toString(36).slice(2, 6);
}

const TEMPLATES: Array<{ level: LogViewerLevel; text: () => string }> = [
  {
    level: "info",
    text: () =>
      `POST /v1/payments 201 in ${120 + Math.floor(Math.random() * 600)}ms`,
  },
  {
    level: "info",
    text: () =>
      `GET /v1/invoices/${rid()} 200 in ${80 + Math.floor(Math.random() * 400)}ms`,
  },
  { level: "info", text: () => `user ${rid()} signed in with SSO` },
  {
    level: "info",
    text: () =>
      `job invoice.render completed in ${90 + Math.floor(Math.random() * 500)}ms`,
  },
  { level: "debug", text: () => `cache hit for key session:${rid()}` },
  {
    level: "debug",
    text: () =>
      `acquired lock queue:refunds after ${50 + Math.floor(Math.random() * 300)}ms`,
  },
  {
    level: "warn",
    text: () =>
      `slow query on invoices_by_customer took ${300 + Math.floor(Math.random() * 300)}ms`,
  },
  {
    level: "warn",
    text: () =>
      `retrying webhook delivery (attempt ${1 + Math.floor(Math.random() * 4)} of 5)`,
  },
  {
    level: "error",
    text: () => `payment_intent ${rid()} failed: card_declined`,
  },
  {
    level: "error",
    text: () => "connection reset by peer while reading from redis:6379",
  },
];

function makeLine(id: number, at: number): LogViewerLine {
  const template = TEMPLATES[Math.floor(Math.random() * TEMPLATES.length)];
  const source = SOURCES[Math.floor(Math.random() * SOURCES.length)];
  return {
    id,
    timestamp: at,
    level: template.level,
    source,
    message: template.text(),
  };
}

export default function LogViewerDemo() {
  const next = React.useRef(0);
  const [lines, setLines] = React.useState<LogViewerLine[]>(() => {
    const now = Date.now();
    return Array.from({ length: 60 }, (_, index) =>
      makeLine(next.current++, now - (60 - index) * 1000),
    );
  });

  React.useEffect(() => {
    const timer = setInterval(() => {
      setLines((current) => {
        const grown = [...current, makeLine(next.current++, Date.now())];
        return grown.length > 400 ? grown.slice(grown.length - 400) : grown;
      });
    }, 1500);
    return () => clearInterval(timer);
  }, []);

  return (
    <div className="flex w-full items-center justify-center bg-background p-6">
      <div className="w-full max-w-2xl">
        <LogViewer lines={lines} label="Deploy logs" height={360} />
      </div>
    </div>
  );
}

More by klyv-ui

View all
  • Gantt Chart

    Gantt Chart

    89
  • Shader Canvas

    Shader Canvas

    60
  • Klyv Animated Beam

    Klyv Animated Beam

    59
  • Pareto Chart

    Pareto Chart

    53
  • Fluid Canvas

    Fluid Canvas

    37
  • Sunburst Chart

    Sunburst Chart

    30
  • Gooey Loader

    Gooey Loader

    28
  • Morphing Text

    Morphing Text

    27