# ScrollView and Lazy stacks
## Intent
Use `ScrollView` with `LazyVStack`, `LazyHStack`, or `LazyVGrid` when you need custom layout, mixed content, or horizontal/ grid-based scrolling.
## Core patterns
- Prefer `ScrollView` + `LazyVStack` for chat-like or custom feed layouts.
- Use `ScrollView(.horizontal)` + `LazyHStack` for chips, tags, avatars, and media strips.
- Use `LazyVGrid` for icon/media grids; prefer adaptive columns when possible.
- Use `ScrollViewReader` for scroll-to-top/bottom and anchor-based jumps.
- Use `safeAreaInset(edge:)` for input bars that should stick above the keyboard.
## Example: vertical custom feed
```swift
@MainActor
struct ConversationView: View {
private enum Constants { static let bottomAnchor = "bottom" }
@State private var scrollProxy: ScrollViewProxy?
var body: some View {
ScrollViewReader { proxy in
ScrollView {
LazyVStack {
ForEach(messages) { message in
MessageRow(message: message)
.id(message.id)
}
Color.clear.frame(height: 1).id(Constants.bottomAnchor)
}
.padding(.horizontal, .layoutPadding)
}
.safeAreaInset(edge: .bottom) {
MessageInputBar()
}
.onAppear {
scrollProxy = proxy
withAnimation {
proxy.scrollTo(Constants.bottomAnchor, anchor: .bottom)
}
}
}
}
}
```
## Example: horizontal chips
```swift
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(spacing: 8) {
ForEach(chips) { chip in
ChipView(chip: chip)
}
}
}
```
## Example: adaptive grid
```swift
let columns = [GridItem(.adaptive(minimum: 120))]
ScrollView {
LazyVGrid(columns: columns, spacing: 8) {
ForEach(items) { item in
GridItemView(item: item)
}
}
.padding(8)
}
```
## Design choices to keep
- Use `Lazy*` stacks when item counts are large or unknown.
- Use non-lazy stacks for small, fixed-size content to avoid lazy overhead.
- Keep IDs stable when using `ScrollViewReader`.
- Prefer explicit animations (`withAnimation`) when scrolling to an ID.
## Pitfalls
- Avoid nesting scroll views of the same axis; it causes gesture conflicts.
- Don’t combine `List` and `ScrollView` in the same hierarchy without a clear reason.
- Overuse of `LazyVStack` for tiny content can add unnecessary complexity.