swiftui · essay · performance · identity

SwiftUI Lazy Stacks and Scrolling

Published October 12, 2026 · 3 min read · advanced

The confusion

Lazy stacks sound like a performance feature, so it is easy to treat them as a blanket fix. Developers switch to LazyVStack, expect everything to get faster, and then discover the scrolling problem moved instead of disappearing.

That happens because laziness helps with creation, not with every cost around a scrolling list.

What SwiftUI is actually doing

A lazy container delays work until a row is needed. That reduces upfront construction, which is useful for long lists and large feeds.

But once the row is on screen, the usual SwiftUI rules still apply:

  • the row should stay cheap
  • identity should stay stable
  • expensive work should not hide in body
  • row state should belong to the right owner

If any of those are wrong, a lazy stack only exposes the problem later.

The mental model

Lazy containers reduce up-front work, but they do not remove row complexity.

So the question is not “Should I use a lazy stack?” The real question is “What should a row do when it appears, and what should it never do?”

That is why lazy scrolling and identity posts belong together. The layout choice only works when the identity and side-effect model stays clean.

A small proof

import SwiftUI

struct ItemList: View {
    let items: [String]

    var body: some View {
        ScrollView {
            LazyVStack(alignment: .leading, spacing: 12) {
                ForEach(items, id: \.self) { item in
                    Text(item)
                        .padding(.vertical, 8)
                }
            }
        }
    }
}

This version is useful because it keeps the row lightweight and the identity explicit. The container can stay lazy without forcing you to hide work inside each row.

Why this matters in real apps

Scrolling performance is usually a combination problem. If rows do too much work, if IDs are unstable, or if state lives in the wrong place, the list will feel glitchy regardless of container choice.

That is why “make it lazy” is not a full answer. It is only one part of the answer.

Where this model breaks down

Not every screen needs a lazy container. If the content is short or the layout is simple, the extra indirection is not worth it.

And if your rows are expensive, laziness helps only after you fix the row work itself.

One sentence to remember

Lazy stacks save startup work, not bad row design.

Next steps

These follow-ups keep the same identity and performance model in view: