A SwiftUI library for rendering Markdown as native views — no web views, no external dependencies.
- Headers (levels 1–6), paragraphs, blockquotes, code blocks, dividers
- Ordered and unordered lists with arbitrary nesting (including mixed types)
- Tables with column alignment
- Images (remote URLs and local asset catalog)
- Inline formatting: bold, italic,
code,strikethrough, links - Custom view injection via
<view>tags with parameters and data payloads - Lazy rendering support for long documents
- Fully customizable styling through SwiftUI environment
- Swift 5.9+
- iOS 15+ / macOS 12+ / tvOS 15+
Add MarkdownUI as a Swift Package dependency:
https://github.com/nicklama/MarkdownUI
import MarkdownUI
struct ContentView: View {
@State var document = try! MarkdownDocument("# Hello\n\nThis is **Markdown**.")
var body: some View {
ScrollView {
Markdown(document, lazy: false)
.padding()
}
}
}Markdown(document, lazy: false)
.markdownStyle(MarkdownStyle(
header1: .init(
padding: .init(top: 20, bottom: 8),
font: .largeTitle.bold(),
color: .blue
),
code: .init(
padding: .init(top: 10, bottom: 10),
internalPadding: .init(top: 12, leading: 12, bottom: 12, trailing: 12),
cornerRadius: 12,
font: .system(.caption).monospaced(),
backgroundColor: .black.opacity(0.05)
)
))
.tint(.blue) // Link colorEmbed custom SwiftUI views inside your Markdown using <view> tags. Use the tag-only initializer for simple cases:
<view tag="myChart" />Markdown(document, lazy: false) { tag in
switch tag {
case "myChart":
MyChartView()
default:
EmptyView()
}
}Pass HTML-style attributes to custom views. Use the customView: initializer to access them:
<view tag="myChart" title="Sales Report" color="blue" />Markdown(document, lazy: false, customView: { customView in
switch customView.tag {
case "myChart":
MyChartView(
title: customView.parameters["title"] ?? "Chart",
color: customView.parameters["color"] ?? "gray"
)
default:
EmptyView()
}
})For larger payloads like JSON, use a non-self-closing tag. The content between the tags is available as Data?:
<view tag="dataChart">[{"label": "Q1", "value": 42}, {"label": "Q2", "value": 58}]</view>Markdown(document, lazy: false, customView: { customView in
if customView.tag == "dataChart", let data = customView.content {
let bars = try? JSONDecoder().decode([Bar].self, from: data)
MyDataChart(bars: bars ?? [])
}
})For long documents, use lazy: true to render blocks in a LazyVStack:
Markdown(document, lazy: true)| Element | Syntax |
|---|---|
| Headers | # H1 through ###### H6 |
| Bold | **text** or __text__ |
| Italic | *text* or _text_ |
| Strikethrough | ~~text~~ |
| Inline code | `code` |
| Links | [text](url) |
| Images |  |
| Unordered lists | - item |
| Ordered lists | 1. item |
| Blockquotes | > quote |
| Code blocks | ```language |
| Tables | | col | col | with alignment |
| Dividers | --- |
| Custom views | <view tag="name" /> or <view tag="name">data</view> |
BSD Zero Clause License — see LICENSE for details.