
Displaying web content in SwiftUI
Learn how to display web content in a SwiftUI with new WebView.
At WWDC 2025, Apple unveiled a new SwiftUI native view for displaying web content directly in SwiftUI, without the need to bridge with UIKit, the WebView
, available for iOS 26, macOS 26, and more.
Part of the WebKit framework, SwiftUI's view WebView
offers two distinct approaches depending on your needs:
- Simple URL-based loading
- Advanced control with
WebPage
When working with url-based loading, for basic web content display, you can use the initializer WebView(url:)
:
import SwiftUI
import WebKit
struct WebViewUrlView: View {
@State private var toggle = false
@State private var url: URL? = URL(string: "https://www.createwithswift.com")
var body: some View {
if let url = url {
WebView(url: url)
}
}
}
This lightweight approach is perfect when you need to display in-app web content quickly, without requiring complex interaction.
The URL-based WebView
automatically handles loading, provides basic navigation gestures, and integrates seamlessly with SwiftUI's state management. You can dynamically change the URL by updating the state variable, and the WebView
will automatically reload the new content.
Another approach is to use the WebView(_:)
initializer, wich takes a WebPage
object are a parameter, that represents and controls your web content.
struct WebPageView: View {
@State var page = WebPage()
var body: some View {
VStack {
WebView(page)
.onAppear {
page.load(URL(string: "https://www.createwithswift.com")!)
}
}
}
}
The WebPage
class in SwiftUI lets you load, control, and interact with web content in a declarative and Swift concurrency-friendly way. It works seamlessly with WebView
to support tasks that range from custom loading to JavaScript execution, offering powerful customization and navigation control features.
WebPage
has a full set of observable properties and functions you can use to make reacting to changes incredibly simple, especially with SwiftUI.
@State var page = WebPage()
// Access loading state
if page.isLoading {
ProgressView()
}
// Display page title
Text(page.title ?? "Loading...")
// React to URL changes
.onChange(of: page.url) { newURL in
// Handle navigation
}
WebView
allows us to load web pages, observe them, customize user agent, etc. This native integration solves real problems developers face daily, such as memory management (no more worrying about retain cycles between your SwiftUI view and the web view delegates) and state synchronization (WebPage
's observable properties automatically trigger UI updates when the web content changes).
With it's simplified API, complex operations like JavaScript injection become single function calls.
// Before: Complex delegate setup and callback handling
// Now: Simple async/await
let result = try await page.callJavaScript("document.title")
And since it's a SwiftUI view, modifiers can be used to configure behavior, like you would do with any other SwiftUI views and components:
WebView(url: url)
.webViewBackForwardNavigationGestures(.disabled)
.webViewMagnificationGestures(.enabled)
.webViewLinkPreviews(.disabled)
Examples of available modifiers are:
webViewBackForwardNavigationGestures(_:)
to enable navigation with the swipe gesturewebViewMagnificationGestures(_:)
to enable zoom with the pinch to zoom gesturewebViewLinkPreviews(_:)
to enable the press gesture to show a preview of the destination on linkswebViewTextSelection(_:)
to enable interaction with the text content within theWebView
webViewElementFullscreenBehavior(_:)
to enable the WebView to display full screen content
Whether you’re constructing a straightforward web viewer or a complex web-integrated application, SwiftUI’s newly introduced WebView
equips you with the necessary tools without the inherent complexities. This isn’t merely an enhancement; it’s the solution we’ve been waiting for since SwiftUI’s inception.
For SwiftUI developers, this means less time fighting with UIKit bridges and more time building great user experiences. The web is no longer a second-class citizen in SwiftUI apps, it's a first-class, native component that works seamlessly with the rest of your declarative UI.