SwiftUI TabView: Tabs, Paging & the iOS 26 Tab API
Build a bottom tab bar, a swipeable paged carousel, and a badge-driven navigation shell — plus the new iOS 26 Tab and TabSection API with a floating Liquid Glass tab bar.
SwiftUI's TabView creates a tab-based interface: place child views inside and mark each with .tabItem { Label(...) }. Bind a selection to switch tabs programmatically with TabView(selection:). Switch to a swipeable carousel with .tabViewStyle(.page), and add unread counts with .badge(_:). On iOS 26 the new Tab and TabSection value types replace .tabItem and power a floating Liquid Glass tab bar.
The classic TabView
In its simplest form, a TabView takes a set of child views and a .tabItem on each. SwiftUI renders a bottom tab bar on iPhone and adapts the layout on iPad and Mac. Each tab keeps its own view state as you switch, so scroll position and form input persist. Keep tab items to a Label with a title and SF Symbol; if you exceed five tabs, iOS collapses the overflow into a More tab automatically.
TabView {
HomeView()
.tabItem { Label("Home", systemImage: "house") }
SearchView()
.tabItem { Label("Search", systemImage: "magnifyingglass") }
ProfileView()
.tabItem { Label("Profile", systemImage: "person.crop.circle") }
}Programmatic selection binding
To control which tab is active from code — for example, to jump to a tab after a push notification — bind a selection value. Give each tab a .tag matching the type of the selection state, then mutate that state to switch tabs. Using an enum for the tag makes the code self-documenting and prevents typos that a raw Int would allow. This binding is also how you implement scroll-to-top on a second tap of the active tab.
enum AppTab: Hashable { case home, search, profile }
struct RootView: View {
@State private var selection: AppTab = .home
var body: some View {
TabView(selection: $selection) {
HomeView()
.tabItem { Label("Home", systemImage: "house") }
.tag(AppTab.home)
SearchView()
.tabItem { Label("Search", systemImage: "magnifyingglass") }
.tag(AppTab.search)
}
}
}Paged carousels with .tabViewStyle(.page)
The same TabView container becomes a swipeable, full-screen carousel when you apply .tabViewStyle(.page). This is the idiomatic way to build onboarding walkthroughs and image galleries without a third-party library. Add .indexViewStyle to control the page dots, and bind a selection if you want a Next button to advance pages programmatically. Wrap page content in a GeometryReader only if you need precise sizing; usually the container fills the screen for you.
struct Onboarding: View {
@State private var page = 0
private let slides = ["Welcome", "Sync", "Go Pro"]
var body: some View {
TabView(selection: $page) {
ForEach(slides.indices, id: \.self) { i in
Text(slides[i])
.font(.largeTitle)
.tag(i)
}
}
.tabViewStyle(.page)
.indexViewStyle(.page(backgroundDisplayMode: .always))
}
}Badges for unread counts
Attach a badge to any tab with the .badge modifier to surface unread counts or a status dot. Pass an Int for a numeric badge, a String for text, or zero to hide it. Badges are perfect for a messages tab or a notifications center. Because the modifier lives on the tab content, you can drive the value from observable state and it updates live as new items arrive.
InboxView()
.tabItem { Label("Inbox", systemImage: "envelope") }
.badge(unreadCount) // Int; 0 hides the badge
SettingsView()
.tabItem { Label("Settings", systemImage: "gear") }
.badge("New") // String badgeThe iOS 26 Tab API and Liquid Glass tab bar
iOS 26 introduces a value-type Tab API that replaces the .tabItem modifier. You declare Tab(title, systemImage:, value:) items directly, group related tabs with TabSection, and mark a search tab with role: .search so it docks correctly in the new floating Liquid Glass tab bar. The bar floats above content and adopts the glass material automatically. Keep an availability fork so devices on iOS 16–25 continue to use the classic .tabItem syntax.
@available(iOS 26, *)
struct ModernRoot: View {
@State private var selection: AppTab = .home
var body: some View {
TabView(selection: $selection) {
Tab("Home", systemImage: "house", value: .home) {
HomeView()
}
Tab("Search", systemImage: "magnifyingglass", value: .search, role: .search) {
SearchView()
}
TabSection("Library") {
Tab("Saved", systemImage: "bookmark", value: .profile) {
SavedView()
}
}
}
}
}A tab shell that just works
The Swift Kit gives you a TabView root with enum-tagged tabs, live badges, and deep-link routing already wired, so you start above the boilerplate.
Frequently Asked Questions
How do I make a swipeable paged carousel with TabView in SwiftUI?
How do I switch the selected tab programmatically in SwiftUI TabView?
How do I add a badge to a tab in SwiftUI TabView?
What is the iOS 26 Tab API in SwiftUI TabView?
Why does my SwiftUI TabView show a More tab?
Keep exploring
Skip the navigation plumbing
The Swift Kit is a $99 one-time SwiftUI boilerplate with tab navigation, onboarding, auth, and paywalls ready to ship — lifetime updates, 14-day refund.
Get The Swift Kit — $99One-time purchase · Lifetime updates · 14-day refund