Safe Area — Gotchas & Best Practices (.NET 10+)
Breaking Changes
⚠️.NET 9 → 10: ContentPage default changed to None
ContentPage now defaults to edge-to-edge (None) on all platforms. In.NET 9, Android ContentPage behaved like Container. If your Android content goes under the status bar after upgrading, add SafeAreaEdges="Container" explicitly.
<!-- ❌ .NET 10 default — content goes under status bar on Android -->
<ContentPage>
<!-- ✅ Restore .NET 9 Android behavior -->
<ContentPage SafeAreaEdges="Container">⚠️ WindowSoftInputModeAdjust.Resize migration
If you used WindowSoftInputModeAdjust.Resize in.NET 9, you may need SafeAreaEdges="All" on the ContentPage to maintain keyboard avoidance.
Common Gotchas
1. Layouts default to Container, not None
For true edge-to-edge, set SafeAreaEdges="None" on both the page and layouts:
<!-- ❌ Grid still respects system bars (its default is Container) -->
<ContentPage SafeAreaEdges="None">
<Grid>
<Image Source="bg.jpg" Aspect="AspectFill" />
</Grid>
</ContentPage>
<!-- ✅ Both page and layout set to None -->
<ContentPage SafeAreaEdges="None">
<Grid SafeAreaEdges="None">
<Image Source="bg.jpg" Aspect="AspectFill" />
</Grid>
</ContentPage>2. SoftInput on ScrollView has no effect
ScrollView manages its own content insets. Wrap it in a Grid/StackLayout instead:
<!-- ❌ SoftInput is ignored on ScrollView -->
<ScrollView SafeAreaEdges="SoftInput">
<!-- ✅ Set SoftInput on the wrapper layout -->
<Grid SafeAreaEdges="Container, Container, Container, SoftInput">
<ScrollView> ... </ScrollView>
</Grid>3. Default is not None
Default = "use platform defaults for this control type." None = "edge-to-edge." They differ significantly on ScrollView (iOS maps Default to automatic content insets).
4. Blazor Hybrid double-padding
<!-- ❌ XAML safe area + CSS env() = double padding -->
<ContentPage SafeAreaEdges="Container">
<BlazorWebView ... /> <!-- CSS also uses env(safe-area-inset-*) -->
<!-- ✅ Pick ONE approach — CSS is recommended for Blazor -->
<ContentPage SafeAreaEdges="None">
<BlazorWebView ... /> <!-- CSS handles all insets with env() -->5. iOS Shell/NavigationPage edge-to-edge
Content won't extend behind the nav bar unless you set a transparent background AND hide the separator:
<Shell Shell.BackgroundColor="#80000000"
Shell.NavBarHasShadow="False" />Decision Framework
| Scenario | SafeAreaEdges value |
|---|---|
| Forms, critical inputs | All |
| Photo viewer, video player, game | None (on page AND layout) |
| Scrollable content with fixed header/footer | Container |
| Chat/messaging with bottom input bar | Per-edge: Container, Container, Container, SoftInput |
| Blazor Hybrid app | None on page, CSS env() for insets |
Migration Quick Reference
| Legacy (.NET 9) | New (.NET 10+) |
|---|---|
ios:Page.UseSafeArea="True" | SafeAreaEdges="Container" |
IgnoreSafeArea="True" | SafeAreaEdges="None" |
WindowSoftInputModeAdjust.Resize | SafeAreaEdges="All" on ContentPage |
Legacy properties still work but are obsolete.
Best Practices
- Combine
SafeAreaEdgeswithPadding— safe area handles insets,Paddingadds visual spacing:<ContentPage SafeAreaEdges="All"> <VerticalStackLayout Padding="20"> <!-- Both applied — no conflict --> </VerticalStackLayout> </ContentPage> - Use per-control settings for mixed layouts (edge-to-edge header + safe body + keyboard-aware footer).
- For Blazor Hybrid, prefer CSS
env()and leaveSafeAreaEdges="None". Addviewport-fit=coverto the<meta viewport>tag. - Test on notched devices (iPhone X+, Android cutouts), tablets in landscape, and varying screen sizes.
Checklist
- Android upgrade:
SafeAreaEdges="Container"added if content goes under status bar - Edge-to-edge:
Noneset on both page and layout - ScrollView keyboard avoidance uses wrapper Grid, not ScrollView's own
SafeAreaEdges - Blazor Hybrid: using either XAML or CSS safe areas, not both
viewport-fit=coverin Blazor'sindex.html<meta viewport>tag- Legacy
UseSafeArea/IgnoreSafeAreamigrated toSafeAreaEdges