PinnedScrollView is a tiny SwiftUI library that helps you pin any view in a scrollview like a header, without using LazyVStack and Section.


var body: some View {
    ScrollView {
        VStack(spacing: 0) {
            ForEach(0..<20) { _ in
                    .pinned() // 1
                Text("Some content text")
    .pinnedScrollView() // 2
  1. Add .pinned() modifier to the view you want to pin within the scrollview
  2. Add .pinnedScrollView() modifier to the corresponding scrollview

That’s it!

Optional observation

You can also provide a optional closure onReachedTop: (Bool) -> () to the pinned modifier if you want to get a notification when the pinned state changed.

@State private var isHeaderReachedTop = false

// ...
Text("Header" + (isHeaderReachedTop ? " Reached" : ""))
    .pinned { isReachedTop in
        isHeaderReachedTop = isReachedTop


PinnedScrollView requires iOS/iPadOS 14.0.

PinnedScrollView only supports vertical ScrollView and doesn’t support horizontal ScrollView or the List view.


Use Swift Package Manager to add PinnedScrollView to your project.


This package is licensed under the MIT open-source license.


The implementation of PinnedScrollView was inspired by’s course on Sticky Headers for Scroll Views. Unlike the original implementation from, which uses PreferenceKey to coordinate the frames of headers and can lead to performance bottlenecks, PinnedScrollView use onChange to efficiently record the frames of headers.

Additionally, PinnedScrollView use Combine’s debounce and publisher to minimize the re-evaluation count of the headers’ body, aiming to optimize performance.

Special thanks to @auramagi for the algorithm suggestions.


View Github