* feat: add lineNumbers prop to disable line numbers in code blocks
* docs: add lineNumbers prop and noLineNumbers meta string
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Dmitrii Troitskii <jsleitor@gmail.com>
Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(streamdown): preserve double newlines in literalTagContent tags
When a tag is in literalTagContent mode, double newlines (\n\n) inside
the tag content were incorrectly treated as paragraph separators by the
markdown parser, causing the content after the first blank line to be
excluded from the tag's children.
Fix: in preprocessLiteralTagContent, after escaping markdown metacharacters,
replace \n\n with HTML entities. This prevents the markdown
parser from interpreting blank lines as paragraph breaks while still
preserving the newline characters (rehype-raw decodes back to \n).
Fixesvercel/streamdown#456
* Run pnpm fix to resolve formatting issues
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix pre-existing formatting issue in code-block/index.tsx
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix lint and formatting issues
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Dmitrii Troitskii <jsleitor@gmail.com>
Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix(code-block): enable horizontal scrolling for long lines in code blocks
Change overflow-hidden to overflow-x-auto on the code block body container
so that long single-line comments and other long code lines are scrollable
horizontally instead of being silently clipped.
Fixes#438
* Add changeset for code block horizontal scroll fix
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Dmitrii Troitskii <jsleitor@gmail.com>
Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix(code-block): preserve empty lines in code blocks
## Summary
Fix empty lines collapsing in code blocks by inserting a newline character for empty rows.
## Problem
When rendering code blocks with empty lines, the `<span>` elements for empty rows had no content, causing them to collapse to zero height. This affected both visual display and copy behavior.
## Solution
Insert `"\n"` for empty lines instead of rendering an empty span. This ensures:
- **Visual**: Empty lines maintain proper height
- **Copy**: Copied text includes the empty lines correctly
## Changes
- `packages/streamdown/lib/code-block/body.tsx`: Add condition to detect empty rows (`row.length === 0` or single token with empty content) and render `"\n"` instead
* Fix lint issues
* Add tests
* Create fix-empty-lines.md
---------
Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
* feat: add translations/i18n support via translations prop
Implements #190 - adds a `translations` prop to the Streamdown component
that allows customizing all UI strings for internationalization.
Changes:
- Add `packages/streamdown/lib/translations-context.tsx` with
StreamdownTranslations type, defaultTranslations, TranslationsContext,
and useTranslations hook
- Add `translations?: Partial<StreamdownTranslations>` prop to Streamdown
- Wrap render output with TranslationsContext.Provider (both static and
streaming modes)
- Export StreamdownTranslations type and defaultTranslations from index
- Replace all hardcoded UI strings in sub-components with translations:
- code-block/copy-button: copyCode
- code-block/download-button: downloadFile
- mermaid/download-button: downloadDiagram, downloadDiagramAs{Svg,Png,Mmd},
mermaidFormat{Svg,Png,Mmd}
- mermaid/fullscreen-button: viewFullscreen, exitFullscreen
- table/copy-dropdown: copyTable, copyTableAs{Markdown,Csv,Tsv},
tableFormat{Markdown,Csv,Tsv}
- table/download-dropdown: downloadTable, downloadTableAs{Csv,Markdown},
tableFormat{Csv,Markdown}
- image: imageNotAvailable, downloadImage
- link-modal: openExternalLink, externalLinkWarning, close, copyLink,
copied, openLink
- Add __tests__/translations.test.tsx with 12 tests covering defaults,
custom translations, partial overrides, and context access
- Update download-dropdown.test.tsx to match new translation-based title
All 804 tests pass.
* Add changeset for translations prop
* Fix misplaced comments in translations type and broken test type assertion
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Use value equality for translations memo to support inline objects
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Dmitrii Troitskii <jsleitor@gmail.com>
Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: support custom starting line number in code blocks
Add support for specifying a custom starting line number via the code
fence meta string, e.g.:
```js startLine=10
const x = 1;
```
Implementation:
- Add `remarkCodeMeta` remark plugin that forwards the fenced-code meta
string to hast as the `metastring` property so it is available to the
custom React code component.
- Update the rehype-sanitize default schema to allow the `metastring`
attribute on `code` elements.
- Parse `startLine=N` from the meta string in `CodeComponent` and pass
the value down as a `startLine` prop.
- In `CodeBlockBody`, apply `counter-reset: line N-1` as an inline style
when `startLine > 1`, which overrides the Tailwind counter-reset class
and makes CSS counters begin from the specified number.
- Pass `startLine` through the `CodeBlock` and
`HighlightedCodeBlockBody` call chains.
- Add 12 tests covering the remark plugin, CodeBlockBody prop, and
CodeBlock integration.
Closes: resolves#287
* fix: lint errors — use literal keys, hoist regex, sort imports
* feat: allow customizing icons via icons prop
Add an IconContext-based system that lets users override any of the
built-in icons (CheckIcon, CopyIcon, DownloadIcon, etc.) by passing
a Partial<IconMap> via the new `icons` prop on <Streamdown>.
- Created lib/icon-context.tsx with IconMap type, IconProvider, and useIcons hook
- Updated all sub-components to consume icons via useIcons() instead of direct imports
- Added icons prop to StreamdownProps with IconProvider wrapper
- Exported IconMap type from package entry
Closes#412
* style: fix biome lint and formatting errors
* Merge branch 'main' into feat/custom-icons
* fix: review fixes for icons and startLine features
- Add missing changeset for icons feature (minor)
- Fix IconComponent type to include size prop matching actual usage
- Fix IconProvider memoization: use shallow comparison instead of
referential equality so inline icon objects don't cause re-renders
- Move START_LINE_PATTERN constant below all imports
- Add bounds checking for startLine (must be >= 1)
- Convert dynamic imports to top-level imports in startLine tests
- Add icon-context tests (5 tests)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Dmitrii Troitskii <jsleitor@gmail.com>
Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add Tailwind CSS prefix support
* fix: prevent double-prefixing of already-prefixed CSS classes
When cn() output (e.g. 'tw:size-full') is passed as className to a child
component that also calls cn() with the same prefix, prefixClasses() was
naively prepending the prefix again, producing 'tw:tw:size-full'.
Fix: skip classes that already start with the prefix+colon sentinel.
Add tests for both the all-already-prefixed and mixed cases.
* fix: lint errors — sort imports, format, hoist regex constants
* Add changeset for Tailwind prefix support
* fix: review fixes for tailwind prefix PR
- Bump changeset to minor (new feature, not patch)
- Rename shadowed cn import to baseCn in code-block/body.tsx
- Document that user className values are also prefixed
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Dmitrii Troitskii <jsleitor@gmail.com>
Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add useIsBlockIncomplete hook for detecting incomplete streaming blocks
Custom components can now detect when the block they're rendering is still
being streamed (incomplete). This is useful for showing loading states in
code blocks during streaming.
- Add BlockIncompleteContext and useIsBlockIncomplete hook
- Add hasIncompleteCodeFence utility to detect unclosed code fences
- Calculate isIncomplete state per block during render
- Default CodeComponent uses the hook to set data-incomplete attribute
- Add comprehensive tests for the new functionality
* docs: add useIsBlockIncomplete documentation
- Add "Streaming State" section to components.mdx explaining the hook
- Add "Avoid Continuous Re-rendering" section to mermaid.mdx with example
* fix: support tilde code fences in hasIncompleteCodeFence
Adds support for detecting incomplete ~~~ code fences in addition to
backtick fences, per CommonMark/GFM spec.
* fix: address PR review — line-based fence detection, rename hook/context
- rewrite hasIncompleteCodeFence to walk line-by-line per CommonMark spec
- fixes 6+ backtick fences being miscounted
- fixes false positives from inline backticks in prose
- respects fence character matching and length requirements
- rename block-position-context.ts → block-incomplete-context.ts
- rename useIsBlockIncomplete → useIsCodeFenceIncomplete
- add tests for edge cases (long fences, inline backticks, indentation)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Plugin trial
* Update docs and tests
* Update homepage add new section
* Update usage.tsx
* Split out CJK plugin
* Extract plugins into new packages
* Misc fixes
* Fix lint / formatting
* Fix: The Streamdown component's memo comparison doesn't compare the plugins prop, preventing re-renders when plugins change while other props stay the same
Co-authored-by: haydenbleasel <hello@haydenbleasel.com>
* Fix: TypeScript type mismatch at line 89 in `index.ts`: the visitor function's `index` parameter is typed as `number | undefined`, but the `visit` function expects `number | null`. The `undefined` type is not assignable to `number | null`.
This commit fixes the issue reported at /vercel/path0/packages/streamdown-cjk/index.ts:89
## TypeScript type incompatibility in visitor callback
**What fails:** TypeScript compiler fails during DTS (type definitions) build in `packages/streamdown-cjk/index.ts` at line 89
**How to reproduce:**
```bash
cd packages/streamdown-cjk
pnpm build
```
**Result:**
```
index.ts(89,3): error TS2769: No overload matches this call.
Overload 1 of 2, '(tree: Root, check: "link", visitor: BuildVisitor<Root, "link">, reverse?: boolean | null | undefined): undefined', gave the following error.
Argument of type '(node: Link, index: number | null, parent?: Parent) => number | undefined' is not assignable to parameter of type 'BuildVisitor<Root, "link">'.
Types of parameters 'index' and 'index' are incompatible.
Type 'number | undefined' is not assignable to type 'number | null'.
Type 'undefined' is not assignable to type 'number | null'.
```
**Root cause:** The visitor callback function passed to the `visit()` function from `unist-util-visit` was declaring the `index` parameter as `number | null`, but the visitor can also receive `undefined` as the index value. The `BuildVisitor` type signature expects the callback to accept `number | null | undefined` to properly match the library's API.
**Fix:** Updated the `index` parameter type annotation on line 89 from `number | null` to `number | null | undefined` to match the actual possible values that the visitor receives and the expected `BuildVisitor` type signature.
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: haydenbleasel <hello@haydenbleasel.com>
* Fix tests
* Remove CDN paths
* Fix tests
* More cleanup
* Remove shiki from main bundle
* Update index.ts
* Cleanup test repo
* Cleanup website
* Delete next-env files
* Update .gitignore
* Fix lockfile
* Upgrade Next and React
* Simplify themes
* Update test site
* Misc fixes
* Type fixes
* Create perky-cobras-punch.md
---------
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>