Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Text

Pro

Log Stream Text for React

A live log in a box of fixed height: lines arrive at the bottom, the others glide up and the oldest fades out. For React with Base UI, when a page should show a server or a build at work.

Framework

Log Stream Text plays a log as if it were live, like tail -f: a line arrives at the bottom of the box every so often, the lines above glide up a row, and the oldest fades out at the top. The box holds the same number of rows from the start, already full, so the page around it never moves. Tags such as [INFO], [WARN] and [ERROR] take the theme's colours, and each line can carry a time that counts on from the first.

Use it for a developer tool's hero, an observability or hosting landing page, or a status screen that should feel switched on. For a terminal session with typed commands, use Terminal Text; for a single line typed out, Typing Text. A small timer counts the arrivals and a CSS keyframe animation the install adds to your stylesheet glides the lines, with no animation library; with reduced motion the lines arrive without moving.

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Usage

import { LogStreamText } from "@/components/ui/log-stream-text";
<LogStreamText
  start="09:41:00"
  texts={["[INFO] Compiling routes…", "[OK] Build succeeded in 1.2s"]}
/>

Demos

Rows

rows is how many lines the box shows, and so its height, from the first render; interval the time between one line and the next. Lines without a tag or a start time are shown as they are, and a line too long for the box ends in an ellipsis.

Preview width

Once

loop={false} stops after the last line, for a log that tells one story, such as a deployment. Without it, the lines start over after the last, and the times keep counting on.

Preview width

Accessibility

The log is a real role="log" region, named by label, with aria-live="off": screen readers can read its lines whenever they reach it, but aren't interrupted every time one arrives. It stops while the pointer is over it or it has focus, so anyone can read a line before it moves on, and its lines are real text, so they can be selected and copied. With reduced motion the lines arrive without gliding or fading.

Tab
Move focus to the log, which stops it until focus moves on.

FAQ

Why doesn't the box grow as lines arrive?

It holds rows lines from the start, already filled with the first lines, so its height is set before anything moves. Each line stays on one line, cut short with an ellipsis if it's too long, so a long message never pushes the others down.

Which tags are coloured?

A tag in square brackets at the start of a line: [INFO] takes the theme's info colour, [OK] and [SUCCESS] the success colour, [WARN] and [WARNING] the warning colour, [ERROR] and [FATAL] the destructive colour; [DEBUG] and any other tag are muted. Case doesn't matter.

Where do the times come from?

From start: the first line has that time, and each line after it is interval later, counting on through every loop. Worked out from the line's place in the stream rather than the clock, they're the same on the server and in the browser. Leave start out for lines without a time.

Does it pause?

Yes, while the pointer is over it and while it has focus, which a click or the Tab key gives it. It carries on from the next line once the pointer leaves or focus moves on.

Can I feed it real log lines?

Yes: with loop={false} it waits at the last line, and lines you add to texts arrive one every interval. It starts with the box full, so begin with at least rows lines.

Why aren't new lines announced?

A stream that spoke every line would talk over everything else on the page, so the log is aria-live="off": its lines are there to read whenever someone goes to them. When one event matters as it happens, such as a failed deployment, say it in a role="status" message of its own.

What happens with reduced motion?

Lines still arrive, since the log is content, but they appear in place without gliding up or fading.

API Reference

LogStreamText

<div> data-slot="log-stream-text"

PropType
textsstring[]

The lines, arriving in turn. A tag at the start of a line, such as [INFO], [WARN] or [ERROR], takes the theme's colour for it.

interval?number

The time between one line arriving and the next, in milliseconds. @default 1200.

Default
1200
label?string

The log's name for screen readers, such as Build output.

loop?boolean

Whether the lines start over after the last; otherwise the log stops there. @default true.

Default
true
rows?number

How many lines the box shows; it holds their height from the start. @default 6.

Default
6
start?string

The first line's time, HH:MM:SS; each line after it is interval later. Leave it out for lines without a time.

render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

AttributeType
data-turn?string

Mirrors turn.

CSS variableType
--log-stream-text-rowsstring
All 97 text effects