A tiny SwiftUI-like UI framework for WebAssembly, built on SwiftWasm and JavaScriptKit.
Write views with the API shape you already know from SwiftUI, and render them to the browser DOM:
import SwiftWasmUI
@main
struct CounterApp: App {
var body: some View {
CounterView()
}
}
struct CounterView: View {
@State var count = 0
var body: some View {
VStack(spacing: 16) {
Text("Count: \(count)")
Button("Increment") {
count += 1
}
}
.padding()
}
}SwiftUI is closed source and tied to Apple platforms, so bringing its API to the web means reimplementing the UI layer from scratch. Tokamak pioneered this but has been mostly inactive since 2023. SwiftWasmUI is a minimal, understandable take on the same idea — small enough to read in one sitting, modern enough to build with current SwiftWasm toolchains.
Viewprotocol +@ViewBuilder(includingif/elsebranches)@State/Bindingwith automatic re-renderingText,Button,TextField,Toggle,List,ForEach,ColorImage(systemName:)with SF Symbols-style names, rendered via Framework7 Icons (MIT) — SF Symbols itself is licensed for Apple platforms only, so the glyphs are not Apple'sVStack/HStack/Spacerimplemented with flexbox —Spacer()just works- Modifiers:
.padding(),.font(),.foregroundColor(),.background(),.frame(),.cornerRadius() - iOS-flavored default styling (
Listlooks likeUITableView)
- Rendering: state changes rebuild the DOM subtree from scratch — no diffing, no virtual DOM. Simple and predictable; fine for small apps.
- State: view structs are recreated on every render. Before calling
body, the renderer walks the struct withMirror, finds@Stateproperties in declaration order, and reconnects them to_StateStoreinstances that live in a persistent mount tree. Stores are shared by reference, so handlers from a previous render can still write safely. - Events: DOM handlers are
JSClosures that mutate state and trigger a re-render.
- Swift toolchain with a SwiftWasm SDK installed
(
swift sdk listshould show e.g.swift-6.2.4-RELEASE_wasm)
./scripts/build-web.sh
python3 -m http.server 8000 --directory Web
# open http://localhost:8000The demo is an iOS-look tab app (counter + todo list) defined in
Sources/SwiftWasmUIDemo/DemoApp.swift.
This is a research project, not a production framework.
- Full re-render on every state change (no reconciliation)
ForEachkeys by position, not byidTextFieldupdates state silently while typing and commits on Enter (a full re-render would recreate the input and drop focus)- No animations, gestures, environment values, or navigation — yet
See ROADMAP.md for what's coming next, and docs/api-comparison.md for side-by-side screenshots of every implemented API rendered by SwiftWasmUI (browser) and SwiftUI (iPhone) from identical code.
PRs welcome — see CONTRIBUTING.md.
MIT — see LICENSE.