Text composition in SwiftUI can often be cumbersome, especially when there's logic affecting its format and content.
TextBuilder leverages the power of Swift Macros to solve this problem. The @TextBuilder macro transforms functions into builder-style closures, making text composition intuitive and readable.
Add TextBuilder to your Swift Package Manager dependencies:
.package(url: "https://github.com/davdroman/swiftui-text-builder", from: "4.0.0"),Then, add the dependency to your desired target:
.product(name: "TextBuilder", package: "swiftui-text-builder"),Apply @TextBuilder to functions that return Text. The macro will transform the function body into a builder-style closure that concatenates text segments.
@TextBuilder
func loremIpsum() -> Text {
    Text("Lorem").underline().foregroundColor(.blue)
    Text("ipsum dolor")
    Text("sit").bold()
    Text("amet, consectetur")
}This creates a concatenated Text without any separators between segments.
You can specify a separator to be inserted between text segments:
@TextBuilder(separator: " ")
func spacedText() -> Text {
    Text("Lorem").underline().foregroundColor(.blue)
    Text("ipsum dolor")
    Text("sit").bold()
    Text("amet, consectetur")
}For multiline text:
@TextBuilder(separator: "\n")
func multilineText() -> Text {
    Text("Lorem").underline().foregroundColor(.blue)
    Text("ipsum dolor")
    Text("sit").bold()
    Text("amet, consectetur")
}TextBuilder accepts String types directly and provides a convenient .text computed property:
@TextBuilder(separator: " ")
func mixedText() -> Text {
    "Hello"  // String literal becomes verbatim Text
    "world".text.bold()  // Use .text for chaining modifiers
    String(2025)  // Any StringProtocol works
}TextBuilder supports Swift's control flow statements:
@TextBuilder(separator: " ")
func conditionalText(showDetails: Bool) -> Text {
    "Hello"
    if showDetails {
        "with details"
    } else {
        "basic"
    }
    if let name = userName {
        name.text.italic()
    }
    for i in 1...3 {
        String(i)
    }
}If you prefer not to use macros, you can use the underlying Text initializer directly:
var body: some View {
    Text(separator: " 👏 ") {
        "Lorem".text.underline().foregroundColor(.blue)
        "ipsum dolor"
        "sit".text.bold()
        "amet, consectetur"
    }
}This is useful if you simply want to insert some rich text into a view body without defining a separate function.
The @TextBuilder macro currently cannot be applied to computed properties due to Swift limitations. Use functions instead.
See Swift Issue #75715 for updates on computed property support.