Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

Fenced code blocks don't render incrementally during streaming

Abierto
#473 3 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

@mvanhorn ya está trabajando en esto.

Desde el 22/3/2026.

  • #474 de @mvanhorn — abierto

Evaluación

Dificultad
4/5
Tiempo estimado
3-5 días
Aptitud para principiantes
48/100
Tipo de issue
Error
Claridad
Bastante claro
Estado de actividad
Tranquilo
Stack tecnológico
react, typescript
Área
ai, frontend

Línea de trabajo

Reproduce el problema con una cadena Markdown en crecimiento que se pase a Streamdown y, después, examina el comportamiento de parseMarkdownIntoBlocks y marked's Lexer.lex con fences sin cerrar. Sigue hasIncompleteCodeFence, Block, useIsCodeFenceIncomplete y parseIncompleteMarkdown para comprobar si los tokens de código parciales llegan a la ruta de renderizado de código. La tarea está terminada cuando el contenido de código cercado se renderiza incrementalmente durante el streaming sin hacer retroceder el renderizado de prosa.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

Fenced code blocks don't render incrementally during streaming

Description

When streaming AI responses token-by-token, fenced code blocks (triple backtick) appear to render all at once rather than incrementally as the markdown string grows. Text outside code blocks streams smoothly with visible character-by-character rendering, but the moment a code fence opens, the content inside appears to buffer until the block is complete, then dumps visually.

Environment

  • streamdown: 2.5.0
  • @streamdown/code: 1.1.1
  • React 18
  • Using <Streamdown animated isAnimating plugins={{ cjk, code, math, mermaid }}>

Reproduction

  1. Pass a growing markdown string to <Streamdown> that includes a fenced code block
  2. The string updates token-by-token (verified via console.log — length increments: 4, 8, 24, 26, 34, 58... smoothly up to 860+)
  3. Prose text before and after the code fence streams beautifully with the animated caret
  4. The code block content appears all at once or in large chunks

Minimal usage:

<Streamdown animated isAnimating plugins={{ cjk, code, math, mermaid }}>
  {streamingMarkdownString}
</Streamdown>

Where streamingMarkdownString grows incrementally and contains:

Some text before...

```typescript
// This content appears to dump all at once
function example() {
  return "hello";
}
```

What we investigated

1. Transport is not the issue

Console logging confirms the raw string updates smoothly, token-by-token. The buffering is in the rendering layer, not the data transport.

2. parseMarkdownIntoBlocks uses marked's Lexer.lex()

parseMarkdownIntoBlocks calls Lexer.lex(markdown, { gfm: true }) and iterates tokens. When the lexer encounters a fenced code block, it emits a single code token only after the closing fence is found. While the fence is open (no closing backticks yet), the raw content likely falls into a different token type or gets merged differently.

Key question: Does marked's Lexer.lex() emit a code-type token for a partial fence (no closing backticks), or does it fall back to a paragraph token? If it treats unclosed fences as paragraphs, the block would render as text during streaming and then suddenly swap to a code block when the closing fence arrives — which is exactly what we observe.

3. isIncomplete infrastructure exists

The Streamdown component checks for unclosed code fences (via hasIncompleteCodeFence) on the last block and passes isIncomplete={true} to the Block component. The useIsCodeFenceIncomplete hook exposes this. However, if marked's lexer doesn't produce a code-type token for the unclosed fence, the isIncomplete flag may not trigger the code block rendering path.

4. chatbot.ai-sdk.dev achieves smooth code block streaming

The reference chatbot at chatbot.ai-sdk.dev (which uses Streamdown via MessageResponse) renders code blocks incrementally during streaming. This suggests either a configuration difference or that AI SDK's useChat delivers data in a way that interacts with Streamdown differently than a custom WebSocket stream.

5. parseIncompleteMarkdown prop

We noticed this prop defaults to false. Does remend processing affect how unclosed code fences are rendered during streaming? Does enabling parseIncompleteMarkdown={true} change the behavior?

Our setup (not using AI SDK)

We're NOT using AI SDK's useChat. We have a custom WebSocket streaming pipeline that delivers text tokens from a container running Claude CLI. The growing markdown string is accumulated in a Zustand store and passed as children to Streamdown. The string IS raw markdown with fences.

Questions

  1. Does parseMarkdownIntoBlocks produce incremental block updates for unclosed code fences?
  2. Is there a prop or configuration we're missing to enable incremental code block rendering?
  3. Does chatbot.ai-sdk.dev use any additional configuration beyond what's in its open-source MessageResponse wrapper?
  4. Could remend preprocessing be the key — does it close unclosed fences before the lexer sees them?
Lenguaje dominante
TypeScript
Estrellas
5.7k
Forks
313
Merge medio
1 d 19 h
PR fusionados (30 d)
24

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de vercel/streamdown

Todos los issues de vercel/streamdown

Issues similares

Más issues de TypeScript

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.