SwiftUI List Tutorial — Sections, Swipe Actions & Custom Rows (2026)
Everything you need to ship production-quality List views in SwiftUI — from basic rendering to swipe actions, custom separators, edit mode, search, and async data loading.
SwiftUI List is a scrollable container that renders rows with built-in cell styling, separators, and edit affordances. The minimal form is `List { ForEach(items) { Text($0.title) } }`. Add `.swipeActions`, `.refreshable`, `.searchable`, and `.listStyle()` to power up. For production apps in 2026, target iOS 17+ to use the simpler, faster `List(items, selection:)` initializer that supports multi-selection and works natively with @Observable view models.
Basic SwiftUI List Example
The simplest List takes a closure and a ForEach. SwiftUI automatically lazy-renders each row, so a list of 10,000 items remains buttery smooth. You almost never need UICollectionView in SwiftUI 2026.
struct ContentView: View {
let items = ["Apple", "Banana", "Cherry"]
var body: some View {
List(items, id: \.self) { item in
Text(item)
}
}
}Sections, Swipe Actions, and Custom Rows
Group rows with Section and add swipe actions on either edge. For trailing destructive actions, set role: .destructive — the system renders the red background automatically. Leading edges work well for "mark complete" or "favorite" actions.
- Section { ... } header: { Text("Today") } groups rows with sticky headers
- .swipeActions(edge: .trailing) supports up to 3 buttons before truncation
- allowsFullSwipe: true triggers the first action on full swipe
- .listRowBackground() lets you theme rows per the Swift Kit design system
List {
Section("Today") {
ForEach(tasks) { task in
TaskRow(task: task)
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
Button(role: .destructive) { delete(task) } label: {
Label("Delete", systemImage: "trash")
}
}
.swipeActions(edge: .leading) {
Button { toggleStar(task) } label: {
Label("Star", systemImage: "star.fill")
}
.tint(.yellow)
}
}
}
}
.listStyle(.insetGrouped)Pull-to-Refresh + Search
Both .refreshable and .searchable became first-class in iOS 15 and matured in iOS 17. They compose cleanly — you can have a searchable, refreshable list in 4 lines.
List(filteredItems) { item in
ItemRow(item: item)
}
.searchable(text: $query, prompt: "Search")
.refreshable { await viewModel.reload() }Common Pitfalls
Three issues bite most SwiftUI List users:
- List ignores @State that mutates outside the row — use @Observable view models
- Default .insetGrouped style adds horizontal padding you can't remove without .listStyle(.plain)
- Animations on row updates require .animation(.default, value: items) on the ForEach
- iOS 16 List doesn't lazy-load images — use AsyncImage or Nuke inside rows
Production-ready Lists, pre-styled.
The Swift Kit ships themed List rows, swipe-action primitives, and empty states — wired into the centralized design system.
Frequently Asked Questions
How do I create a SwiftUI List?
How do I add swipe actions to a SwiftUI List?
How do I hide List row separators in SwiftUI?
How do I add pull-to-refresh to a SwiftUI List?
What's the difference between List and ForEach in SwiftUI?
How does The Swift Kit handle Lists?
Keep exploring
Ship a complete SwiftUI app — Lists, paywalls, auth, all wired
The Swift Kit gives you a production-ready SwiftUI boilerplate with every common pattern pre-built. $99 one-time.
Get The Swift Kit — $99One-time purchase · Lifetime updates · 14-day refund