---
title: "Chat Message Rendering: Markdown, Code Blocks, Tables, and Rich Content"
description: "Build a rich message renderer for AI agent chat interfaces that handles markdown, syntax-highlighted code blocks, tables, and embedded images using React and TypeScript."
canonical: https://callsphere.ai/blog/chat-message-rendering-markdown-code-blocks-tables-rich-content
category: "Learn Agentic AI"
tags: ["Markdown", "Syntax Highlighting", "React", "TypeScript", "Rich Content"]
author: "CallSphere Team"
published: 2026-03-17T00:00:00.000Z
updated: 2026-05-06T01:02:44.938Z
---

# Chat Message Rendering: Markdown, Code Blocks, Tables, and Rich Content

> Build a rich message renderer for AI agent chat interfaces that handles markdown, syntax-highlighted code blocks, tables, and embedded images using React and TypeScript.

## The Challenge of Agent Message Content

AI agents produce rich output: code snippets in multiple languages, data tables, mathematical notation, step-by-step instructions with nested lists, and inline references. Rendering this content faithfully in a chat bubble requires a markdown pipeline that handles edge cases gracefully without introducing security vulnerabilities through raw HTML injection.

## Setting Up the Markdown Pipeline

The `react-markdown` library provides a solid foundation. Combine it with `remark-gfm` for GitHub Flavored Markdown (tables, strikethrough, task lists) and a syntax highlighting library for code blocks.

```mermaid
flowchart LR
    INPUT(["User intent"])
    PARSE["Parse plus
classify"]
    PLAN["Plan and tool
selection"]
    AGENT["Agent loop
LLM plus tools"]
    GUARD{"Guardrails
and policy"}
    EXEC["Execute and
verify result"]
    OBS[("Trace and metrics")]
    OUT(["Outcome plus
next action"])
    INPUT --> PARSE --> PLAN --> AGENT --> GUARD
    GUARD -->|Pass| EXEC --> OUT
    GUARD -->|Fail| AGENT
    AGENT --> OBS
    style AGENT fill:#4f46e5,stroke:#4338ca,color:#fff
    style GUARD fill:#f59e0b,stroke:#d97706,color:#1f2937
    style OBS fill:#ede9fe,stroke:#7c3aed,color:#1e1b4b
    style OUT fill:#059669,stroke:#047857,color:#fff
```

```typescript
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { oneDark } from "react-syntax-highlighter/dist/esm/styles/prism";

interface MessageRendererProps {
  content: string;
}

function MessageRenderer({ content }: MessageRendererProps) {
  return (

      {content}

  );
}
```

The `components` prop lets you override how each markdown element renders. This is where you add syntax highlighting, custom table styles, and image handling.

## Syntax-Highlighted Code Blocks

The code component must distinguish between inline code and fenced code blocks. Fenced blocks have a `className` prop containing the language.

```typescript
import { ComponentPropsWithoutRef } from "react";

function CodeBlock({
  children,
  className,
  ...props
}: ComponentPropsWithoutRef) {
  const match = /language-(\w+)/.exec(className || "");

  if (!match) {
    return (
      `
        {children}
      `
    );
  }

  const language = match[1];
  const codeString = String(children).replace(/\n$/, "");

  return (

        {language}

        {codeString}

  );
}
```

## The Copy Button

Every code block needs a copy button. Implement it with the Clipboard API and visual feedback.

```typescript
import { useState, useCallback } from "react";

function CopyButton({ text }: { text: string }) {
  const [copied, setCopied] = useState(false);

  const handleCopy = useCallback(async () => {
    await navigator.clipboard.writeText(text);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  }, [text]);

  return (

      {copied ? "Copied!" : "Copy"}

  );
}
```

## Styled Tables

Agent responses frequently include comparison tables, data summaries, and feature matrices. Default HTML tables look terrible without styling.

```typescript
import { ComponentPropsWithoutRef } from "react";

function StyledTable({
  children,
  ...props
}: ComponentPropsWithoutRef) {
  return (

  );
}
```

Add matching overrides for `th` and `td` elements with padding, borders, and alternating row colors to complete the table styling.

## Safe Image Rendering

Agent responses may reference images. Render them with size constraints and error handling so that broken image links do not break the entire chat layout.

```typescript
import { useState } from "react";

function SafeImage(props: React.ImgHTMLAttributes) {
  const [error, setError] = useState(false);

  if (error) {
    return (

        Image could not be loaded

    );
  }

  return (
     setError(true)}
      className="max-w-full h-auto rounded-lg my-2"
      loading="lazy"
    />
  );
}
```

## Preventing XSS in Rendered Content

`react-markdown` does not render raw HTML by default, which is the safest behavior. If you enable the `rehype-raw` plugin to support HTML in agent responses, you must pair it with `rehype-sanitize` to strip dangerous elements like `` tags and event handlers. For most agent interfaces, keeping raw HTML disabled is the better choice.

## FAQ

### How do I handle LaTeX or mathematical notation in agent responses?

Install `remark-math` and `rehype-katex`, then add them to the `remarkPlugins` and `rehypePlugins` arrays respectively. This renders inline math with single dollar signs (`$x^2$`) and block math with double dollar signs. Import the KaTeX CSS stylesheet to style the rendered equations.

### How do I prevent very long code blocks from making the chat bubble too wide?

The `overflow-x-auto` class on the code container enables horizontal scrolling when code lines are wider than the bubble. Set `word-break: break-all` on inline code to prevent long strings without spaces from overflowing. For block code, never set `white-space: pre-wrap` because it breaks code indentation.

### Should I memoize the message renderer?

Yes. Wrap `MessageRenderer` in `React.memo` because chat messages are immutable after they are fully streamed. Without memoization, every new message appended to the list causes all previous messages to re-render their full markdown pipeline, which becomes expensive with syntax highlighting across dozens of messages.

---

#Markdown #SyntaxHighlighting #React #TypeScript #RichContent #AgenticAI #LearnAI #AIEngineering

---

Source: https://callsphere.ai/blog/chat-message-rendering-markdown-code-blocks-tables-rich-content
