Skip to content

Container Views: Sections (SwiftUI)

A SwiftUI guide for coding agents. Also covers foreach sections, group sections, sectionconfiguration, section header footer container, recompose sections custom container, sectioned carousel.

ForEach(sections:) and Group(sections:) decompose a @ViewBuilder content view into its Sections, so a custom container can lay out each header, content, and footer its own way. This continues the subview decomposition APIs from the basics doc.

Enumerate sections with ForEach(sections:)

Callers write plain Section(header:) and Section(footer:) blocks. The container receives each one as a SectionConfiguration: it is Identifiable and exposes header, footer, and content, each a SubviewsCollection.

struct Carousel<Content: View>: View {
  @ViewBuilder var content: Content

  var body: some View {
    ScrollView {
      LazyVStack {
        ForEach(sections: content) { section in
          VStack {
            section.header
            ScrollView(.horizontal) {
              LazyHStack {
                section.content
                  .containerRelativeFrame(.horizontal)
                  .frame(minHeight: 100)
              }
              .scrollTargetLayout()
            }
            .scrollTargetBehavior(.viewAligned)
            .contentMargins(16)
            section.footer
          }
        }
      }
    }
  }
}

section.content is a collection, so you can also iterate it with an inner ForEach(section.content) or wrap it in any stack.

Index sections with Group(sections:)

Group(sections:) hands over the whole SectionCollection, a RandomAccessCollection of SectionConfiguration values. Use it to single out the first section and slice the rest.

Group(sections: content) { sections in
  if !sections.isEmpty {
    sections[0].content.frame(minHeight: 100)
  }
  if sections.count > 1 {
    ForEach(sections[1...]) { section in
      section.header
      // horizontal rail per section
      section.footer
    }
  }
}

Content without sections still works

When the caller passes plain views with no Section, SwiftUI treats the whole content as one section, so the section APIs never see an empty hierarchy. Guard the indexed access anyway, as above, because the caller controls the count.

Use this guidance in your coding agent

Install the Better Design MCP once. Your agent then loads this page with one call.

get-swiftui-guide({ topic: "swiftui-mastering-container-views-sections" })
claude mcp add --scope user better-design --transport http https://better-design.com/api/mcp --header "Authorization: Bearer <YOUR_API_KEY>"
Browse related design systems