SwiftWeb is a Swift server and browser runtime for building HTML-first web apps with typed routing, server actions, SwiftWebUI components, and WebAssembly-powered client islands.
Status: developer preview. The browser/WASM path targets Swift 6.3.1 and the current host development server uses a toolchain split documented below.
flowchart LR
A["SwiftWeb.App"] --> B["Scene graph"]
B --> C["Page / PageGroup"]
B --> D["Route policies"]
C --> E["SwiftHTML render graph"]
E --> F["HTML response"]
E --> G["Client WASM island"]
G --> H["SwiftWebUIRuntime"]
G --> L["@Actor resolved services"]
L --> M["@Resolvable distributed actor"]
I["Server actions"] --> J["request context"]
J --> K["@Session"]
| Product | Purpose |
|---|---|
SwiftWeb |
Public app facade, page routing, server actions, and source macros. |
SwiftWebCore |
Route/action/page runtime contracts and server-rendering core used by host adapters. |
SwiftWebBrowserRuntime |
Browser runtime descriptors, WASM asset routes, and HTML runtime injection. |
SwiftWebVapor |
Vapor host adapter for local development workers, Cloud Run, and native/container server builds. |
SwiftWebUI |
SwiftUI-inspired component layer built on top of SwiftHTML. |
SwiftWebUITheme |
Host-neutral theme tokens, style system, root stylesheet, colors, materials, and spacing values. |
SwiftWebUIRuntime |
Browser-side WASM runtime bridge for SwiftWebUI client components. |
SwiftWebActors |
Shared distributed actor runtime support for server/client actor calls. |
SwiftWebDevelopmentHooks |
Worker-side development hooks and typed HMR contracts. |
SwiftWebWasmBuild |
Toolchain resolution, WASM artifact processing, compression, and build profiles. |
SwiftWebPackageGeneration |
Generated server/dev/WASM package materialization and manifest inspection. |
SwiftWebDevServer |
Persistent dev host, watcher, HMR event stream, worker supervision, and rebuild orchestration. |
SwiftWebStoryboardTooling |
Storyboard scaffold/materialization and dev runtime launch. |
SwiftWebDevelopment |
Convenience facade that re-exports development modules. |
sweb |
CLI for new projects, dev server, Storyboard, and production builds. |
SwiftWeb is moving toward a host-neutral runtime core with separate host adapters for
Vapor/container servers and Cloudflare Workers. Vapor remains the native server host
for local development, Cloud Run, and other container targets, while Cloudflare support
should lower the same App/Scene/Page/Worker model into generated TypeScript
entrypoints and Swift/Wasm artifacts.
See Platform Host Architecture for the target
responsibility split, Worker model, @Session API, Cloudflare placement, and Vapor
extraction plan. See Directory And File Structure Design
for the source layout and file placement rules. See Actor Injection Design
for the @Actor client component API over Apple's @Resolvable distributed
actor model.
| Area | Requirement |
|---|---|
| Swift tools version | 6.3 |
| Browser WASM toolchain | Swift 6.3.1 release toolchain |
| Browser WASM SDK | swift-6.3.1-RELEASE_wasm |
| Host development build | Xcode Swift toolchain may be required by the current Vapor 5 HTTP stack |
| Platforms | macOS package development; browser runtime via WASM |
SwiftWeb keeps the host toolchain and browser WASM toolchain separate:
flowchart LR
A["Host/dev server graph"] --> B["Xcode Swift toolchain"]
C["Browser WASM graph"] --> D["Swift 6.3.1 WASM SDK"]
E["Generated packages"] --> A
E --> C
Use the real Swift 6.3.1 toolchain executable for WASM builds, not a swiftly shim:
export SWIFT_WEB_WASM_SWIFT="$(swiftly run +6.3.1 which swift)"
export SWIFT_WEB_WASM_TOOLCHAIN_BIN="$(dirname "$SWIFT_WEB_WASM_SWIFT")"For the current developer preview, depend on the repository branch until the host-side HTTP dependencies are fully versioned for SwiftPM release consumption:
// swift-tools-version: 6.3
import PackageDescription
let package = Package(
name: "MyApp",
platforms: [
.macOS("26.2"),
],
products: [
.library(name: "MyApp", targets: ["MyApp"]),
],
dependencies: [
.package(url: "https://github.com/1amageek/swift-web.git", branch: "main"),
.package(url: "https://github.com/1amageek/swift-html.git", from: "0.7.1"),
],
targets: [
.target(
name: "MyApp",
dependencies: [
.product(name: "SwiftHTML", package: "swift-html"),
.product(name: "SwiftWeb", package: "swift-web"),
.product(name: "SwiftWebUI", package: "swift-web"),
],
swiftSettings: [
.enableUpcomingFeature("ApproachableConcurrency"),
]
),
],
swiftLanguageModes: [.v6]
)Mint can install the sweb executable product directly from the repository. Pin
@main while SwiftWeb is in developer preview; replace it with a release tag when
tagged releases are available.
| Need | Command |
|---|---|
Install and link sweb globally |
mint install 1amageek/swift-web@main sweb |
| Run without linking | mint run 1amageek/swift-web@main sweb <command> |
| Print the installed executable path | mint which 1amageek/swift-web@main sweb |
mint install 1amageek/swift-web@main sweb
sweb --help
sweb new MyApp --output ../MyAppWhen developing SwiftWeb itself, clone the repository and run the CLI from the checkout:
git clone https://github.com/1amageek/swift-web.git
cd swift-web
xcrun swift run sweb --helpGenerate a new app package next to the SwiftWeb checkout:
xcrun swift run sweb new MyApp --output ../MyAppThe generated app has this shape:
MyApp
├─ Package.swift
├─ Sources/MyApp/App.swift
├─ Sources/MyApp/Routes/HomePage.swift
└─ .swiftweb/generated
├─ server
├─ dev
└─ wasm
The app package depends on the local SwiftWeb checkout and released swift-html 0.7.1.
sweb new also materializes the generated launchers, dev packages, server packages,
and WASM packages under .swiftweb/generated. Use sweb prepare only when you want
to refresh generated packages for an existing SwiftWeb app without building or
running it:
cd ../MyApp
sweb prepareFrom the app package directory:
sweb xcodesweb xcode runs the same materialization step as sweb prepare, then opens
.swiftweb/generated/dev in Xcode.
The generated Xcode package includes a <AppName>-dev scheme that starts the same
development runtime used by sweb dev.
From the app package directory:
sweb devOpen:
http://127.0.0.1:3000/
The dev command materializes .swiftweb/generated, builds the app server, starts the
worker process, watches source changes, and sends browser update events.
flowchart LR
A["sweb dev"] --> B["materialize generated package"]
B --> C["build app server"]
C --> D["run worker"]
D --> E["watch files"]
E --> F["browser update"]
Sources/MyApp/App.swift mounts pages:
import SwiftWeb
public struct MyApp: App {
public init() {}
public var body: some Scene {
HomePage()
}
}Sources/MyApp/Routes/HomePage.swift defines the route:
import SwiftHTML
import SwiftWeb
@Page("/")
struct HomePage {
func body() -> some HTML {
div {
h1 { "Hello SwiftWeb" }
p { "Rendered by SwiftHTML and served through SwiftWeb." }
}
}
}Add another route by creating another @Page type and mounting it from App.body:
@Page("/about")
struct AboutPage {
func body() -> some HTML {
main {
h1 { "About" }
p { "This page is rendered on the server." }
}
}
}public var body: some Scene {
HomePage()
AboutPage()
}Use PageGroup when a set of pages shares a path prefix:
public var body: some Scene {
HomePage()
PageGroup("admin") {
AdminDashboardPage()
AdminUsersPage()
}
}Use @Session inside request-time surfaces such as page bodies and server actions.
The wrapped value is WebSession, not a raw host request object.
@Page("/account")
struct AccountPage {
@Session var session
func body() -> some HTML {
if session.isAuthenticated {
AccountView()
} else {
LoginView()
}
}
}| Session API | Meaning |
|---|---|
session.isAuthenticated |
Reads SwiftWeb's authentication marker or stored userID. |
session.userID |
Reads the stored user identifier. |
session["key"] |
Reads or writes string session state. |
session.authenticate(userID:) |
Stores the user identifier and marks the session authenticated. |
session.clearAuthentication() |
Removes SwiftWeb authentication keys. |
session.destroy() |
Invalidates the current persisted session. |
@Session is request-scoped. Do not read it from App.body or Scene.body, because
those build app topology without an active request. Scene-level access control belongs
to route policy descriptors such as the planned .restrict(...) modifier.
public var body: some Scene {
PageGroup("admin") {
AdminDashboardPage()
}
// Target route-policy shape; this is a request-time descriptor.
// .restrict(.authenticated, redirectTo: "/login")
}Import SwiftWebUI when you want the higher-level component layer:
import Foundation
import SwiftHTML
import SwiftWeb
import SwiftWebUI
@Page("/")
struct HomePage {
func body() -> some HTML {
main {
GridSystem {
Pane(span: 12) {
VStack(spacing: .medium) {
Text("Hello SwiftWeb")
.font(.title)
Link("Continue", destination: URL(string: "/about")!)
.buttonStyle(.borderedProminent)
}
}
}
.frame(maxWidth: 720)
}
}
}SwiftWebUI lowers into the SwiftHTML graph. It does not replace SwiftHTML; raw SwiftHTML elements remain available when you need exact HTML control.
Run the SwiftWebUI component Storyboard from the SwiftWeb checkout:
xcrun swift run sweb storyboardOpen:
http://127.0.0.1:3001/storyboard
Storyboard generates an isolated package under .swiftweb/storyboard; it does not edit
your app package.
Run Storyboard with production WASM artifacts and compression sidecars:
xcrun swift run sweb storyboard \
--production \
--runtime standard \
-c releaseThis path builds the generated Storyboard WASM runtime through the production artifact
processor, writes .wasm.gz and .wasm.br, then starts the production app-server.
SwiftWeb supports the standard WASM profile for Storyboard production validation.
Build the generated server package:
sweb buildBuild browser WASM artifacts:
export SWIFT_WEB_WASM_SWIFT="$(swiftly run +6.3.1 which swift)"
export SWIFT_WEB_WASM_TOOLCHAIN_BIN="$(dirname "$SWIFT_WEB_WASM_SWIFT")"
sweb build \
--wasm \
--swift-sdk swift-6.3.1-RELEASE_wasm \
-c releaseSwiftWeb's browser support boundary is the standard Swift WASM SDK:
| Runtime profile | Support | Browser artifact shape |
|---|---|---|
standard |
Supported. Full ClientComponent hydration, client state, browser events, and SwiftWebUI runtime behavior. |
App client source, SwiftHTML, SwiftWebUI, SwiftWebUIRuntime, SwiftWebActors, JavaScriptKit. |
| Embedded Swift WASM | Not supported. Current Swift SDK and runtime dependencies do not provide the required Distributed, Codable, and Foundation capabilities for SwiftWeb's browser graph. |
No public artifact contract. |
Production WASM builds strip debug/producers sections, optionally run wasm-opt -Oz,
write <artifact>.wasm.size.json, and create cached .gz / .br sidecars.
The repository includes a minimal hello world app and a counter app with server actions, page invalidation, and a client-side counter component.
Run the minimal app:
cd Examples/HelloWorld
sweb devOpen:
http://127.0.0.1:3000/
Run the counter app:
cd Examples/CounterApp
sweb devOpen:
http://127.0.0.1:3000/counter
| Command | Purpose |
|---|---|
sweb new <AppName> |
Generate a minimal SwiftWeb app package. |
sweb prepare |
Materialize generated server, dev, and WASM packages for an existing app. |
sweb xcode |
Materialize generated packages and open the dev package in Xcode. |
sweb dev |
Run the development server with generated packages and HMR. |
sweb storyboard |
Run the SwiftWebUI component Storyboard. |
sweb build |
Build the generated production server package. |
sweb build --wasm |
Build browser WASM runtime artifacts and production sidecars. |
Package commands default to the current directory. Run them from the directory that
contains Package.swift, or pass --package-path when targeting a package from
another directory.
sweb dev uses a compact color console for dev status, rebuilds, HMR, and common
errors. It honors NO_COLOR; set SWIFT_WEB_LOG_STYLE=plain to keep the underlying
swift-log output.
Use sweb from inside the application package. The CLI treats the directory that
contains Package.swift as the app root, materializes generated packages under
.swiftweb/generated, and keeps your editable source under Sources/<AppName>.
flowchart LR
A["edit app sources"] --> B["sweb dev"]
B --> C["materialize .swiftweb/generated"]
C --> D["build server package"]
D --> E["run worker"]
E --> F["browser reload/HMR"]
F --> A
| Step | Command | When to use it |
|---|---|---|
| Create an app | sweb new MyApp --output ../MyApp |
Start a new SwiftWeb package and materialize .swiftweb/generated. |
| Refresh generated packages | sweb prepare |
Refresh .swiftweb/generated for an existing app without starting the server. |
| Open the generated dev package | sweb xcode |
Inspect or debug the generated development package in Xcode. |
| Run the dev loop | sweb dev |
Start the server, watcher, rebuild loop, and browser updates. |
| Build the server package | sweb build |
Validate the generated production server package. |
| Build browser WASM | sweb build --wasm --swift-sdk swift-6.3.1-RELEASE_wasm -c release |
Produce optimized browser runtime artifacts and compression sidecars. |
The normal loop is:
cd MyApp
sweb devThen edit route, component, and client island source files in Sources/MyApp.
Generated packages are implementation output; inspect them when debugging, but keep
source changes in the app package or in SwiftWeb itself.
For Xcode-based debugging, run:
cd MyApp
sweb xcodeThis opens .swiftweb/generated/dev, whose <AppName>-dev scheme runs the same
development runtime as sweb dev.
Before release-oriented checks, run:
cd MyApp
sweb build
export SWIFT_WEB_WASM_SWIFT="$(swiftly run +6.3.1 which swift)"
export SWIFT_WEB_WASM_TOOLCHAIN_BIN="$(dirname "$SWIFT_WEB_WASM_SWIFT")"
sweb build --wasm --swift-sdk swift-6.3.1-RELEASE_wasm -c releaseSwiftWeb browser runtime packages copy runtime-only sources into generated WASM packages:
| Runtime source | Browser WASM behavior |
|---|---|
| SwiftHTML | Runtime source copy; preview/doc sources are excluded. |
| SwiftWebUI | Runtime source copy for client components. |
| SwiftWebUIRuntime | JavaScriptKit-backed browser adapter. |
| JavaScriptKit | Runtime-only copy; BridgeJS macros are not included by default. |
| SwiftSyntax | Not included in generated browser runtime packages. |
sweb build --wasm uses the standard Swift WASM compiler profile. SwiftWeb does
not expose Embedded Swift WASM as a supported runtime profile. Embedded Swift can
be revisited only if the browser runtime can be expressed without Distributed,
Codable, Foundation, or profile-specific source families.
The intended production split is:
flowchart LR
A["Production page"] --> B{"Browser behavior"}
B -->|"client state / client events"| C["standard WASM runtime"]
B -->|"server-rendered / server actions"| D["HTML + HTTP actions"]
C --> E[".wasm + .gz + .br sidecars"]
D --> F["no separate Embedded Swift profile"]
Historical Embedded Swift measurements are research notes only and are not part of the public support contract.
ClientComponent values are lowered into WASM bundles by contract. The default
contract joins the eager main bundle:
public struct ClientSummary: ClientComponent, Sendable {
@State private var count = 0
public init() {}
public var body: some HTML {
Button("Count \(count)") {
count += 1
}
}
}Use loadPolicy to decide when the browser loads a client island, and bundle
to decide which logical bundle groups related islands:
public struct ClientChart: ClientComponent, Sendable {
public static let loadPolicy: LoadPolicy = .visible
public static let bundle: BundlePolicy = .named("analytics")
public init() {}
public var body: some HTML {
GroupBox {
Heading("Chart")
Text("Loaded when the chart approaches the viewport.").foregroundStyle(.secondary)
}
}
}Page-local overrides can be written where the client island is used:
ClientInspector()
.loadPolicy(.manual)
.bundle(.shared("tools"))| Policy | Purpose |
|---|---|
.main |
Keep small or common eager components in the initial runtime. |
.component |
Split one large isolated client island. |
.named("analytics") |
Group page or app features under a readable bundle name. |
.shared("workspace") |
Group reusable client components that should share one bundle. |
| Load policy | Browser behavior |
|---|---|
.eager |
Load and instantiate during initial runtime startup. |
.visible |
Load when the island approaches the viewport. |
.interaction |
Load on hover, focus, touch, press, or click intent. |
.idle |
Load during a browser idle window. |
.manual |
Wait for an explicit runtime request. |
Modifiers must be attached to the outermost ClientComponent island. Nested
client components share the outer island's bundle contract. The default WASM
split strategy coalesces non-eager bundles by load policy, while
SWIFTWEB_WASM_SPLIT_BUILD_STRATEGY=resolved builds one artifact per resolved
logical bundle. See ClientBundleLoadingDesign
for the full design.
Production WASM builds generate .wasm.gz and .wasm.br sidecars. Brotli defaults to
quality 11 for production transfer size, and sidecars are cached by the post-processed
WASM content hash so unchanged artifacts are not recompressed.
| Topic | Current contract |
|---|---|
| Swift version | Keep Package.swift at // swift-tools-version: 6.3. |
swift-html |
Released dependency for generated apps: 0.7.1. Storyboard development can still use a local sibling checkout when present. |
| Host compatibility | Current Vapor 5 HTTP stack may require an Xcode Swift toolchain for host/dev builds. |
| WASM compatibility | Browser runtime remains pinned to Swift 6.3.1 and the matching WASM SDK. |
| Versioned SwiftPM release | Blocked until branch/revision host dependencies are replaced or explicitly scoped out. |
SwiftWeb is released under the MIT License. See LICENSE.