SwiftUI.cc
Snippets
NavigationTabsIntermediate

SwiftUI TabView Page Style and Index Indicators

tabViewStyle(.page) turns TabView into a swipeable pager — onboarding carousels and image galleries — with index dots controlled by indexViewStyle and display mode.

3 min readUpdated 2026-06
Onboarding pager with always-visible dots
import SwiftUI

struct Onboarding: View {
    @State private var page = 0

    var body: some View {
        TabView(selection: $page) {
            ForEach(0..<3, id: \.self) { index in
                VStack(spacing: 12) {
                    Image(systemName: ["sparkles", "bolt", "checkmark.seal"][index])
                        .font(.system(size: 56))
                    Text("Feature \(index + 1)")
                        .font(.title2.bold())
                }
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .tag(index)
            }
        }
        .tabViewStyle(.page(indexDisplayMode: .always))
        .indexViewStyle(.page(backgroundDisplayMode: .always))
        .background(.indigo.opacity(0.15))
    }
}

TabView page style preview

One modifier, different widget

tabViewStyle(.page) keeps TabView's API — children, tags, selection — but swaps the bar for horizontal swiping with dot indicators. Onboarding flows get the standard UIKit page-control look in a line.

Indicator control

Two independent dials:

.tabViewStyle(.page(indexDisplayMode: .always))          // dots: always/automatic/never
.indexViewStyle(.page(backgroundDisplayMode: .always))   // capsule behind dots

Set indexDisplayMode: .never and bind selection to draw your own indicator — a progress bar, numbered chip, or animated dots.

Pager + buttons

The selection binding makes Next buttons one-liners: withAnimation { page += 1 }. Combine with a conditional Get Started button on the final page.

When ScrollView is the better pager

For dozens of pages or data-driven feeds, ScrollView(.horizontal) with scrollTargetBehavior(.paging) builds pages lazily and integrates scrollPosition tracking. Page-style TabView builds its children eagerly, which is fine for a handful of onboarding screens but heavy for galleries of remote images.

Common mistakes

  • Invisible white dots on white pages — set the background display mode.
  • Forgetting tags, so the Next button cannot drive the pager.
  • Using page style for long feeds and paying eager-construction cost.

Related reference