A SwiftUI package that builds a horizontally scrolling, infinitely looping carousel with variable-width slides, snap-to-item scrolling, scroll visibility reporting, and optional auto-advance.
- Infinite horizontal scrolling — Seamlessly scroll in both directions without limits.
- Flexible slide sizing — Supports slides with varying widths and heights, with customizable vertical alignment.
- Snap-to-item scrolling — Easily snaps to the nearest slide when scrolling.
- Visibility reporting — Get notified about each slide’s visibility and position as you scroll.
- Auto-advance — Optionally scrolls to the next slide automatically on a set interval, with smart pause during user interaction.
- Performance optimized — Ensures smooth scrolling, even with complex layouts or large content.
| Platform | Minimum version |
|---|---|
| iOS | 18.0 |
| tvOS | 18.0 |
| watchOS | 11.0 |
| macOS | 26.0 |
Add the following line to your dependencies in the Package.swift file:
.package(url: "https://github.com/TStrawberry/Carousel.git", from: "v1.0.0")Import the module and wrap your slides in Carousel:
import SwiftUI
import Carousel
struct ExampleView: View {
var body: some View {
Carousel(alignment: .center, spacing: 10) {
ForEach(0..<10, id: \.self) { index in
SlideView(index: index)
.containerRelativeFrame(.horizontal) { width, _ in
width * (index.isMultiple(of: 2) ? 1 : 0.5)
}
.id(index)
.onCarouselScrollVisibilityChange { visibility in
// visibility.isVisible, visibility.visiblePercentage
}
}
}
}
}Carousel(alignment: .bottom, spacing: 16) {
// slides…
}Attach scrollPosition to drive or observe the scroll offset:
@State private var scrollPosition = ScrollPosition()
Carousel { /* … */ }
.scrollPosition($scrollPosition, anchor: .leading)
// Example: scroll to a slide by id
withAnimation(.smooth) {
scrollPosition.scrollTo(id: someIndex)
}Carousel { /* … */ }
.autoScrollCarousel(timeInterval: 3)By default, auto-scroll moves to the next slide using each item’s layout id. You can customize the action:
.autoScrollCarousel(timeInterval: 3) { scrollPosition, index in
scrollPosition.wrappedValue.scrollTo(id: index.advanced(by: 1).id)
}Auto-scroll does not run while the user is actively scrolling.
CarouselVisibility exposes:
| Property | Description |
|---|---|
isVisible |
Whether the slide’s frame intersects the scroll view’s visible rect |
visiblePercentage |
Fraction of the slide’s width that is visible (0…1) |
scrollGeometry |
Current ScrollGeometry from the scroll view |
frame |
The slide’s frame in content coordinates |
Attach the modifier to each slide:
.onCarouselScrollVisibilityChange { visibility in
if visibility.isVisible {
print("Slide visible: \(visibility.visiblePercentage)")
}
}Combine with SwiftUI’s onScrollVisibilityChange if you only need a boolean threshold per slide.
MIT © 2026 Tao Tang