streamdown@2.7.0

vectorize-io/hindsightstreamdown@2.7.0Sep 30, 2026by github-actions[bot]

Upgrading?

AI Summary

streamdown@2.7.0 delivers major performance improvements and new features, including streaming optimizations, new props for customization, and Mermaid diagram rendering fixes.

Key Highlights

  • Significant performance improvements for streaming speed
  • New `fallbackComponent` prop for missing tags
  • New `disableAutolinkProtocols` prop
  • Mermaid diagrams render one at a time to prevent stream blocking
  • Incomplete images show loading skeletons

Breaking Changes

  • Node.js 20 or newer is now required when using @streamdown/code

New Features

  • Streaming performance optimizations
  • Fallback component
  • Protocol disabling
  • Portal prop
  • Image loading skeletons
  • Mermaid rendering fixes

Full Release Notes

This release is mostly about streaming speed, plus a few new props and a lot of fixes.

*:zap: Performance*
• Code blocks now highlight only the new lines as they stream, instead of the whole block on every update
• Blocks that are already parsed get reused while a document streams, and only block tokens are lexed
• Code and table auto-scroll runs once per frame instead of on every chunk
• Showing and hiding the caret no longer restyles the whole document, which used to cost tens of ms per toggle on long streams
• The code highlight cache now has a size limit (LRU), so memory stays bounded in long sessions
• Mermaid diagrams render one at a time while their fence streams, so a growing diagram no longer holds up the stream

*:sparkles: New*
• `fallbackComponent`: a fallback renderer for tags in `allowedTags` or HTML tags that have no entry in `components`
• `disableAutolinkProtocols`: turn off GFM autolinking for specific protocols (e.g. `["mailto"]`)
• `portal`: set the container used by Mermaid and table fullscreen and the link safety modal
• `defaultComponents` is now exported, so you can build overrides on top of the built-in renderers
• `onCopy` / `onError` now work on the default code and Mermaid copy buttons
• Incomplete images show a loading skeleton while they stream instead of disappearing

*:bug: Fixes*
• Mermaid diagrams auto-fit their container and text is readable again
• A `<` inside math (e.g. `$$ \sum_{j<k} $$`) no longer cuts off the rest of the message
• Code block buttons can be clicked inside nested scroll containers
• Tables copied or downloaded as Markdown keep literal HTML tags and entities
• Loose list items no longer get extra spacing from a wrapping `<p>`
• CJK: bare URLs after fullwidth punctuation are linkified again

*:jigsaw: remend 1.4.0*
• New single-pass scanner that follows CommonMark for code fences and spans: handles `~~~`, fences in quotes and lists, CRLF, and multi-backtick spans
• Fixes slow (quadratic) healing on input full of delimiters
• Identifiers like `snake__case` no longer create false emphasis
• Healing is now idempotent

*:warning: Heads up*
• `@streamdown/code@2.0.0` moves to *Shiki v4* and now needs *Node 20 or newer*
• `marked` is now on v18


Below, you can find the detailed changes.
---


### Minor Changes

- 6b14de4: Add a `disableAutolinkProtocols` prop to `<Streamdown>` for disabling GFM autolinking of specific URL protocols (e.g. `mailto`).

  ```tsx
  <Streamdown disableAutolinkProtocols={["mailto"]}>
    {"Contact us at hello@example.com"}
  </Streamdown>
  ```

  Bare emails and bare URLs whose protocol matches the list (case-insensitive, `"mailto"` and `"mailto:"` are equivalent) are unwrapped back to plain text. Explicit markdown links (`[text](url)`) are left as links, including when the label reconstructs the URL (e.g. `[foo@x.com](mailto:foo@x.com)`). When the prop is omitted, autolinking behavior is completely unchanged.

  Closes #607.

- b1965bb: feat: add `fallbackComponent` prop for missing map entries / `allowedTags`

  Adds a new `fallbackComponent` prop to `<Streamdown>`. When provided, it is
  used as a fallback renderer for any HTML tag or allowed custom tag that does
  not have an explicit entry in the `components` map:

  ```tsx
  <Streamdown
    allowedTags={{ mention: ["user_id"] }}
    fallbackComponent={({ node, children, ...props }) =>
      createElement(node!.tagName, props, children)
    }
  >
    {markdown}
  </Streamdown>
  ```

  `fallbackComponent` applies to custom tags declared via `allowedTags` (with no
  explicit component entry) and to standard HTML tags absent from the built-in
  component set (e.g. `<span>`, `<em>`, `<div>`, `<br>`). Built-in and explicit
  `components` entries always win — this is not a full unstyled mode.

  Refs #543

### Patch Changes

- b651249: Give animated word spans a style object instead of a CSS string, so rendering skips parsing a style declaration for every word on every update. `createAnimatePlugin` still writes CSS strings.
- b651249: Check whether a stream only appended text by comparing a slice of the new input with the previous one instead of calling `startsWith`, which compared the whole document character by character on every update.
- b651249: Hide the caret after a table or an unfinished code fence by marking the last element instead of changing the container's class and style. Each toggle used to restyle the whole document, which cost tens of milliseconds per flip on long streams.
- c971f42: Wire `onCopy` and `onError` through default code and Mermaid copy controls via `controls.code.copy` and `controls.mermaid.copy`.
- 4ec5639: Bound the code highlight cache. Results of streaming code blocks are no longer cached (each is superseded by the next update), and finished code blocks are kept in a least-recently-used cache bounded by their size (tokens, lines and characters) and by a limit of 5,000 results. Lookups compare the full code, so blocks that differ only in the middle no longer share tokens. A code block also ignores highlight results for code it has already moved past.
- 16e7e65: Export `defaultComponents` so custom `components` overrides can compose Streamdown's built-in renderers (styles, `data-streamdown`, link safety, code/mermaid/table behavior) instead of reimplementing them.
- 163c63b: fix(code-block): use absolute positioning for action buttons to fix click events in nested scroll containers

  Switches the code block action button wrapper from `position: sticky` to
  `position: absolute` (with `position: relative` on the container) so that
  hit-testing works correctly in layouts with multiple nested `overflow: auto`
  scroll containers. Previously, the `sticky` + `pointer-events-none/auto`
  pattern caused browsers to mis-route click events to the code block wrapper
  rather than the buttons when the component was embedded in 2+ nested scroll
  containers.

- e0ce123: Incomplete images during streaming now render a loading placeholder instead of being removed entirely. Incomplete images (e.g. `![alt](https://exampl`) are replaced with `![alt](streamdown:incomplete-image)` by remend, and the streamdown `ImageComponent` renders an animated skeleton for this special URL. This mirrors the existing behavior for incomplete links (`streamdown:incomplete-link`). The `streamdown` protocol is allowlisted for `img[src]` in the default sanitize schema so the sentinel survives rehype-sanitize + rehype-harden.
- e7a23ef: Render Mermaid diagrams one at a time while their code fence streams, pausing after each render for as long as it took, so a growing diagram no longer holds up the stream. Once the fence closes or the stream stops, the final diagram renders without a pause.
- b651249: Pin streaming code blocks and tables to the bottom once per frame instead of on every update, avoiding a forced layout per streamed chunk.
- 5504e50: Preserve literal HTML tags and character entities when copying or downloading tables as Markdown.
- 5b26c28: Speed up block parsing by lexing only block tokens and reusing already parsed blocks while a document streams.
- 18dcb20: Add a top-level `portal` prop for configuring the container used by Mermaid fullscreen, table fullscreen, and the built-in link safety modal.
- fdf4e33: Fix Mermaid diagrams so text is readable and diagrams auto-fit container.
  - Normalize SVG to remove responsive shrinking
  - Extract intrinsic size from viewBox
  - Add width-and-height auto-fit in PanZoom
  - Preserve user zoom/pan after initial fit
  - Add tests for SVG utilities and auto-fit behavior
- 53812cf: fix: unwrap a single paragraph child inside list items

  Loose list items from the markdown pipeline are often wrapped in a `<p>`. `MemoLi` now detects that sole paragraph child (including the memoized paragraph component) and renders its contents directly, so list items stay visually tight. Also updates the list animation retrigger test to match the unwrapped markup.

  Fixes #475

- a155a9e: Update `marked` from `^17.0.1` to `^18.0.11`.

  marked v18 no longer folds a block token's trailing blank line(s) into its own `raw`; that whitespace now surfaces as a separate `space` token immediately following the block (e.g. after `html`, `heading`, and `table` tokens). This changed the token stream shape that `parseMarkdownIntoBlocks` consumes, causing a dangling `space` token after a closed custom-tag HTML block to be emitted as its own standalone block. `parseMarkdownIntoBlocks` now folds a `space` token into the preceding block instead of pushing it as a new one, restoring v17-identical block boundaries and counts. The lossless `token.raw` concatenation invariant is preserved.

- Updated dependencies [cf0ac1c]
- Updated dependencies [e0ce123]
- Updated dependencies [fdd1351]
  - remend@1.4.0

Related Repositories