mcpbeat Sign in

Layout Skill for Claude

SwiftUI layout beyond stacks — the Layout protocol (when custom layout beats GeometryReader), Grid vs lazy grids, custom containers with sections and container values, and lazy-stack/ScrollView performance rules (what breaks laziness, prefetch discipline, scroll APIs). Use when building custom layouts or containers, fixing lazy-stack jank or memory growth, or wiring programmatic/snapping scrolling.

2k tokens
context cost
the whole folder, loaded on every use
1
files
instructions only
0
copies elsewhere
how many repositories repackaged it
585
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/rshankras/claude-code-apple-skills --skill layout

The instruction itself

9 sections, as written by the author

SwiftUI Layout & Containers

The layer between "stacks and spacers" and "it scrolls like butter with 100k rows" — Apple's

Layout protocol, container composition, and the lazy-stack rules from the WWDC26 deep dive.

View identity/data-flow questions route to swiftui/data-flow.

When This Skill Activates

  • "Make these buttons equal width" / measurement-dependent layout
  • Building a reusable container (custom List/board/carousel) that should accept ForEach + sections
  • Lazy stack jank, memory growth, scroll-position bugs, broken scroll targeting
  • Programmatic scrolling, paging/snapping, scroll-linked effects
  • GeometryReader causing layout loops or mangled sizing

Custom Layout protocol (not GeometryReader)

Reach for a custom Layout whenever you must **measure subviews and feed the measurement back

into layout** — GeometryReader only measures its container and can't influence the engine.

Canonical case: equal-width buttons.

  • sizeThatFits: propose .unspecified to read each subview's ideal size

(subviews.map { $0.sizeThatFits(.unspecified) }); guard empty subviews;

replacingUnspecifiedDimensions() for nil proposal dimensions.

  • placeSubviews: never assume origin (0,0) — use bounds.minX/midX (non-zero origins are

what make layouts composable); place(at:anchor:proposal:) with a proposal that may differ

from the ideal size (that's how equal widths happen).

  • Respect spacing preferences: subviews[i].spacing.distance(to:along:), taking the larger

of conflicting preferences — matching built-in containers. No hardcoded 8s.

  • Per-subview data via LayoutValueKey (+ a layoutValue convenience modifier), read as

subview[Key.self].

  • Cache only after Instruments shows layout cost — it's an optimization, not a requirement.
  • Switch layouts without killing identity: AnyLayout(HStackLayout()) ↔ custom layout with

.animation(_:value:) — SwiftUI sees one changing view, so state survives and it animates.

  • Don't build fallbacks into the layout — wrap alternatives in ViewThatFits.

Grid decisions

| Need | Use |

|---|---|

| Static 2D with cross-row alignment | Grid/GridRow (+ gridCellColumns to span, gridColumnAlignment per column) |

| Scrollable, large content | LazyVGrid/LazyHGrid (only visible views load; one axis fixed up front) |

| "First arrangement that fits" | ViewThatFits |

Custom containers (Demystify Containers)

Make containers that compose like List does:

  • API shape: a trailing @ViewBuilder var content: Content — callers can then mix static

views, ForEach, and conditionals.

  • Iterate resolved children with ForEach(subviews: content); need the whole collection

(count/chunking)? Group(subviews: content) { subviews in … }.

  • Internalize declared vs resolved: one declared ForEach resolves to N subviews; Group to

its children; EmptyView to zero; if conditionally. Counting declared views is a bug.

  • Sections are opt-in: ForEach(sections: content), reading section.header /

section.content; check header.isEmpty before rendering the slot.

  • Per-child customization via container values: extension ContainerValues { @Entry var … },

set with a convenience modifier, read via subview.containerValues. Scoping model:

**Environment flows down · Preferences flow up · container values reach only the direct

container.** Setting one on a Section styles the whole section.

Lazy stacks & scrolling performance (WWDC26 rules)

LazyVStack builds views only until the viewport fills; totals and offsets are estimated

from average placed-view size and corrected as you scroll. Everything below follows from that:

  • One subview per ForEach element, always. An if inside a row (0-or-1 views) forces the

stack to keep off-screen views + their @State alive to preserve indices — and environment

changes then re-evaluate off-screen bodies. Filter at the data layer (@Query predicate);

gate auth-type conditions *outside* the stack.

  • Never key logic off absolute scroll offset in a lazy stack (onScrollGeometryChange sees

estimates) — use onScrollTargetVisibilityChange(threshold: 0.8) for visibility triggers.

  • Set up in init, not onAppear (_model = State(initialValue:)): body runs during

prefetch; onAppear fires only on-screen, throwing prefetch work away and causing

post-appearance size jumps. Start async loads in init/task.

  • Don't persist meaningful state in row @State — off-screen views are eventually

released. Hoist (@State var highlighted: Set<ID> outside, @Binding down).

  • scrollTransition transforms must stay inside the original frame (scale ✅; rotations

escaping the frame make views vanish early).

  • Don't drive layout from onGeometryChange height feedback (content shoves, targeting

breaks) — that's the custom Layout case above.

  • Nest LazyHStack inside LazyVStack freely (unscrolled rows stay unloaded) — but fix child

heights (lineLimit, explicit frames) in the horizontal stacks.

  • pinnedViews: [.sectionHeaders] pins headers; infinite scroll = trailing

ProgressView().onAppear { fetchNextPage() } after the ForEach.

The scroll API map

  • Snapping/paging: scrollTargetLayout() + scrollTargetBehavior(.viewAligned/.paging).
  • Track/control position: scrollPosition binding; programmatic ScrollPosition +

scrollTo(id:) — works for unloaded targets *if* IDs map to stable one-subview elements.

  • Scroll-linked effects: scrollTransition (enter/leave viewport) and visualEffect

(geometry without GeometryReader) — details in design/animation-patterns.

  • Reactions: onScrollGeometryChange (fine outside lazy estimation),

onScrollVisibilityChange (autoplay/analytics).

  • Performance floor: list/scroll internals were rewritten (WWDC25) — macOS lists ~6× faster at

100k+ rows, and lazy loading works in nested ScrollView+LazyVStack; profile with the

SwiftUI instrument (performance/swiftui-debugging).

Output Format

Layout review: Symptom | Rule violated | Fix — check the one-subview-per-element rule first

in any lazy-stack complaint; it explains most jank, memory growth, and targeting bugs.

References

  • https://developer.apple.com/videos/play/wwdc2022/10056/ (Compose custom layouts)
  • https://developer.apple.com/videos/play/wwdc2024/10146/ (Demystify SwiftUI containers)
  • https://developer.apple.com/videos/play/wwdc2026/321/ (Dive into lazy stacks and scrolling)
  • Related skills: swiftui/data-flow (identity/ForEach IDs), performance/swiftui-debugging, design/animation-patterns (scroll-linked effects)

Other skills for the same job

different authors, same section of the catalogue
Theme Factory
by anthropics
vendor ×16

Toolkit for styling artifacts with a theme. These artifacts can be slides, docs, reportings, HTML landing pages, etc. There are 10 pre-set themes with colors/fonts that you can apply to any artifact that has been creating, or can generate a new theme on-the-fly.

36k tokens
Brand Guidelines
by anthropics
vendor ×13

Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.

3k tokens
Artifacts Builder
by JayZeeDesign
×8

Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn/ui). Use for complex artifacts requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX artifacts.

11k tokens scripts
Web Artifacts Builder
by anthropics
vendor ×7

Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn/ui). Use for complex artifacts requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX artifacts.

11k tokens scripts
Frontend Design
by Karanjot786
×6

Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.

1k tokens
Frontend Design
by anthropics
vendor ×4

Guidance for distinctive, intentional visual design when building new UI or reshaping an existing one. Helps with aesthetic direction, typography, and making choices that don't read as templated defaults.

5k tokens
Expo Tailwind Setup
by openai
vendor ×4

Set up Tailwind CSS v4 in Expo with react-native-css and NativeWind v5 for universal styling

3k tokens
Use Dom
by openai
vendor ×3

Use Expo DOM components to run web code in a webview on native and as-is on web. Migrate web code to native incrementally.

3k tokens

How to use it

Copy the folder

Take rshankras/layout from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.