SwiftUI Liquid Glass
Use native Liquid Glass APIs on iOS 26+. Do not recreate the effect with materials, blurs, or shadows.
Core rules
- Don't build custom Liquid Glass through adding backgrounds, outlines, blurs and shadows.
- Buttons — style: use
.buttonStyle(.glass)for non-colored buttons, and.buttonStyle(.glassProminent)for tinted buttons..glassProminentsupports.tint(), but this can only be a color (not a gradient). - Buttons — shape: use
.buttonBorderShape()instead of applying a shape to the button label by hand. - Buttons — padding: using this button setup adds roughly 13pt of padding inside the liquid glass shape; keep this in mind when adapting designs.
- Custom views: use
.glassEffect(.regular, in: ...)to embed custom Views inside liquid glass containers. - Grouped glass: use
GlassEffectContainerwhen multiple liquid glass elements are next to each other. this View / Container adds a liquid merge effect when the elements grow / touch each other. - Scrolling: avoid using liquid glass inside ScrollView and List (anything that scrolls).
- Bottom bars: when anchoring a liquid glass View to the bottom of the screen, prefer embedding it in
.safeAreaBar(.bottom)instead of a VStack or.overlay(). safeAreaBar adds a subtle blur effect behind its content. - Toolbar items — no glass: on iOS 26+, navigation bar and window toolbar items get a shared Liquid Glass background by default. For items that should not show the glass capsule (plain icons, custom labels, status text, logos), apply
.sharedBackgroundVisibility(.hidden)on theToolbarItem, not on the inner view. - SF Symbols — no pre-enclosed symbols: do not use pre-enclosed SF Symbol variants (such as
plus.circle,xmark.circle.fill, ortrash.circle) inside Liquid Glass buttons or containers. The glass shape already acts as the container; pre-enclosed symbols create an awkward "double enclosure" (a circle inside a circle). Use base/unenclosed symbols (plus,xmark,trash) and let.buttonBorderShape()or the glass container define the enclosing shape.
Decision tree
Need a button? ├─ Neutral / secondary → .buttonStyle(.glass) └─ Tinted / primary → .buttonStyle(.glassProminent).tint(someColor) Icon in a glass button / surface? └─ Plain unenclosed symbol (e.g. "plus", not "plus.circle") — glass provides enclosure Need a custom non-button surface (chip, badge, card)? └─ .glassEffect(.regular, in: shape) on the view content Multiple glass elements nearby? └─ Wrap in GlassEffectContainer(spacing: ...) { ... } Fixed bottom toolbar / action bar? └─ .safeAreaBar(.bottom) { ... } — not VStack + overlay Toolbar item without glass background? └─ ToolbarItem { ... }.sharedBackgroundVisibility(.hidden) Inside ScrollView, List, or Form rows? └─ Do not use Liquid Glass — use solid/material fallback
Workflow
1) Review existing UI
- Flag custom blur/material stacks masquerading as glass.
- Check buttons use
.glass/.glassProminent, not hand-built capsules. - Confirm
.buttonBorderShape()is used instead of clipping the label. - Verify grouped elements sit in
GlassEffectContainer. - Flag glass inside scrollable containers.
- Check bottom-anchored bars use
.safeAreaBar(.bottom). - Check toolbar items that should appear without glass use
.sharedBackgroundVisibility(.hidden)on theToolbarItem. - Flag pre-enclosed SF Symbols (e.g. circle/square variants) inside glass buttons or surfaces.
- Gate with
#available(iOS 26, *)and provide fallbacks.
2) Implement or refactor
- Pick the right primitive (button style vs
glassEffectvssafeAreaBar). - Apply layout and typography first; add glass modifiers last.
- Wrap adjacent glass elements in
GlassEffectContainer. - Account for ~13pt internal button padding when matching designs.
- Use unenclosed SF Symbols so the glass shape provides the enclosing geometry.
- Add iOS 26 availability checks and pre-26 fallbacks.
Patterns
Glass buttons
swift// Secondary / neutral Button("Cancel") { dismiss() } .buttonStyle(.glass) .buttonBorderShape(.capsule) // Primary / tinted — color only, not gradient Button("Save") { save() } .buttonStyle(.glassProminent) .tint(.blue) .buttonBorderShape(.roundedRectangle(radius: 12))
Do not clip the label yourself:
swift// ❌ Wrong — shape on label, not the glass button Button { action() } label: { Text("Save") .padding() .background(.ultraThinMaterial, in: Capsule()) } // ✅ Right — native glass handles shape and padding Button("Save") { action() } .buttonStyle(.glassProminent) .buttonBorderShape(.capsule)
Do not use pre-enclosed SF Symbols inside glass buttons:
swift// ❌ Wrong — pre-enclosed symbol creates redundant double enclosure Button { add() } label: { Image(systemName: "plus.circle.fill") } .buttonStyle(.glass) .buttonBorderShape(.circle) // ✅ Right — plain symbol, glass shape provides the enclosure Button { add() } label: { Image(systemName: "plus") } .buttonStyle(.glass) .buttonBorderShape(.circle)
Custom glass surfaces
swiftLabel("3 items", systemImage: "tray") .padding(.horizontal, 16) .padding(.vertical, 10) .glassEffect(.regular, in: .capsule)
Add .interactive() when the surface responds to touch:
swiftText("Tap me") .padding() .glassEffect(.regular.interactive(), in: .rect(cornerRadius: 16))
Grouped glass (merge effect)
swiftGlassEffectContainer(spacing: 24) { HStack(spacing: 24) { ToolButton(icon: "pencil") ToolButton(icon: "eraser") ToolButton(icon: "lasso") } } private struct ToolButton: View { let icon: String var body: some View { Image(systemName: icon) .frame(width: 56, height: 56) .font(.title2) .glassEffect(.regular, in: .circle) } }
Tune spacing to control how close elements must be before the liquid merge kicks in.
Bottom action bar
swiftContentView() .safeAreaBar(.bottom) { HStack { Button("Share") { share() } .buttonStyle(.glass) Button("Done") { done() } .buttonStyle(.glassProminent) } }
Prefer this over pinning with VStack { Spacer(); ... } or .overlay(alignment: .bottom).
Toolbar items without glass
On iOS 26+, toolbar items in the same logical grouping share a Liquid Glass background. Hide it when the item should look bare:
swift.toolbar { ToolbarItem(placement: .principal) { Text("Draft") .font(.headline) } .sharedBackgroundVisibility(.hidden) ToolbarItem(placement: .topBarTrailing) { Button { add() } label: { Image(systemName: "plus") } } .sharedBackgroundVisibility(.hidden) ToolbarItem(placement: .topBarTrailing) { Button("Save") { save() } .buttonStyle(.glassProminent) } }
Apply .sharedBackgroundVisibility(.hidden) on the ToolbarItem, not on the Button or label inside. Hiding the effect places the item in its own grouping, which can change spacing relative to glass-backed neighbors.
Do not put the modifier on the inner view:
swift// ❌ Wrong — modifier on Button, glass background remains ToolbarItem(placement: .topBarTrailing) { Button { add() } label: { Image(systemName: "plus") } .sharedBackgroundVisibility(.hidden) } // ✅ Right — modifier on ToolbarItem ToolbarItem(placement: .topBarTrailing) { Button { add() } label: { Image(systemName: "plus") } } .sharedBackgroundVisibility(.hidden)
Availability fallback
swiftif #available(iOS 26, *) { content.glassEffect(.regular, in: .rect(cornerRadius: 16)) } else { content.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 16)) }
Anti-patterns
| Anti-pattern | Why it fails | Do instead |
|---|---|---|
.background(.ultraThinMaterial) + blur + stroke + shadow | Not Liquid Glass; wrong optics and no merge/morph | .glassEffect(...) or button styles |
Gradient .tint() on .glassProminent | API accepts color only | Solid Color tint, or custom label without glassProminent tint |
Glass on List/ScrollView rows | Scroll + glass = visual glitches, perf cost | Opaque/material rows; glass only on fixed chrome |
| Shape applied to button label | Misses native padding (~13pt) and border rendering | .buttonBorderShape() |
Bottom bar via .overlay | No system blur bar treatment | .safeAreaBar(.bottom) |
| Multiple glass views without container | No merge effect, worse rendering | GlassEffectContainer |
| Glass capsule on toolbar items that should be bare | iOS 26 adds shared glass to toolbar groupings by default | .sharedBackgroundVisibility(.hidden) on the ToolbarItem |
.sharedBackgroundVisibility on inner view | Does not remove toolbar glass background | Apply on ToolbarItem (or other ToolbarContent) |
Pre-enclosed SF Symbols in glass buttons (plus.circle, xmark.circle.fill) | Redundant double enclosure ("circle in circle"); clashes with glass shape | Use unenclosed symbol (plus, xmark) and let .buttonBorderShape provide the container |
Review checklist
- No hand-rolled blur/material glass imitations
- Buttons use
.glassor.glassProminentwith.buttonBorderShape() - Design spacing accounts for ~13pt internal button padding
- Custom surfaces use
.glassEffect(.regular, in: ...) - Adjacent glass wrapped in
GlassEffectContainer - No glass inside scroll views
- Bottom chrome uses
.safeAreaBar(.bottom) - Toolbar items without glass use
.sharedBackgroundVisibility(.hidden)onToolbarItem - No pre-enclosed SF Symbols in glass buttons (e.g. "plus" instead of "plus.circle")
-
#available(iOS 26, *)with fallback on older OS
Additional resources
- Detailed API notes and morphing transitions: reference.md
- Applying Liquid Glass to custom views
- Landmarks: Building an app with Liquid Glass


