Elementary: A modern and efficient HTML rendering library - inspired by SwiftUI, built for the web

Fact of the matter is that macros are computed at compile time, which offer significant performance improvement/efficiency over existing runtime-based html methods.

Test it yourself using my macro implementation: GitHub - RandomHashTags/swift-htmlkit: Write HTML using Swift Macros

with these testsin release mode:

func test_htmlkit() {
    measureElapsedTime(key: "htmlkit") {
        let _:String = #html([
            #body([
                #h1(["Swift HTML Benchmarks"])
            ])
        ])
    }
}

func test_elementary() {
    measureElapsedTime(key: "elementary") {
        let _:String = html() { body() { h1() { "Swift HTML Benchmarks" } } }.render()
    }
}
func measureElapsedTime(key: String, _ block: () -> Void) {
    let duration:ContinuousClock.Duration = ContinuousClock().measure(block)
    print("measureElapsedTime;" + key + " took=\(duration)")
}

this arbitrary test producesat least on my machine (iMac 2019, Swift 6, macOS 15):

measureElapsedTime;elementary took=0.000146341 seconds
measureElapsedTime;htmlkit took=1.04e-07 seconds

146.341 microseconds vs 104 nanoseconds.

Comparing the two libraries, the macro implementation is 100x more efficient.
*I haven't tested it against Vapor's HTMLKit or Leaf, but they don't use macros, so they're probably slower too

3 Likes

This is really great and a promising approach.

Did you consider producing StaticString? Would that make further optimizations possible?

I haven't considered using StaticString, but I would assume it should offer a slight performance improvement. However it is worth noting that if you want to manipulate the compiled html during runtime you would need to convert it to a string manually. If I do go that approach I'll add some convenience logic to convert it easily.

A limitation right now is the implementation needs to do some string interpolation, which is not allowed for a StaticString, when assigning attributes with an enum value type or interpolated value. I am actively researching & testing workarounds.

Thanks so much for making this @sliemeobn, I also think that full stack Swift server apps with HTMX are a great way to get there. Whilst Swift Macros are powerful and seem like they could further increase performance (eventually though it could be negligible depending on the library usage), having a package with zero dependencies is priceless. Adding a small sponsorship for encouragement to keep going :innocent:

1 Like

@sliemeobn one question, would you have a suggestion on the best way to handle Elementary types inside raw html?
Pseudocode example:

HTMLRaw("""
... existing html

label(.for("name")) { "Enter your name:" }
input(.type(.text), .name("name"), .autofocus)
    .roundedTextbox()

... existing html
""")

I guess I would wrap it in an Elementary element (or a HTMLBuilder function), like so:

div {
   HTMLRaw("...snippet 1...")
   
   label(.for("name")) { "Enter your name:" }
   input(.type(.text), .name("name"), .autofocus)
      .roundedTextbox()
   
   HTMLRaw("...snippet 2...")
}

you could also wrap the elementary bits in an element and call .render() I guess (but that'd be a bit less efficient), like so:

HTMLRaw("""
... existing html

\(MyElement().render())

... existing html
""")
1 Like

Great suggestions, thanks a lot :folded_hands: