Skip to content

Streamdown is not working properly only in rendering the mathematical equations. Rest is working fine and perfect. #159

@Srijan-Baniyal

Description

@Srijan-Baniyal

Bug Description

The Streamdown package works as expected for general rendering, but it fails to correctly display mathematical equations. Other functionalities are working fine.

Image Image Image

Steps to Reproduce

You can just try it out. This is what I have facing problem in.

This is the code for my API:-

import { auth } from "@clerk/nextjs/server";
import { createOpenAI } from "@ai-sdk/openai";
import { streamText } from "ai";

const openai = createOpenAI({
  apiKey: process.env.OPENAI_API_KEY as string,
});

export async function POST(request: Request) {
  try {
    const { userId } = await auth();
    if (!userId) {
      console.log("❌ Unauthorized request - no userId");
      return new Response(
        JSON.stringify({
          error: "Unauthorized - Please sign in to use this service",
        }),
        { status: 401, headers: { "Content-Type": "application/json" } }
      );
    }
    const body = await request.json();
    let { messages, model, system, prompt } = body || {};
    if ((!messages || !Array.isArray(messages) || messages.length === 0) && typeof prompt === "string" && prompt.trim().length > 0) {
      messages = [{ role: "user", content: prompt }];
    }
    if (!messages || !Array.isArray(messages) || messages.length === 0) {
      return new Response(JSON.stringify({ error: "Messages are required" }), {
        status: 400,
        headers: { "Content-Type": "application/json" },
      });
    }
    const formattedMessages = messages.map((msg: any) => ({
      role: msg.role,
      content: msg.content,
    }));
    const selectedModel = typeof model === "string" && model.trim().length > 0
      ? openai(model)
      : openai("gpt-4o-mini");

    const systemPrompt =
      typeof system === "string" && system.trim().length > 0
        ? system
        : "You are a helpful AI assistant. You can help with various tasks including text processing, answering questions, and general conversation. Be friendly and informative.";

    const streaming = streamText({
      model: selectedModel,
      system: systemPrompt,
      messages: formattedMessages,
      maxOutputTokens: 1024,
    });
    return streaming.toTextStreamResponse();
  } catch (error) {
    console.error("Chat API error:", error);
    return new Response(JSON.stringify({ error: "Internal server error" }), {
      status: 500,
      headers: { "Content-Type": "application/json" },
    });
  }
}

And this is the code for the client side(Not fancy but yeah testing right now):-

"use client";

import { useEffect } from "react";
import { useCompletion } from "@ai-sdk/react";
import { Textarea } from "@/components/ui/textarea";
import { Button } from "@/components/ui/button";
import { Streamdown } from "streamdown";

export default function AIDashboard() {
  const { completion, input, setInput, handleSubmit, isLoading, error } =
    useCompletion({
      api: "/api/cipher",
      streamProtocol: "text",
    });

  useEffect(() => {
    if (error) {
      // eslint-disable-next-line no-console
      console.error("Chat error:", error);
    }
  }, [error]);

  return (
    <div className="space-y-4 p-4 max-w-4xl mx-auto">
      <div className="space-y-4 min-h-[300px] max-h-[500px] overflow-y-auto border rounded-lg p-4">
        {!completion && (
          <div className="text-center text-muted-foreground py-8">
            <p>Start a conversation with the AI assistant!</p>
            <p className="text-sm">
              Ask questions, request help, or just chat.
            </p>
          </div>
        )}
        {completion && (
          <div className="p-4 rounded-lg bg-gray-50 dark:bg-gray-900/20 border border-gray-200 dark:border-gray-800 mr-12">
            <div className="flex items-start space-x-2">
              <div className="font-semibold text-sm text-gray-800 dark:text-gray-200">
                AI Assistant
              </div>
            </div>
            <div className="mt-2 whitespace-pre-wrap text-gray-800 dark:text-gray-200">
              <Streamdown>{completion}</Streamdown>
            </div>
          </div>
        )}

        {isLoading && (
          <div className="bg-gray-50 dark:bg-gray-900/20 border border-gray-200 dark:border-gray-800 mr-12 p-4 rounded-lg">
            <div className="flex items-center space-x-2">
              <div className="font-semibold text-sm text-gray-800 dark:text-gray-200">
                AI Assistant
              </div>
              <div className="text-sm text-gray-500">typing...</div>
            </div>
            <div className="mt-2 flex space-x-1">
              <div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce"></div>
              <div
                className="w-2 h-2 bg-gray-400 rounded-full animate-bounce"
                style={{ animationDelay: "0.1s" }}
              ></div>
              <div
                className="w-2 h-2 bg-gray-400 rounded-full animate-bounce"
                style={{ animationDelay: "0.2s" }}
              ></div>
            </div>
          </div>
        )}
      </div>

      {/* Error Display */}
      {!!error && (
        <div className="p-4 bg-red-50 dark:bg-red-900/20 rounded-lg border border-red-200 dark:border-red-800">
          <p className="text-red-800 dark:text-red-200">
            Error: {String((error as any)?.message ?? "Something went wrong")}
          </p>
        </div>
      )}

      <form onSubmit={handleSubmit} className="space-y-3">
        <div>
          <label
            htmlFor="chat-input"
            className="block text-sm font-medium mb-2"
          >
            Message the AI Assistant:
          </label>
          <Textarea
            id="chat-input"
            value={input}
            onChange={(e) => setInput(e.target.value)}
            placeholder="Ask the AI assistant anything! Try: 'Hello!' or 'Tell me a joke' or 'Explain React'"
            className="min-h-24"
            onKeyDown={(e) => {
              if (e.key === "Enter" && !e.shiftKey) {
                e.preventDefault();
                handleSubmit(e as any);
              }
            }}
          />
          <p className="text-xs text-muted-foreground mt-1">
            Press Enter to send, Shift+Enter for new line
          </p>
        </div>

        <Button
          type="submit"
          disabled={!input.trim() || isLoading}
          className="w-full"
        >
          {isLoading ? (
            <div className="flex items-center space-x-2">
              <div className="w-4 h-4 border-2 border-white/20 border-t-white rounded-full animate-spin"></div>
              <span>Sending...</span>
            </div>
          ) : (
            "Send Message"
          )}
        </Button>
      </form>
    </div>
  );
}

Expected Behavior

Just to render out the mathematical equations in the output that's all.

Actual Behavior

The Mathematical Equations is just throwing out the syntax and looking quite weird.

Code Sample

Streamdown Version

^1.3.0

React Version

19.1.1

Node.js Version

24.4.1

Browser(s)

Safari

Operating System

macOS

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions