Introducing ElementaryUI: Swift in the Browser ๐Ÿš€

Hello there, lovely Swift community!

A web framework? In Swift? With WebAssembly?

Listen, I know, creating a new web framework can feel like a bit of a fool's errand.

But what are you gonna do? Use JavaScript for the rest of your life? Write apps in Rust? Wait for the web to go away?

So, instead of listing all the reasons in our heads why this may fail, let's work together and say:

Yes: ElementaryUI !

  • :globe_with_meridians: Swift in the Browser - Run declarative Swift applications natively in the browser with WebAssembly
  • :artist_palette: SwiftUI-inspired APIs - Familiar and intuitive syntax for building web UIs
  • :high_voltage: Built-in Reactivity - Automatic state management with @State, @Environment, and @Reactive
  • :package: Tiny Binaries - Full Embedded Swift support means kB-sized wasm bundles instead of MB
  • :sparkles: Magical Animations - Powerful CSS-based animation system including automatic FLIP transitions
  • :rocket: Vite-Powered Development - Fast dev server with hot reload for rapid iteration

What is it?

ElementaryUI is a Swift frontend framework that runs in the browser. It lets you build web applications in Swift instead of JavaScript, bringing Swift's safety and expressiveness to frontend development.

It takes inspiration from SwiftUI to provide an idiomatic way to describe state-driven user interfaces while firmly staying a web framework.

import ElementaryUI

@View
struct Counter {
    @State var count = 0

    var body: some HTML {
        div {
            p { "Count: \(count)" }
            button { "Increment" }
                .onClick { count += 1 }
        }
    }
}

Early Days, Big Mission

I'll be honest, this is an ambitious open-source project in its very early days. The foundation is starting to settle, but there is still a lot to build, invent, and improve.

However, the mission is clear: Make Swift a first-class option for web projects, and keep the project healthy for the long run.

If you are building a production-critical banking dashboard today, maybe wait a bit. But if you are a Swift enthusiast looking to build a personal project, an internal tool, or a web component without touching JavaScript, now is the perfect time to dive in.

Get Involved

There's plenty of work ahead to make Swift a truly first-class web technology:

  • Web components support
  • Client-side routing
  • Server-side rendering
  • And much more...

Whether you want to contribute code, try it out, give feedback, or sponsor development, we'd love to have you!

Let's make Swift in the browser a reality together! :tada:

62 Likes

Impressive project. The @HTMLBuilder syntax looks elegant.

I quickly ran through the guide on your website and some points caught my attention.

  • I am not very deep into Swift for Embedded, but what exactly did not work with @Observable that made you re-do it with your @Reactive macro? Is there some obvious blocker why Observation is not available for Embedded? Is this something that is on the roadmap for Embedded (maybe more a question to the people working on it)? Would be quite cool to share one @Observable model for a SwiftUI project and a Web project.
  • What does the @View macro do? From the guide it seems like it conforms your type to the View protocol, but I guess it does more than that.
  • Out of interest, does HTML(+JS) have concepts like onAppear and onDisappear or is this something that you bridged over to HTML?
4 Likes
4 Likes

Aside from the trouble with @Observable in Embedded Swift (see rauhul's link above) we could absolutely offer support for it in "normal Swift" mode. I think adding a package trait to enable Observation support should be quite possible. Maybe even based on the new Observations API.

it does exactly this ; )

basically, it generates code that hooks up your @Environment and @State properties, and a bit of equality-checking code. Since in embedded that sort of type metadata is not present at runtime, we need compile-time tricks. (should be faster too)

These handlers technically have nothing to do with the DOM or HTML elements, but rather with ElementaryUI's view hierarchy. The closest thing in HTML-world is probably connected/disconnected for custom elements, or intersection observer stuff.

2 Likes

Been watching this project for a while now, super cool to see it get to this point, congrats!

One of the wasm drawbacks Iโ€™ve heard touted is not having direct access to the DOM, meaning you have to go through JavaScript to render things. I was never clear on whether this drawback was about convenience or performance though. Have you noticed any performance issues in your testing?

1 Like

There's a misunderstanding here, Wasm is an instruction set. It can't have access to DOM in the same way that ARM, X86, or RISC-V instruction sets don't have direct access to DOM or any application-level abstractions.

You're probably referring to glue code that bridges from a Wasm module into DOM. Some of the glue code is indeed written in JavaScript to load a Wasm module in the first place. But actual rendering calls are located in the Wasm module.

These calls need access to JavaScript objects, but there are different approaches to passing them around. Some of them allow representing JavaScript objects in Wasm directly as opaque references, some pass around an index in a side table that holds those references. JavaScriptKit (used by ElementaryUI) currently uses the latter, while opaque references are only available in recent browsers and could be adopted in the future, which I do expect to bring some performance and binary size improvements.

4 Likes

The (frankly quite limited and sporadic) profiling and performance testing I have done so far always left me quite confident.

As in, most "reconciler runs" after state changes + DOM commits are done within a few ms tops. My guess is that - even today without any particular efforts towards optimizing - there is enough performance headroom for most everyday use cases.

Imho accessing DOM APIs through the JS runtime is much less a problem as people think - string handling (juggling bits for utf8 vs utf16) is probably the only "real" issue in this area.

but, as @Max_Desiatov also mentioned above, there is still plenty of fat to trim if needed. I am also eager to switch parts over to JavaScriptKit's BridgeJS and see what that does, but I need to get my head around the whole pipeline first.

My suspicion (not confirmed by measurements yet) is that it isn't re-encoding that can undermine performance (encoding can be fast enough in modern JS engines), but repeated allocations and copying string data between JS garbage-collected buffers and Wasm-owned linear memory. If you stick to JSString references, don't unbox it into String, and only call methods native to it, you should be fine. You'll avoid re-encoding that way too. With potential future Wasm reference types adoption, manipulating strings in this manner can be in theory as fast as it already is for JS.

There's also new JS String Builtins proposal now available in all major browsers that would be great to explore.

3 Likes

If you want to explore how Swift + Tailwind work for client-side web apps, I just published a Tailwind Starter Template. Tailwind IntelliSense included!

Elementary Demo Split Screen

7 Likes

Cool work, congrats!

I will say though the name is confusing because googling for it youโ€™d get โ€œElementary UI. A collection of reusable React UI components built with styled-components.โ€ and a few other ones to the point where this library doesnโ€™t even show up (at allโ€ฆ?). Have you considered unique-ifying it somehow? The reason Iโ€™m asking is somewhat meta, Iโ€™m always concerned when we have to search for things and add โ€œthe swift oneโ€ rather just the thing happening to be in swift. A lot of Swift Project libraries are guilty of that by prefixing name with swift- but open source initiatives donโ€™t really have to. Just random idea thought Iโ€™d share

thanks Konrad!

about the name: I hear you, it was honestly a very hard decision. I have spent way longer than I care to admit looking for snappy alternatives (I almost named it "Swilt"... yeah...).

Ultimately, I decided to stick to the elementary "branding" since I didn't think any other name was clearly better, and elementary had already collected a bit of a following. Aside from "elementary OS" (which does not really overlap, very little potential for confusion) I did not really find anything named "ElementaryUI" that was particularly active or big.

The rename to ElementaryUI just happened, so I'd say let's give it some time for search ranks to catch up - my hope is that there will be enough community buzz that this problem will eventually fix itself.

but yeah, I am sure I am not the only one to always google like
"something something swift -taylor" (and in my case often -taylor -school :D)

having said all that: If somebody has a killer name for it that they think would justify all the rebranding trouble -> please do tell.

6 Likes