SwiftUI Lazy Stacks and Scrolling
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: