Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelogContact
Components
Buttons & Actions
ButtonButton GroupCopy ButtonDelete ButtonTheme ToggleToggleToggle Group
Inputs
Avatar UploadCalendarCheckboxColor PickerComboboxCountry SelectCron PickerCurrency InputDate PickerDate Range PickerDate Time PickerDuration PickerEmail InputEmoji PickerFile UploadFilter BuilderInputInput GroupListboxMasked InputMention InputMonth PickerMulti SelectNumber InputOTP InputPassword InputPhone InputRadio GroupRatingSearch InputSegmented ControlSelectSignature PadSliderSwitchTag InputTextareaTime PickerTimezone SelectTree Select
Forms
Address InputBooking WidgetCSV ImportFieldForm WizardInline EditInvoice line itemsRich Text EditorSchema Builder
Navigation
Back To TopBounce SidebarBreadcrumbCommand MenuGlobal SearchGooey NavHook SidebarNav MenuNav TabsPaginationProximity SidebarScroll ProgressSidebarStepsTable Of ContentsTabsTopbarUser MenuWorkspace Switcher
Overlays
Alert DialogContext MenuDialogDrawerDropdown MenuFamily DrawerHover CardPopoverShare DialogSheetShortcuts DialogTooltipTour
Feedback
AlertBannerCookie ConsentEmoji ReactionEmpty StateNotification BellNotification CenterProgressRoute ProgressSetup ChecklistSkeletonSpinnerToast
Data Display
Approval FlowAvatarBadgeCalendar ViewCode BlockComment ThreadContact cardCountdownDescription ListDiff ViewerFile ListFile managerFolderGanttJSON ViewerKbdLog ViewerMarkdownPresenceSchedulerTagTimelineTree View
Tables & Lists
Audit LogData TableDeal PipelineEditable GridKanbanListMembers tablePermissions MatrixTableTask ListVirtual List
Charts & Stats
Animated CounterArea chartBar chartDonut chartFunnel chartGaugeGitHub ActivityHeatmap calendarLeaderboardLine chartRadar chartRadial barSparklineStat CardTrackerTrend badge
Layout
AccordionApp ShellCardCollapsibleDetail LayoutEntity HeaderPage HeaderScroll AreaSeparatorSettings LayoutSplit ViewToolbar
Media
Audio PlayerCarouselCSV ViewerDOCX EditorDOCX ViewerExcel ViewerFile SystemImage compareImage CropperImage GalleryImage LightboxLocation MapPDF ViewerPowerPoint ViewerQR CodeStep PlayerVideo Player
AI Kit
Agent Status CardChat MessageChat ThreadChat WidgetConversation ListFluid OrbGrid RevealMatrix OrbModel PickerPrompt InputReasoning IndicatorRule BuilderSuggestion ChipsTool Call TimelineUsage MeterVoice InputWorkflow Canvas
Text & Effects
Background PatternGravity LettersMarqueeTerminalText AnimateText Rotate
Mobile
Action SheetBottom Tab BarFABHaptic ButtonHero HeaderInstall PromptLarge Title HeaderNotification BannerOnboarding CarouselPicker WheelPull To RefreshSlide To ConfirmSticky CTA BarSwipe DeckSwipeable Tabs
Commerce
Cart drawerCategory pickerCheckout stepsCoupon inputFavorite buttonFilter panelListing cardListing gridOrder trackerPricePrice rangePricing tableProduct galleryQuantity stepperReviewsSeller card
Marketing Sections
App Download SectionBlog Cards SectionCTA SectionFAQ SectionFeature GridFeature ShowcaseHero SectionIntegrations SectionLogo CloudMarketing FooterMarketing HeaderMarketing SectionNewsletter SectionPricing SectionStats SectionSteps SectionTeam SectionTestimonials SectionTimeline SectionVideo Section
Blocks
Admin Dashboard PageAdmin ShellAI Chat PageAPI Keys PageAutomation Builder PageCheckout PageCRM Contact PageCRM Pipeline PageError PagesInbox PageInvoice PageKPI DashboardListing Detail PageLogin PageMarketplace Home PageMarketplace Search PageMarketplace ShellMobile App HomeOnboarding FlowOrder History PagePost Listing WizardPricing PageProfile PageRegister PageSettings PageUsers Admin Page
Templates
About PageAcademy PageAccessibility PageAffiliate PageAgency PageAI Product PageAppointment Booking PageBlog PageBlog Post PageBug Bounty PageCareers PageChangelog PageCommunity PageCompare PageContact PageCustomer Story PageCustomers PageDemo Booking PageDevelopers PageDocs Home PageDownload PageEnterprise PageEvents PageExperts PageFAQ PageFeature PageGlossary PageHelp Article PageHelp Center PageHome Services PageImpact PageIntegration Detail PageIntegrations PageJob Detail PageLanding PageLaunch PageLegal PageManifesto PagePortfolio PagePress PageProduct Tour PagePromo PageReal Estate PageReferral PageResearch Report PageRestaurant PageRoadmap PageROI Calculator PageSecurity PageSolutions PageStartups PageStatus PageStorefront PageSwitch PageTemplate Gallery PageTravel PageWaitlist PageYear in Review Page
Hooks & Utilities
AI ChatChart kitCommerceDate utilsFile exportFile typesFiltersForm validationHapticsInvoicingMotion presetsNavigationNumber formatRelative timeRomanian locationsSpreadsheetText matchTonesUIProvideruseClipboarduseConsentuseControllableStateuseCooldownuseDebounceuseKeyboardInsetuseLocaleuseLongPressuseMediaControlsuseMediaQueryuseOverflowItemsusePersistedStateuseScrollEdgesuseScrollStateuseSelectionuseShortcutuseStickToBottom
GalleryTheme

Data Display

Log Viewer

Data Display

Log Viewer React component for Tailwind CSS v4

KbdMarkdown
Live props
Behavior
maxLines100000
filterMode
regex
expandable
searchShortcut
fileName
State
defaultQuery
defaultFollow
loading
Layout
defaultWrap
height420px
toolbar
Appearance
showTimestamps
timeFormat
showLevels
showSource
showLineNumbers
ansi
size
Content
locale
Code
import { LogViewer } from "@/components/ui/log-viewer"

<LogViewer />
  1. Home
  2. Components
  3. Data Display
  4. Log Viewer
Pro

Log Viewer React component for Tailwind CSS v4

v1.0.0·Added in 1.0.0

A React log viewer for streaming logs up to 100,000 lines: level colors, level and regex filters, tail follow, ANSI colors and JSON details.

New lines stream in at the bottom and the view follows them. Scroll up (wheel, finger, scrollbar or keys) and following pauses by itself: a pill at the bottom counts the new lines (12 new lines) and a tap on it, the End key or scrolling back to the very end follows again; the follow toggle and the Live / Paused badge in the footer show the state. Type in the search to hide the lines that do not match (case and diacritics are ignored), or turn on the .* toggle for a regular expression (an invalid one is marked and filters nothing); in highlight mode every line stays, hits are marked and Enter / Shift+Enter or the arrows step through them. The level chips turn levels on and off and show their counts. Click, tap or press Enter on a line with details to open its JSON tree under it (the stream pauses so it stays put). The toolbar wraps lines, shows or hides the time, and the ⋯ menu copies the visible lines, downloads everything as a dated .log file or clears the buffer; on a narrow viewer wrap and time move into that menu, and below 768px it opens as a bottom sheet while the chips scroll sideways. The log body is one Tab stop: Up and Down move a focus row, Page Up / Page Down jump a page, Home goes to the first line, End to the newest, Escape drops the focus row (screen readers hear each focus row as it moves), and Ctrl+F / ⌘F inside the viewer jumps to its search (searchShortcut adds a global key). Lines over 10.000 characters are cut with a Show all button; with maxLines reached the oldest lines drop and the footer says how many.

Log Viewer is a Pro React component in the React data display components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add log-viewer, together with Badge, Button, Dropdown Menu, Empty State, JSON Viewer and 16 more and the npm packages @tanstack/react-virtual, motion and lucide-react.

Add to a project

Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup

npx wingo-ui@latest add log-viewer

Log Viewer is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans

Usage

Import LogViewer from @/components/ui/log-viewer.

import { useEffect, useState } from "react"
import { LogViewer, type LogLine, type LogViewerApi } from "@/components/ui/log-viewer"

const initial: LogLine[] = [
  { time: "2026-09-24T13:38:12.345Z", level: "info", source: "sync", message: "Fetching messages for tax ID 12-3456740" },
  { time: "2026-09-24T13:38:12.902Z", level: "warn", source: "sync", message: "Invoice 2026-0409: slow response, retrying (2/5)" },
  {
    time: "2026-09-24T13:38:13.410Z",
    level: "error",
    source: "validator",
    message: "Validation error: BR-CO-17 tax amount differs",
    meta: { invoice: "2026-0411", code: "BR-CO-17" },
  },
]

export function SyncJobLog({ events }: { events: EventSource }) {
  const [api, setApi] = useState<LogViewerApi | null>(null)

  useEffect(() => {
    if (!api) return
    const onMessage = (event: MessageEvent<string>) => api.append(event.data)
    events.addEventListener("message", onMessage)
    return () => events.removeEventListener("message", onMessage)
  }, [api, events])

  return (
    <LogViewer
      lines={initial}
      setApi={setApi}
      fileName="invoices-4812"
      searchShortcut="mod+shift+f"
      locale="en-US"
      labels={{ search: "Filter the log" }}
    />
  )
}

Props

  • linesdefault "[]"
    LogLine[] | string[]

    The lines: { id?, time?, level?, message, source?, meta? } or raw strings (parsed with parseLogLine). Pass a new array to append: only the new tail is read; a shorter or different array starts over. For high rates use setApi().append, which never re-renders the parent.

  • maxLinesdefault 100000
    number

    A ring buffer: past it the oldest lines drop and the footer says how many. Lowering it at runtime trims from the start.

  • levelsdefault "["trace", "debug", "info", "warn", "error", "fatal"]"
    LogLevel[]

    The level chips shown in the toolbar ("trace", "debug", "info", "warn", "error", "fatal").

  • levelFilter
    LogLevel[]

    Controlled visible levels. Lines without a level are always shown.

  • defaultLevelFilterdefault "["trace", "debug", "info", "warn", "error", "fatal"]"
    LogLevel[]

    Visible levels on mount; every level by default.

  • onLevelFilterChange
    (levels: LogLevel[]) => void

    A level chip was turned on or off.

  • query
    string

    Controlled search text.

  • defaultQuerydefault ""
    string

    The search on mount. Words match anywhere in the message or source, ignoring case and diacritics; the filter runs 120ms after typing stops.

  • onQueryChange
    (query: string) => void

    Every keystroke in the search.

  • filterModedefault "filter"
    "filter" | "highlight"

    filter hides the lines that do not match; highlight keeps them, marks the hits and steps through them with Enter / Shift+Enter.

  • regexdefault false
    boolean

    The search is a regular expression (case-insensitive). The .* toggle in the toolbar flips it; an invalid expression is marked and filters nothing.

  • follow
    boolean

    Controlled following of the newest line.

  • defaultFollowdefault true
    boolean

    Stick to the newest line on mount. Scrolling up pauses it by itself.

  • onFollowChange
    (follow: boolean) => void

    Following paused (scroll up, a key, an opened line) or resumed (the pill, End, the toggle, scrolling to the end).

  • wrap
    boolean | "auto"

    Controlled line wrapping.

  • defaultWrapdefault "auto"
    boolean | "auto"

    auto wraps below 768px and keeps one line (scrolling sideways) above.

  • onWrapChange
    (wrap: boolean) => void

    The wrap toggle was pressed.

  • showTimestampsdefault true
    boolean

    The time column. The toolbar clock toggle flips it too.

  • timeFormatdefault "time"
    "time" | "datetime" | "relative" | "iso"

    time 16:38:12.345, datetime 24.09.2026, 16:38:12, relative 2 min ago (ticking), iso as stored. Local times print after hydration.

  • showLevelsdefault true
    boolean

    The 3-letter level tag (INF, WRN, ERR) in the level's tone.

  • showSourcedefault true
    boolean

    The source chip ([spv], [http]), truncated at 14 characters.

  • showLineNumbersdefault false
    boolean

    Line numbers that keep counting after old lines drop. They are skipped when copying text.

  • ansidefault true
    boolean

    Renders ANSI colors (30 to 37, 90 to 97, backgrounds, bold, dim, italic, underline) with the tone palette; false strips them.

  • expandabledefault true
    boolean

    Lines with meta open a JSON tree under them on click, tap or Enter.

  • sizedefault "sm"
    "sm" | "md"

    12px text on 24px rows or 13px on 28px rows.

  • heightdefault 420
    number

    Height of the log body in px.

  • toolbardefault true
    boolean

    The row with search, level chips, toggles and the ⋯ menu.

  • searchShortcutdefault ""
    string

    A global key that focuses the search, like mod+shift+f (shown as a hint in the field). Empty binds nothing; Ctrl+F / ⌘F always works inside the viewer.

  • fileNamedefault "logs"
    string

    Download base name: logs-2026-09-24-1640.log.

  • loadingdefault false
    boolean

    Skeleton rows in place of the log; the toolbar waits.

  • emptyState
    ReactNode

    Replaces the built-in No logs yet state.

  • now
    Date | number

    The clock for relative timestamps. Defaults to the current time, ticking every second.

  • setApi
    (api: LogViewerApi) => void

    Called after mount with { append(lines), clear(), scrollToBottom(), follow(on), getLines() }. append takes a line, a string or arrays of either and batches them into one render per frame.

  • localedefault ""
    string

    Counts and times. Empty uses the UIProvider locale, then the browser's.

  • labels
    Partial<LogViewerLabels>

    Every string: log, search, regex, invalidRegex, levels { trace, debug, info, warn, error, fatal }, levelsGroup, wrap, timestamps, follow, live, paused, newLines(count, formatted), jumpToLatest, copy, download, clear, moreActions, actions, cancel, lines(…), errors(…), filtered(…), dropped(…), matches(index, total), previousMatch, nextMatch, empty, emptyDescription, noMatches, noMatchesDescription, clearFilters, showAll(…), details, loading, copied, copyFailed, downloaded(fileName). Pass only the keys you translate.

  • classNames
    Partial<Record<"toolbar" | "search" | "chips" | "chip" | "actions" | "body" | "row" | "number" | "time" | "level" | "source" | "message" | "meta" | "jump" | "empty" | "status", string>>

    Extra classes for the inner parts.

  • className
    string

    Extra classes for the root panel, merged last.

Parts

  • DEFAULT_LOG_VIEWER_LABELS

    constant

    The English copy of the toolbar, filters, menus and announcements. Spread it into labels when you translate only a few keys.

  • LogLine

    type

    One parsed line: message, plus optional id, time, level, source and meta (shown as JSON details).

  • parseLogLine

    function

    Parses "2026-09-24T16:38:12.345Z INFO [spv] message", "[16:38:12] WARN message", "ERROR message" and JSON lines with level / msg / time (pino numeric levels too; the other keys become meta). Signature: (text: string) => LogLine.

  • stripAnsi

    function

    Removes ANSI escape sequences (colors, cursor moves, links). Signature: (text: string) => string.

  • LOG_LEVELS

    constant

    The level recipe (tone, 3-letter tag, row tint, text class), for legends and custom rows. Type: Record<LogLevel, { tone: Tone; tag: string; row: string; text: string }>.

Examples

100,000 lines

Virtualized: only the rows on screen are in the DOM. Scroll, filter and watch the frame counter.

Warnings and errors only

The level chips filter with live counts; a controlled levelFilter.

Regex highlight

Highlight mode keeps every line and marks BR-\w+-\d+ hits; Enter and Shift+Enter step through them.

Build with ANSI colors

npm run build output: SGR colors mapped to the tone palette, line numbers, no wrap.

Parsed text lines

Plain strings and JSON lines parsed by parseLogLine; new lines are appended from a new array.

On a phone

Wrapped lines, a scrolling chip rail and the ⋯ menu that opens as a sheet below 768px.

Dependencies

@tanstack/react-virtualmotionlucide-reactbadgebuttondropdown-menuempty-statejson-viewersearch-inputtagtoggletooltipuse-clipboarduse-controllable-stateuse-localeuse-media-queryuse-stick-to-bottomfile-exportmotionnumber-formatrelative-timetext-matchtonesui-config

Files

  • components/ui/log-viewer.tsx

FAQ

Is the React Log Viewer component free?

The preview, props and docs are free; copying the source needs a Pro plan. See plans.

How do I install Log Viewer?

Run npx wingo-ui@latest add log-viewer. It also adds the Wingo UI items it builds on (Badge, Button, Dropdown Menu, Empty State, JSON Viewer and 16 more) and the npm packages @tanstack/react-virtual, motion and lucide-react. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/log-viewer.

How does Log Viewer behave on phones?

The toolbar wraps lines, shows or hides the time, and the ⋯ menu copies the visible lines, downloads everything as a dated .log file or clears the buffer; on a narrow viewer wrap and time move into that menu, and below 768px it opens as a bottom sheet while the chips scroll sideways.

See also

JSON VieweruseStickToBottomhookCode BlockFile exportlib

Used in

Automation Builder Page

More React data display components

MarkdownPresenceSchedulerTagTimelineTree View
All 23 React data display components

Articles using this component

  • React Log Viewer: Stream 100,000 Lines With Regex Search

    Component guides · Oct 16, 2026 · 8 min read

  • React Virtualized Table: 100k Rows With TanStack Virtual

    Component guides · Oct 16, 2026 · 12 min read

PreviousKbdNext Markdown

Invoice sync · job #4812

Northwind CRM · started today at 13:31 UTC · billing API, tax ID 12-3456740

Running
INFInfoxml✓ Invoice 2026-0448 validated (UBL 2.1, 22.6 KB)
DBGDebughttpPOST /v1/invoices/upload 429 316ms
TRCTracequeuejob invoices:4812 heartbeat 365
WRNWarnsyncInvoice 2026-0449: slow response, retrying (3/5), Details
TRCTracequeuejob invoices:4812 heartbeat 367
WRNWarnsyncInvoice 2026-0449: slow response, retrying (3/5), Details
ERRErrorvalidatorValidation error: BR-US-065 the unit code is missing on line 3, Details
INFInfosyncFetching messages for tax ID 78-1234500
DBGDebughttpGET /v1/invoices/status 429 448ms
DBGDebughttpGET /v1/invoices/status 200 482ms
WRNWarnsyncInvoice 2026-0449: slow response, retrying (4/5), Details
INFInfoxml✓ Invoice 2026-0449 validated (UBL 2.1, 13.3 KB)
DBGDebughttpGET /v1/invoices/status 200 483ms
DBGDebughttpGET /v1/invoices 200 548ms
INFInfomailSent the confirmation to james.miller@northwind.app
INFInfomailSent the confirmation to ryan.cooper@northwind.app
DBGDebughttpGET /v1/invoices 200 301ms
TRCTracequeuejob invoices:4812 heartbeat 380
DBGDebughttpGET /v1/invoices/download 200 463ms
WRNWarnsyncInvoice 2026-0450: slow response, retrying (3/5), Details
DBGDebughttpGET /v1/invoices 200 456ms
DBGDebugdbupsert received_invoices 2026-0450 10ms
INFInfosyncFetching messages for tax ID 78-1234500
DBGDebugdbupsert received_invoices 2026-0450 7ms
DBGDebughttpGET /v1/invoices/status 200 495ms
WRNWarnhttpRate limit: 45/50 requests per minute
DBGDebughttpGET /v1/invoices 200 164ms
DBGDebughttpGET /v1/invoices 200 469ms
WRNWarnsyncInvoice 2026-0450: slow response, retrying (3/5), Details
DBGDebughttpGET /v1/invoices 200 133ms
DBGDebugdbupsert received_invoices 2026-0450 4ms
WRNWarnsyncInvoice 2026-0450: slow response, retrying (4/5), Details
DBGDebughttpPOST /v1/invoices/upload 200 122ms
INFInfosyncFetching messages for tax ID 12-3456740
WRNWarnsyncInvoice 2026-0450: slow response, retrying (2/5), Details
WRNWarnsyncInvoice 2026-0450: slow response, retrying (4/5), Details
TRCTracequeuejob invoices:4812 heartbeat 399
DBGDebughttpGET /v1/invoices 200 289ms
400 lines · 24 errorsLive

stream: 20 lines/s · follow: on · search: no key · file: logs-2026-09-24-1640.log · locale: auto