Displaying Markdown in SwiftUI: A Step-by-Step Tutorial

This article contains tips on how to easily display Markdown in SwiftUI using the Text widget, as well as how to change link colors and use AttributedString

Displaying Markdown in SwiftUI: A Step-by-Step Tutorial

Introduction to Markdown in SwiftUI

Since the groundbreaking release of SwiftUI for iOS 15, Apple introduced native built-in support for rendering Markdown text directly through the standard Text widget. This native integration drastically reduced the necessity for third-party parsing libraries when developers merely wanted to embed bold text, italics, strikethroughs, or hyperlinks.

To write inline Markdown within SwiftUI, you simply construct a standard Text view and write the Markdown payload right inside its initialization string parameter. The framework intelligently parses the syntax during layout compilation.

struct ContentView: View {
    var body: some View {
        VStack(spacing: 12) {
            Text("This is just regular Text")
            Text("This with **Bold** style")
            Text("This with *Italic* style")
            Text("This one with ~Strikethrough~ style")
            Text("This one with `Monopace` style")
            Text("This one with a [Go to Google](https://www.google.com) Link")
        }
        .padding()
    }
}
Easy Way to Display Markdown in SwiftUI using Text

Writing Markdown in a Variable

As your content grows or gets populated from an external API, you'll naturally want to source your Markdown from an isolated variable instead of hard-coding literals in the view body. A beginner might approach it like this:

struct ContentView: View {
    @State private var text: String = "**SwiftUI** helps you build great-looking apps across all _Apple_ platforms with the power of Swift — and surprisingly little code. [Learn SwiftUI](https://developer.apple.com/xcode/swiftui/)"

    var body: some View {
        VStack {
            Text(text) // This won't parse the markdown!
        }
        .padding()
    }
}

Unfortunately, this straightforward approach won't work as expected. Instead of rendering the stylized Markdown, SwiftUI renders raw characters verbatim, exposing asterisks and brackets natively.

Easy Way to Display Markdown in SwiftUI using Text

Why does this fail? Under the hood, if you write a string literal inside Text("..."), the Swift compiler treats it as a LocalizedStringKey natively, automatically triggering the Markdown parsing pipeline. Conversely, passing a pure generic String variable forces standard unformatted textual representation.

Therefore, to bind a populated variable properly to Markdown styling, you must either explicitly annotate your property as a LocalizedStringKey or cast it ad-hoc before injection.

struct ContentView: View {
    @State private var rawString: String = "Hello **World**"
    
    // Approach 1: Declare explicitly as LocalizedStringKey
    @State private var localizeText: LocalizedStringKey = "**SwiftUI** helps you build great-looking apps... [SwiftUI](https://developer.apple.com/xcode/swiftui/)"

    var body: some View {
        VStack(spacing: 20) {
        	// Approach 2: Force casting Strings
            Text(LocalizedStringKey(rawString))
            
            Divider()
            
            // Using predefined LocalizedStringKey
            Text(localizeText)
        }
        .padding()
    }
}
Easy Way to Display Markdown in SwiftUI using Text

By default, any inline Markdown link inherently adopts Apple's global interactive accent (which defaults to standard royal blue on most devices). While that guarantees a consistent OS-level feel, branding might demand otherwise.

To easily mutate the hyperlinked segments within your text block, append the .tint() modifier. This ensures the tappable boundaries are repainted natively without tearing down the underlying localized key setup.

struct ContentView: View {
    var body: some View {
        VStack(spacing: 12) {
            Text("Regular [link](https://apple.com) with default color")
            
            Text("Styled [link](https://apple.com) with a custom [hyperlink](https://apple.com) color")
                .tint(.pink) // Overriding default blue
        }
        .padding()
    }
}
Easy Way to Display Markdown in SwiftUI using Text

Working with AttributedString

For complex payload handling where you need varying fonts, background highlights, and inline spacing, simple Markdown falls short. This is where AttributedString excels. Introduced in recent iOS iterations, AttributedString is a modern, value-type replacement for the older Objective-C NSAttributedString.

By constructing an AttributedString from your Markdown payload, you unlock profound programmatic access to specific text ranges and ranges of stylized characters.

struct ContentView: View {
    private var attrText: AttributedString {
        do {
            var text = try AttributedString(
                markdown: "**SwiftUI** helps you build great-looking apps across all _[Apple](https://apple.com)_ platforms with the power of Swift — and surprisingly little code."
            )

            // Highlighting a specific substring
            if let range = text.range(of: "SwiftUI") {
                text[range].backgroundColor = .yellow
                text[range].foregroundColor = .red
            }

            // Exaggerating font scaling
            if let range = text.range(of: "Apple") {
                text[range].foregroundColor = .purple
                text[range].font = .system(size: 30)
            }

            return text
        } catch {
            return "Failed to parse markdown payload."
        }
    }

    var body: some View {
        VStack {
            Text(attrText)
        }
        .padding()
    }
}
Easy Way to Display Markdown in SwiftUI using Text

Markdown Limitations and Workarounds

While SwiftUI makes rendering text layouts a breeze, pure inline Markdown formatting explicitly lacks support for block-level elements currently natively bound to web rendering engines. Specifically, SwiftUI's Text does not support:

1. Embedded images - Markdown tags like `![alt](url)` will not automatically pull assets into SwiftUI components. 2. Numbered lists or Bullet points - It won't structurally auto-indent bullet parameters. 3. Tables and Headings - While you can artificially inflate font weights manually, `### Heading` gets parsed as standard plaintext.

If your application heavily relies on CMS delivery with comprehensive Markdown trees, you must utilize highly specialized Swift packages (such as `MarkdownUI`) or leverage raw UITextView abstractions bridging over to UIKit.

Conclusion

Displaying Markdown in SwiftUI is surprisingly simple yet incredibly malleable. It serves brilliantly for simple formatting inside localized assets or fetching small database blurbs. As soon as you outgrow its native simplicity, casting via AttributedString guarantees endless micro-typography adjustments for an aesthetic finish.

GitHub - meshwara/SwiftUI-Markdown-Sample
Contribute to meshwara/SwiftUI-Markdown-Sample development by creating an account on GitHub.