# Markdown

The Compose UI components SDK can render message text as markdown, so a message written with `**bold**` or a `> quote` displays formatted instead of showing its syntax.

Markdown is off by default. Opt in by replacing the message text formatter on `ChatTheme`:

```kotlin
ChatTheme(
    autoTranslationEnabled = autoTranslationEnabled,
    messageTextFormatter = MessageTextFormatter.markdownFormatter(
        autoTranslationEnabled = autoTranslationEnabled,
    ),
) {
    // Your UI content
}
```

`ChatTheme`'s default is untouched, so nothing renders differently until you pass this.

Pass `autoTranslationEnabled` the same value you pass to `ChatTheme`, as the default formatter does. Hardcoding it leaves the formatter resolving translations, or failing to, while the rest of the UI does the opposite.

The View-based SDK has its own markdown support through a separate module. See [Formatting](https://getstream.io/chat/docs/sdk/android/v6/ui/formatting/#markdown) for that one.

## What is rendered

| Construct                                       | Result                                  |
| ----------------------------------------------- | --------------------------------------- |
| `*italic*`, `**bold**`, `***both***`            | Emphasis                                |
| `~~strikethrough~~`                             | Struck through                          |
| `` `code` ``                                    | Monospace with a subtle background      |
| Fenced and indented code blocks                 | Monospace block, no syntax highlighting |
| `# Heading` through `###### Heading`            | Six levels, mapped to the type scale    |
| `- item`, `1. item`, and nesting                | Marker per item, indented per level     |
| `> quote`, including nesting                    | Indented with a rail drawn beside it    |
| `[text](url)`, `[text][ref]`, and bare URLs     | Tappable links                          |
| `---`                                           | A thematic break                        |
| Two trailing spaces, a trailing `\`, or `<br/>` | A hard line break                       |

Four constructs are not drawn, because none of them can be expressed in a styled string:

- An image renders as its alt text, which is the specification's own fallback for an image that cannot be shown.
- A table keeps its source text.
- A task list has no checkboxes. Its items render as an ordinary bulleted list with the marker left as text, so `- [x] done` reads as `• [x] done`.
- HTML is not interpreted. A block keeps its source and an inline tag is shown as written, so `<b>bold</b>` reads as typed. The one exception is `<br/>`, which breaks the line as the table above says.

Anything the renderer does not recognise falls back to the text as typed, so no content is dropped.

## Line breaks

A single line break renders as a line break. The specification collapses it to a space and asks for two trailing spaces to force one, which nobody can type on a phone keyboard, and following it would reflow every multi-line message that reads correctly as plain text today. The View-based SDK deviates the same way.

## Styling

Markdown is styled from the `typography` and `colors` you pass to `markdownFormatter`, which default to the SDK's own. It does not read them from `ChatTheme`, because the formatter is built before the theme provides anything. So if you customise either on `ChatTheme`, pass the same values through:

```kotlin
val isInDarkMode = isSystemInDarkTheme()
val colors = StreamColors.defaultColors().copy(textHighEmphasis = Color.Black)

ChatTheme(
    isInDarkMode = isInDarkMode,
    colors = colors,
    messageTextFormatter = MessageTextFormatter.markdownFormatter(
        autoTranslationEnabled = autoTranslationEnabled,
        isInDarkMode = isInDarkMode,
        colors = colors,
    ),
) {
    // Your UI content
}
```

Pass `isInDarkMode` as well, not only `colors`. It selects the message text and mention colours on top of backing the `colors` default, so an app driving dark mode itself, rather than from the system setting, styles those from the wrong mode without it.

Headings use four sizes for six levels, since the type scale has no more than that, so two pairs share one. The second and third levels render identically, and the fifth and sixth differ only in colour.

## Mentions and links

Markdown renders first, then mentions, links and emails are highlighted over the result. So a mention still highlights in a message that also contains markdown, and a URL inside a code span is deliberately left alone rather than linkified.

Markdown inside a mention token is a different matter: writing `@**Name**` renders the name bold, but the composer drops the mention, because it looks for the `@Name` token in the text you are sending and `@**Name**` no longer contains it.

Only `http`, `https`, `mailto` and `tel` destinations become links. Message text comes from other people and a tapped link is handed to the system, so a link reading as ordinary prose must not be able to open a `javascript:` target or deep link into your app.

## Where markdown is not rendered

Only the message list renders markdown. Quoted replies, channel list previews, thread and search previews, and the composer's edit indicator continue to show the markdown source, since they do not go through `messageTextFormatter`.

## Combining with other formatters

`markdownFormatter` replaces the default formatter and cannot be combined through `MessageTextFormatter.composite`. Rendering changes the text's length, so the character offsets a composed formatter styles by no longer line up with `Message.text`.

If you need markdown plus your own styling, wrap the formatter and post-process the `AnnotatedString` it returns.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/android/v6/compose/markdown/](https://getstream.io/chat/docs/sdk/android/v6/compose/markdown/).