NewThe Flutter Kit — Flutter boilerplate$149$69
SwiftUI Tutorial · iOS 17+ · iOS 26

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.

Last updated: 2026-05-16 9 min read By Ahmed Gagan, iOS Engineer
Quick Answer

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.

Min iOS
iOS 15 (basic) · iOS 17 (recommended)
Performance
Lazy by default — only visible rows render
Edit mode
Built-in via EditButton + onDelete/onMove
Swift Kit support
✓ Themed via DesignSystem.swift

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.

BasicList.swift
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
SwipeActionsList.swift
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)

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.

Get The Swift Kit — $99

Frequently Asked Questions

How do I create a SwiftUI List?
Wrap your row content in a List view: List { ForEach(items) { item in Text(item.title) } }. The List view automatically supplies the cell styling, separators, and scrolling behavior. Use List(items, id: \.self) for simpler iteration when items conform to Identifiable.
How do I add swipe actions to a SwiftUI List?
Use the .swipeActions(edge:allowsFullSwipe:content:) modifier on each row. Example: .swipeActions(edge: .trailing) { Button(role: .destructive) { delete(item) } label: { Label("Delete", systemImage: "trash") } }. You can add multiple buttons and configure leading vs trailing edges.
How do I hide List row separators in SwiftUI?
Use .listRowSeparator(.hidden) on each row, or apply .listStyle(.plain) and .listRowSeparator(.hidden) on the List itself to remove all separators. For iOS 16+ you can also tint separators with .listRowSeparatorTint(.accentColor).
How do I add pull-to-refresh to a SwiftUI List?
Attach .refreshable { await reload() } to the List. The system displays the spinner automatically and awaits the async closure. This works on iOS 15+ and integrates with async/await.
What's the difference between List and ForEach in SwiftUI?
List is a scrollable container that provides cell styling, separators, and edit affordances. ForEach generates view content from a collection. You typically use them together: List wraps ForEach to render rows. ForEach alone (inside VStack/ScrollView) gives you custom layouts without List's built-in styling.
How does The Swift Kit handle Lists?
The Swift Kit ships with a pre-styled ListRow design token, swipe action primitives, empty state views, and themed separators wired into the centralized design system. Change one token in DesignSystem.swift and every List across the app updates.

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 — $99

One-time purchase · Lifetime updates · 14-day refund