Follow a growing log to the latest
Follow the newest line until the reader scrolls up; a named button jumps back and resumes following.
At a glance
- Who: Developers building a chat, log, or console that grows while the reader watches.
- What: Follow the newest line until the reader scrolls up; a named button jumps back and resumes following.
offsetdefaults to 8px;behaviordefaults to"auto"(instant jump). - Use when: On any overflowing scroll parent (
AppShellmainRefor a custom pane) with content that appends. - Avoid when: For route-change scroll-to-top — use
Reset scroll on route change. CSSoverflow-anchorcannot expose a latch for the button. - See also: Reset scroll on route change, Lay out an admin page (sidebar + topbar + header), Localize kit chrome in English or French
Install
pnpm add @zedtech/zedkit-uiLatest: 2.0.1 (via changesets).
Example
"use client";
import { useRef, type ReactNode } from "react";
import { JumpToLatest, useStickToBottom } from "@zedtech/zedkit-ui";
export function LogPane({ children }: { children: ReactNode }) {
const scroller = useRef<HTMLDivElement>(null);
const { isStuck, jumpToLatest } = useStickToBottom(scroller);
return (
<div className="relative pane-shrink">
<div ref={scroller} tabIndex={0} role="region" aria-label="Log" className="h-full overflow-y-auto focus-ring">
{children}
</div>
<JumpToLatest className="absolute pin-bottom pin-x" stuck={isStuck} onClick={jumpToLatest} />
</div>
);
}Wraps: (none)
Accessibility
Hidden while latched (not in the tab order). Visible control is a real button with the kit 44px phone target; empty label falls back to kit chrome (that visible text is the accessible name — do not pass a conflicting aria-label). jumpToLatest moves focus to the scroller. A custom pane needs tabIndex={0}, role="region", aria-label, and a visible focus ring (AppShell main already does this). Jumps instantly by default and always when motion is reduced (OS or html[data-motion=reduce]). behavior="smooth" while a log is still growing is a continuous animation — keep "auto" when the reader may be motion-sensitive. Not a live region: in chat, pair with StreamText; a plain log or console should add aria-live or role="status" if new lines must be announced.
Format a compact duration
Turn a millisecond span or two instants into compact `6m 17s` / `1h 02m` text. Digits follow the locale argument; unit letters stay Latin. Install with npm.
Reset scroll on route change
Scrolls to the top (or to the `#hash` anchor) when the route key changes, leaving back/forward restoration to the browser. Install with npm.