Message Content Preprocessing#
Chainlit's frontend transforms raw message text before passing it to the Markdown renderer. The core of this pipeline is prepareContent in frontend/src/lib/message.ts, called by MessageContent in frontend/src/components/chat/Messages/Message/Content/index.tsx. The output feeds directly into Markdown.tsx and, for fenced code blocks, into CodeSnippet.tsx.
prepareContent β What It Does#
prepareContent accepts { elements, content, id, language } and returns { preparedContent, inlinedElements, refElements }.
Element Name Linkification#
The function builds a regex from all element names (sorted longest-first to prevent substring shadowing) and runs replaceAll across the entire content string . For each match it checks forId scope against the message id:
display: 'inline'elements β text left as-is; element appended toinlinedElements(rendered separately below the message body) .- Reference elements β text replaced with a Markdown link
[name](encoded-slug)and appended torefElements. The slug is URL-encoded viatoSafeLinkTarget, which also encodes parentheses to avoid breaking Markdown link syntax. - No matching element β text returned unchanged .
Known limitation: the regex runs over the raw string with no awareness of Markdown code fences (`````) or inline code spans (```). An element name that appears inside a code block will still be linkified, corrupting the code block's content.
Language Wrapping#
If message.language is set and the content does not already start with `````, prepareContent wraps the entire content in a fenced code block :
```{language}
{content}
This is what causes single-language tool outputs to be rendered as syntax-highlighted code.
---
## Callsites in `MessageContent`
`MessageContent` calls `prepareContent` twice (cite:code_file:e0572a9a:44-53) (cite:code_file:e0572a9a:90-99):
1. **Output** β `message.output` + optional streaming cursor placeholder; uses `message.language`.
2. **Input** β `message.input` (only when `showInput` is truthy); `language` is `message.showInput` if it's a string, else `undefined`.
The returned `refElements` is passed as a prop to `<Markdown>` for link resolution, and `inlinedElements` is rendered by `<InlinedElements>` beneath the Markdown block (cite:code_file:e0572a9a:72-79) (cite:code_file:e0572a9a:126-127).
---
## Rendering Pipeline
message.output / message.input
β
βΌ
prepareContent() β frontend/src/lib/message.ts
ββ linkifies element names (regex replace, no code-fence awareness)
ββ wraps in fenced block (if message.language set)
ββ returns preparedContent, refElements, inlinedElements
β
βΌ
β frontend/src/components/Markdown.tsx
ββ ReactMarkdown + remarkGfm, remarkDirective, remarkMath, rehypeKatex
ββ a{} resolver:
β if name β refElements β
β else β plain link
ββ pre{} resolver β
β
βΌ
β frontend/src/components/CodeSnippet.tsx
ββ highlight.js syntax highlight (monokai-sublime theme)
β
β frontend/src/components/Elements/ElementRef.tsx
ββ inline β , reference β clickable pill ( via onElementRefClick)
### `Markdown.tsx` β Link and Code Resolution
`Markdown.tsx` uses `ReactMarkdown` with a custom `a` component (cite:code_file:e6fc6d9f:156-171): if the link text matches an element in `refElements`, it renders `<ElementRef>`; otherwise it renders a standard `<a target="_blank">`. The `pre` component delegates to `<CodeSnippet>` (cite:code_file:e6fc6d9f:153-155). Plugins active by default: `remarkGfm`, `remarkDirective`, `cursorPlugin`; `remarkMath`/`rehypeKatex` are opt-in via the `latex` prop (cite:code_file:e6fc6d9f:124-136).
### `CodeSnippet.tsx` β Syntax Highlighting
`CodeSnippet` extracts the `language-*` class injected by `ReactMarkdown` from the `<pre>` node (cite:code_file:64fb8f03:60-63). If a language is found and `highlight.js` supports it, it renders `<HighlightedCode>` with `hljs.highlightElement()` (cite:code_file:64fb8f03:16-43); otherwise it falls back to a plain `<code>` block. The header always shows a language label and a copy button (cite:code_file:64fb8f03:79-92).
### `ElementRef.tsx` β Element Display
`ElementRef` renders `display: 'inline'` elements as a bold `<span>` and reference elements as a small pill-shaped `<a>` that fires `onElementRefClick` from `MessageContext` (cite:code_file:922366a5:10-27).
---
## Key Files
| File | Purpose |
|---|---|
| [`frontend/src/lib/message.ts`](cite:code_file:4ceb9431) | `prepareContent` β linkification + language wrapping |
| [`frontend/src/components/chat/Messages/Message/Content/index.tsx`](cite:code_file:e0572a9a) | `MessageContent` β calls `prepareContent`, renders `<Markdown>` |
| [`frontend/src/components/Markdown.tsx`](cite:code_file:e6fc6d9f) | Markdown rendering with element/link/code resolution |
| [`frontend/src/components/CodeSnippet.tsx`](cite:code_file:64fb8f03) | Syntax-highlighted code block with copy button |
| [`frontend/src/components/Elements/ElementRef.tsx`](cite:code_file:922366a5) | Inline vs. reference element rendering |