A complete step-by-step tutorial in Xcode
For many years, embedding web content inside a SwiftUI app required using WKWebView through UIViewRepresentable or NSViewRepresentable. While powerful, that approach was complex, verbose, and not aligned with the declarative philosophy of SwiftUI.
With iOS 26 and macOS 26, Apple introduced a new native view called WebView, which allows you to display web pages, HTML, and interactive content directly inside a SwiftUI view in a simple, declarative, and cross-platform way.
In this tutorial you will learn how to use WebView to:
- Load web pages
- Display local HTML
- Intercept links
- Show loading indicators
- Integrate web navigation into modern SwiftUI apps
All using Xcode and SwiftUI, without UIKit or AppKit wrappers.
1. What is WebView in SwiftUI?
WebView is a view introduced in SwiftUI for iOS 26, macOS 26, and visionOS 3 that wraps the WebKit engine in a fully declarative API.
This means you can now do something as simple as:
WebView(url: URL(string: "https://www.apple.com")!)and get a fully functional web browser inside your app.
It is designed to integrate with:
- SwiftUI navigation (
NavigationStack) - Reactive state (
@State,@Observable) - Security and privacy
- The app sandbox
2. Creating the project in Xcode
Let’s start from scratch.
- Open Xcode 18 or later
- Choose File → New → Project
- Select iOS App
- Set:
- Product Name:
SwiftUIWebViewDemo - Interface:
SwiftUI - Language:
Swift - Minimum OS: iOS 26
- Product Name:
Click Create.
3. Importing WebKitUI
In iOS 26 and macOS 26, WebView lives in a new module:
import SwiftUI
import WebKitUIOpen ContentView.swift and add this import.
4. Displaying a basic web page
Let’s create the first example: showing a website.
struct ContentView: View {
var body: some View {
WebView(url: URL(string: "https://developer.apple.com")!)
.ignoresSafeArea()
}
}Run the app.
You will see Apple Developer’s website displayed inside your app, with scrolling, links, videos, and JavaScript working.
This is a huge leap forward compared to earlier versions.
5. Using NavigationStack with WebView
Let’s integrate it into a real SwiftUI navigation flow.
struct ContentView: View {
var body: some View {
NavigationStack {
WebView(url: URL(string: "https://www.swift.org")!)
.navigationTitle("Swift")
.navigationBarTitleDisplayMode(.inline)
}
}
}Now WebView behaves like any other SwiftUI view.
6. Loading dynamic content
In real apps, you rarely want a fixed URL. Let’s use state:
struct ContentView: View {
@State private var url = URL(string: "https://www.apple.com")!
var body: some View {
VStack {
WebView(url: url)
Button("Go to Swift") {
url = URL(string: "https://www.swift.org")!
}
}
}
}When the URL changes, WebView updates automatically.
This is pure SwiftUI reactivity.
7. Displaying local HTML
You can also display HTML generated by your app.
let html = """
<html>
<head>
<style>
body { font-family: -apple-system; padding: 40px; }
h1 { color: blue; }
</style>
</head>
<body>
<h1>Hello SwiftUI WebView</h1>
<p>This is local HTML content.</p>
</body>
</html>
"""Then:
WebView(html: html)This is perfect for:
- Documentation
- Terms and conditions
- Offline articles
- AI-generated content
8. Showing a loading indicator
WebView exposes a progress binding:
struct ContentView: View {
@State private var progress = 0.0
var body: some View {
VStack {
ProgressView(value: progress)
WebView(
url: URL(string: "https://www.apple.com")!,
progress: $progress
)
}
}
}This lets you display a native loading bar.
9. Intercepting links
You can control what happens when the user taps a link.
WebView(
url: URL(string: "https://www.apple.com")!,
onNavigate: { request in
if request.url?.host == "youtube.com" {
return .cancel
}
return .allow
}
)This is extremely useful for:
- Preventing navigation outside your app
- Opening certain links in Safari
- Implementing deep links
10. JavaScript ↔ SwiftUI communication
One of the most powerful features of WebView is its native JavaScript bridge.
WebView(
html: html,
messageHandler: { message in
print("JS sent:", message)
}
)From JavaScript:
window.swiftui.postMessage("Hello from JS");This enables very powerful hybrid apps.
11. Reader mode, privacy, and sandboxing
WebView inherits all WebKit protections:
- Tracker blocking
- App-isolated cookies
- Process isolation
- Camera and microphone permissions
You can also enable reader mode:
WebView(url: url, readerMode: .enabled)12. Using WebView on macOS 26
The same API works on macOS:
WebView(url: URL(string: "https://www.apple.com")!)
.frame(minWidth: 800, minHeight: 600)This makes it trivial to build cross-platform web-powered apps.
13. Building a mini browser
Let’s build a tiny browser:
struct BrowserView: View {
@State private var urlString = "https://www.apple.com"
var body: some View {
VStack {
TextField("URL", text: $urlString)
.textFieldStyle(.roundedBorder)
.padding()
if let url = URL(string: urlString) {
WebView(url: url)
}
}
}
}You now have a working browser in under 30 lines of code.
14. Real-world use cases
WebView is ideal for:
- Documentation
- Online stores
- Blogs
- Admin dashboards
- Hybrid apps
- OAuth login
- Chatbots
15. Best practices
- Always use HTTPS
- Validate URLs before loading
- Use
onNavigatefor security - Avoid loading untrusted content
- Don’t recreate WebView unnecessarily
16. WebView vs WKWebView
| Old (WKWebView) | New (WebView) |
|---|---|
| UIViewRepresentable | Native SwiftUI |
| Delegates | Closures |
| Verbose code | Declarative |
| UIKit only | iOS + macOS |
Conclusion
The introduction of WebView in iOS 26 and macOS 26 marks a turning point for SwiftUI development. For the first time, Apple provides a clean, modern, declarative way to integrate the web into native apps.
You can now combine:
- Native SwiftUI UI
- Dynamic web content
- JavaScript
- WebKit security
All with very little code.
If you know SwiftUI and HTML, you can now build professional hybrid apps with a truly native user experience.
If you have any questions about this article, please contact me and I will be happy to help you 🙂. You can contact me on my X profile or on my Instagram profile.