swift-web

main

Swift server and browser runtime for HTML-first web apps
1amageek/swift-web

SwiftWeb

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"]
Loading

Packages

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.

Architecture Direction

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.

Requirements

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
Loading

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")"

Installation

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]
)

Install The CLI With Mint

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 ../MyApp

Usage

1. Run The CLI From This Repository

When 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 --help

2. Create An App

Generate a new app package next to the SwiftWeb checkout:

xcrun swift run sweb new MyApp --output ../MyApp

The 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 prepare

3. Open The Xcode Package

From the app package directory:

sweb xcode

sweb 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.

4. Run The Development Server

From the app package directory:

sweb dev

Open:

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"]
Loading

5. Add Routes

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()
    }
}

6. Read The Request Session

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")
}

7. Use SwiftWebUI Components

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.

8. Inspect Components With Storyboard

Run the SwiftWebUI component Storyboard from the SwiftWeb checkout:

xcrun swift run sweb storyboard

Open:

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 release

This 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.

9. Build For Production

Build the generated server package:

sweb build

Build 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 release

SwiftWeb'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.

10. Try The Examples

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 dev

Open:

http://127.0.0.1:3000/

Run the counter app:

cd Examples/CounterApp
sweb dev

Open:

http://127.0.0.1:3000/counter

CLI

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.

Development Workflow With sweb

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
Loading
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 dev

Then 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 xcode

This 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 release

Browser Runtime

SwiftWeb 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"]
Loading

Historical Embedded Swift measurements are research notes only and are not part of the public support contract.

Client Bundles

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.

Development Notes

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.

License

SwiftWeb is released under the MIT License. See LICENSE.

Description

  • Swift Tools
View More Packages from this Author

Dependencies

  • None
Last updated: Sun Jun 28 2026 11:30:48 GMT-0900 (Hawaii-Aleutian Daylight Time)