Skip to content

Scrollable code blocks & tables with auto-scroll during streaming #442

@shloimy-wiesel

Description

@shloimy-wiesel

Problem Statement

When a streamed (or static) response contains a large code block or table, it grows without bound and pushes everything else off-screen. The user has to scroll past hundreds of lines of code just to see the next paragraph. During streaming (isAnimating={true}) the problem is worse: new lines are appended at the bottom but the container keeps growing, so the user constantly loses their place.

Current behavior in the codebase:

Element Container file Overflow handling Height constraint
Code block body lib/code-block/body.tsx overflow-hidden — content is clipped, not scrollable None (auto)
Code block wrapper lib/code-block/container.tsx None (uses content-visibility: auto) None
Table wrapper lib/table/index.tsx overflow-x-auto overflow-y-auto None (auto)

Because there is no max-height, both code blocks and tables expand to their full content height regardless of how long they are.

Proposed Solution

1. Constrain height with max-height + vertical scroll

Apply a sensible default max-height (e.g. 400px for code blocks, 300px for tables) so that long content becomes scrollable instead of stretching the page. The feature should be on by default but allow consumers to override or disable it.

2. Auto-scroll to bottom during streaming

When isAnimating is true and new content is appended, the scrollable container should automatically stay pinned to the bottom - just like a terminal emulator.
If the user manually scrolls up to read earlier content, auto-scroll must pause (scroll-anchoring pattern). It re-engages only if the user scrolls back to the bottom, or when a new streaming response begins.

Auto-scroll applies only during streaming (isAnimating={true}), not during static rendering.

Proposed API

<Streamdown
  isAnimating={isLoading}
  codeBlockMaxHeight={400}   // default: 400 (px). Set to 0 or Infinity to disable.
  tableMaxHeight={300}       // default: 300 (px). Set to 0 or Infinity to disable.
>
  {streamingContent}
</Streamdown>

Both props are optional. When omitted, the defaults kick in.

Alternatives Considered

No response

Use Case

Priority

Important

Contribution

  • I am willing to help implement this feature

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions