Skip to content

fix: forward rest props through block code - #585

Merged
farnabaz merged 1 commit into
vercel:mainfrom
dasheidt:fix/block-code-rest-props
Aug 21, 2026
Merged

farnabaz merged 1 commit into
vercel:mainfrom
dasheidt:fix/block-code-rest-props

Conversation

@dasheidt

@dasheidt dasheidt commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

The asymmetry

CodeComponent has two branches. The inline branch spreads its rest props:

return (
  <code className={cn(...)} data-streamdown="inline-code" {...props}>

The block branch passes a hand-picked six and drops the rest:

return (
  <CodeBlock className={className} code={code} isIncomplete={...}
             language={language} lineNumbers={...} startLine={startLine}>

So an attribute a consumer puts on a code element — via a rehype plugin, which is the supported way to annotate rendered output — reaches inline code and silently disappears on code blocks. One component, two branches, disagreeing about whether consumer props exist.

On main, this document stamps two code elements and exactly one stamp survives:

Some `inline` code.

```ts
const x = 1;

## The change

Forward the rest props on the block branch too, minus `data-block` — the marker the custom `pre` component sets to identify a fenced block, which is internal and should not reach the DOM.

`CodeBlock` already spreads its rest onto the code-block **body**, whose text is exactly the code: the header label and the copy/download controls are siblings, and line numbers are a CSS counter rather than text nodes. So the props land on the element they describe, with no further plumbing.

Then `CodeBlockBody`'s comparator, which listed props by hand and ignored the rest, so a forwarded attribute kept whatever value it first rendered with. (#444 has since added a sixth entry, `maxHeight` — the list grows with each new prop, which is the shape of the problem.) Its custom comparator is removed rather than extended: React's default shallow comparison already compares `result` by reference — the tokens are memoized upstream, so an unchanged code string still does not re-highlight — and unlike a hand-written list it also covers the forwarded props.

## Tests

`__tests__/block-code-rest-props.test.tsx`, four cases, each hunk pinned by its own assertion:

- a consumer attribute reaches a fenced code block (fails on `main`)
- it lands on the element holding the code text, not the chrome (fails on `main`)
- it **updates** when the block moves (fails on `main`, and still fails with only the forwarding hunk applied — this is the one that pins the comparator)
- `data-block` does not leak into the DOM (passes on `main` trivially, guards this change)

Independent of #584: these pass against unmodified comparators, so the two can be reviewed and merged in either order.

## Related

- #583 covers the same code-block staleness from the content side. If this lands, `sameCodeContent` is not needed for the forwarded-props case; if you prefer that shape, the comparator hunk here can be dropped and rebuilt on top of it.
- Deliberately not included: the `pre` component takes only `children` and drops its own props entirely. That is the same class of bug, but fixing it changes what element gets emitted, so it seemed worth keeping separate. Happy to follow up.

---

Rebased onto `main` at `3327c18`. Verified with `pnpm build:packages`, `vitest run` (79 files, 1074 tests), `biome check` on the changed files.

@vercel

vercel Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Someone is attempting to deploy a commit to the Vercel Team on Vercel.

A member of the Team first needs to authorize it.

@dasheidt

Copy link
Copy Markdown
Contributor Author

Rebased onto main at 3327c18. The one conflict was in lib/code-block/body.tsx: #444 added maxHeight as a sixth entry to the hand-written comparator this PR removes. Resolved in favour of removing it — React's default shallow comparison covers maxHeight along with everything else, so the list does not need maintaining as props are added.

Full suite green (79 files, 1074 tests). Still independent of #584 and reviewable in either order.

@farnabaz
farnabaz force-pushed the fix/block-code-rest-props branch from 3bc39c3 to b53ab06 Compare August 21, 2026 11:30
The `code` component spreads `...props` on its inline branch and passes a
hand-picked set of props on its block branch, so an attribute a consumer puts
on a fenced code element — via a rehype plugin, the supported way to annotate
rendered output — reaches inline code and silently disappears on code blocks.
The two branches of one component disagree about whether consumer props exist.

Forward them on the block branch too, minus `data-block`, which is the marker
the custom `pre` component sets to identify a fenced block and is internal.
`CodeBlock` already spreads its rest onto the code-block body, whose text is
exactly the code — the header label and the copy/download controls are
siblings — so the props land on the element they describe.

`CodeBlockBody`'s comparator listed five props by hand and ignored the rest,
which meant a forwarded attribute kept the value it first rendered with. Its
custom comparator is now removed: React's default shallow comparison already
compares `result` by reference, so an unchanged code string still does not
re-highlight, and it covers the forwarded props as well.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Ian Heidt <71056976+dasheidt@users.noreply.github.com>
@farnabaz
farnabaz force-pushed the fix/block-code-rest-props branch from b53ab06 to b0813c9 Compare August 21, 2026 11:32

@farnabaz farnabaz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@farnabaz
farnabaz merged commit 725e390 into vercel:main Aug 21, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants