---
description: The 2026 Capacitor guide to Android edge-to-edge and iOS safe areas — what 8.3.x fixed, the CSS you need, plus Ionic and Tailwind tips.
title: Capacitor Edge-to-Edge & Safe Areas: The Complete Guide - Capawesome
image: https://capawesome.io/docs/assets/images/social/blog/capacitor-edge-to-edge-and-safe-areas-guide.png
---

[ Skip to content](#capacitor-edge-to-edge-safe-areas-the-complete-guide) 

[ 🎉 Introducing **Capawesome Platform** — one platform for Live Updates, Native Builds, App Store Publishing, and Insider SDKs.](https://capawesome.io) 

* [  Formbricks ](/docs/plugins/formbricks/)
* [  Geocoder ](/docs/plugins/geocoder/)
* [  Google Sign-In ](/docs/plugins/google-sign-in/)
* [  Grafana Faro ](/docs/plugins/grafana-faro/)
* [  libSQL ](/docs/plugins/libsql/)
* [  Live Update ](/docs/plugins/live-update/)
* [  Managed Configurations ](/docs/plugins/managed-configurations/)
* [  Media Session ](/docs/plugins/media-session/)
* [  ML Kit ](/docs/plugins/mlkit/)
* [  Navigation Bar ](/docs/plugins/navigation-bar/)
* [  NFC ](/docs/plugins/nfc/)
* [  OAuth ](/docs/plugins/oauth/)
* [  Pedometer ](/docs/plugins/pedometer/)
* [  Photo Editor ](/docs/plugins/photo-editor/)
* [  PostHog ](/docs/plugins/posthog/)
* [  Printer ](/docs/plugins/printer/)
* [  Purchases ](/docs/plugins/purchases/)
* [  RealtimeKit ](/docs/plugins/realtimekit/)
* [  Screen Orientation ](/docs/plugins/screen-orientation/)
* [  Screenshot ](/docs/plugins/screenshot/)
* [  Secure Preferences ](/docs/plugins/secure-preferences/)
* [  Speech Recognition ](/docs/plugins/speech-recognition/)
* [  Speech Synthesis ](/docs/plugins/speech-synthesis/)
* [  Share Target ](/docs/plugins/share-target/)
* [  Square Mobile Payments ](/docs/plugins/square-mobile-payments/)
* [  SQLite ](/docs/plugins/sqlite/)
* [  Superwall ](/docs/plugins/superwall/)
* [  Torch ](/docs/plugins/torch/)
* [  Wifi ](/docs/plugins/wifi/)
* [  Zip ](/docs/plugins/zip/)
* [  Cloud ](/docs/cloud/)
* [  Live Updates ](/docs/cloud/live-updates/)
* Advanced
* Integrations
* [  Native Builds ](/docs/cloud/native-builds/)
* [  Configuration ](/docs/cloud/native-builds/configuration/)
* [  Environments ](/docs/cloud/native-builds/environments/)
* Guides
* [  Sample Projects ](/docs/cloud/native-builds/sample-projects/)
* [  Troubleshooting ](/docs/cloud/native-builds/troubleshooting/)
* [  Automations ](/docs/cloud/automations/)
* [  Assist ](/docs/cloud/assist/)
* Account
* Organizations
* [  Organization and User Management ](/docs/cloud/organizations/memberships/)
* [  Single Sign-On (SSO) ](/docs/cloud/organizations/sso/)
* [  Teams ](/docs/cloud/organizations/teams/)
* [  Two-Factor Authentication ](/docs/cloud/organizations/two-factor-authentication/)
* [  Integrations ](/docs/cloud/integrations/)
* [  License Keys ](/docs/cloud/license-keys/)
* [  Webhooks ](/docs/cloud/webhooks/)
* [  Pricing ](https://capawesome.io/pricing/)
* [  FAQ ](/docs/cloud/faq/)
* [  Support ](/docs/cloud/support/)
* [  Contributing ](/docs/contributing/)
* [  LLMs ](/docs/llms/)
* [  Insiders ](/docs/insiders/)
* [  License ](https://capawesome.io/legal/eula/)
* [  Support ](/docs/insiders/support/)
* [  FAQ ](/docs/insiders/faq/)
* [  Blog ](/blog/)
* Categories

* [  Ionic Framework Specifics ](#ionic-framework-specifics)
* [  Bonus: Tailwind CSS with tailwindcss-safe-area ](#bonus-tailwind-css-with-tailwindcss-safe-area)
* [  When the Capawesome Plugin Still Fits ](#when-the-capawesome-plugin-still-fits)
* [  Get Started ](#get-started)
* [  Conclusion ](#conclusion)

# Capacitor Edge-to-Edge & Safe Areas: The Complete Guide[¶](#capacitor-edge-to-edge-safe-areas-the-complete-guide "Permanent link")

Edge-to-edge has been one of the rougher edges of building with Capacitor for the past two years. Status bars hiding the top of your header, navigation gestures obscuring buttons, an Android plugin patched into every project just to make insets work — most teams ended up with a workaround instead of a real solution.

That story changed quietly between Capacitor 8.3.0 and 8.3.2\. The framework now ships proper edge-to-edge support out of the box on both platforms, and the third-party plugin most projects relied on has moved from "recommended" to "fallback for special cases." This guide is the up-to-date playbook: what the platforms expect, what Capacitor finally fixed, and the small set of CSS you actually need to write today.

[ ![Build and deploy your Capacitor app with Capawesome Cloud](../../assets/external/cloud.capawesome.io/assets/banners/cloud-build-and-deploy-capacitor-apps.69628c3f.png) ](https://capawesome.io/) 

## What Edge-to-Edge Means, and Why It Matters Now[¶](#what-edge-to-edge-means-and-why-it-matters-now "Permanent link")

Edge-to-edge is the design pattern where your app's content extends all the way under the system bars — the status bar at the top, the navigation bar at the bottom, the gesture indicator on iOS. The bars are still visible, but they sit on top of your content instead of pushing it down. The visible result is a fuller, more immersive screen. The technical result is that _you_ are now responsible for making sure nothing important — a header title, a logout button, a floating action button — ends up trapped under one of those bars.

On iOS, this has been the default since the iPhone X in 2017\. The platform exposes the `env(safe-area-inset-*)` CSS variables for content that should stay clear of notches, the home indicator, and the status bar. Most Capacitor projects already handle this.

Android is the part that recently changed. Starting with Android 15 (API level 35), edge-to-edge is enforced for any app targeting SDK 35 or later — and once your Play Store listing requires SDK 35, every install on Android 15+ gets the new behavior. On Android 16, the previous opt-out via `windowOptOutEdgeToEdgeEnforcement` is gone entirely. There's no escape hatch left.

For Capacitor apps the consequence is unavoidable: your WebView now renders behind the status and navigation bars on modern Android, and your CSS needs to account for that. The good news is that, as of Capacitor 8.3.2, the framework gives you the information you need to do it cleanly.

## The Old Pain Points[¶](#the-old-pain-points "Permanent link")

To appreciate what 8.3.x fixed, it helps to remember what shipping edge-to-edge with Capacitor used to look like.

**The StatusBar plugin's escape hatches stopped working.** For years, the common workaround was to set `overlaysWebView: false` and `backgroundColor` on the `@capacitor/status-bar` plugin and call it a day — the WebView would simply not extend under the status bar. That approach quietly stopped working on Android 16, because both options depend on Android's opt-out behavior, which Android 16 no longer allows.

**The CSS variables returned the wrong values.** Even when you tried to do things the "right way" with `env(safe-area-inset-top)` and friends, Android WebView versions below 140 had [a bug](https://issues.chromium.org/issues/40699457) that returned incorrect values. Your top inset would sometimes be `0`, sometimes off by a few pixels — almost never right.

**The de-facto workaround was a plugin.** Most projects ended up installing the [Android Edge-to-Edge Support](/docs/plugins/android-edge-to-edge-support/) plugin, which sidestepped the broken CSS variables by applying insets to the WebView itself. It worked, but it meant carrying a non-trivial third-party dependency that wouldn't be needed once the platform caught up.

The platform has now caught up.

## What Capacitor 8.3.0 – 8.3.2 Fixed[¶](#what-capacitor-830-832-fixed "Permanent link")

Three releases over roughly six weeks closed the gap:

* **8.3.0** — `SystemBars` now uses native safe area insets on Android ([#8384](https://github.com/ionic-team/capacitor/issues/8384)). This is the foundational change. The framework reads insets from `WindowInsetsCompat` and injects them as parallel `--safe-area-inset-*` CSS custom properties. That matters because Android WebView versions below 140 still have a bug where `env(safe-area-inset-*)` returns the wrong values — the injected variables sidestep the bug entirely, and the recommended pattern is to combine both with a `var()` fallback.
* **8.3.1** — Separate style state for the status bar and navigation bar, so styling one no longer accidentally affects the other.
* **8.3.2** — Removed extra view padding on Android API levels ≤ 34, which had been pushing content down by an unwanted amount on older devices.

Together, these mean that on a project running Capacitor 8.3.2 or later, a single CSS rule per edge — the injected `--safe-area-inset-*` variable with `env()` as a fallback — gets you correct safe area handling on both iOS and Android, all the way down to the WebViews that still ship the `env()` bug. No third-party plugin, no manual JavaScript, no platform branching.

## The Recommended 2026 Setup[¶](#the-recommended-2026-setup "Permanent link")

Three core pieces, plus a couple of optional polish steps.

### 1\. Upgrade to Capacitor 8.3.2 or Later[¶](#1-upgrade-to-capacitor-832-or-later "Permanent link")

This is the whole prerequisite. If you're on 8.3.2+, you can skip the rest of this section. If you're on an older 8.x or on 7.x, bump it now — the upgrade is small and the payoff is large.

`[](#%5F%5Fcodelineno-0-1)npm install @capacitor/core@^8.3.2 @capacitor/cli@^8.3.2
[](#%5F%5Fcodelineno-0-2)npm install @capacitor/android@^8.3.2 @capacitor/ios@^8.3.2
[](#%5F%5Fcodelineno-0-3)npx cap sync
`

If you're coming from Capacitor 7, the [Updating to Capacitor 8](/blog/updating-to-capacitor-8/) post covers the breaking changes worth knowing about.

### 2\. Set the Viewport Meta Tag[¶](#2-set-the-viewport-meta-tag "Permanent link")

Both platforms require an opt-in for the WebView to extend under safe areas. Add `viewport-fit=cover` to your viewport meta tag in `index.html`:

`[](#%5F%5Fcodelineno-1-1)<meta
[](#%5F%5Fcodelineno-1-2)  name="viewport"
[](#%5F%5Fcodelineno-1-3)  content="width=device-width, initial-scale=1, viewport-fit=cover"
[](#%5F%5Fcodelineno-1-4)/>
`

Without this, iOS will respect the safe areas automatically by _not_ drawing under the notch — which is sometimes what you want, but it isn't edge-to-edge. If your goal is a full-bleed UI, the `viewport-fit=cover` opt-in is mandatory.

### 3\. Use the Safe-Area CSS Variables[¶](#3-use-the-safe-area-css-variables "Permanent link")

The web platform exposes safe-area insets via the `env(safe-area-inset-*)` function — four values for top, right, bottom, and left. iOS and Android WebView 140+ return correct values here; older Android WebViews don't, which is exactly why Capacitor also injects parallel `--safe-area-inset-*` custom properties.

The recommended pattern is to read the injected variable first and fall back to `env()`:

`[](#%5F%5Fcodelineno-2-1).app-header {
[](#%5F%5Fcodelineno-2-2)  padding-top: var(--safe-area-inset-top, env(safe-area-inset-top, 0px));
[](#%5F%5Fcodelineno-2-3)}
[](#%5F%5Fcodelineno-2-4)
[](#%5F%5Fcodelineno-2-5).app-footer {
[](#%5F%5Fcodelineno-2-6)  padding-bottom: var(
[](#%5F%5Fcodelineno-2-7)    --safe-area-inset-bottom,
[](#%5F%5Fcodelineno-2-8)    env(safe-area-inset-bottom, 0px)
[](#%5F%5Fcodelineno-2-9)  );
[](#%5F%5Fcodelineno-2-10)}
[](#%5F%5Fcodelineno-2-11)
[](#%5F%5Fcodelineno-2-12).app-content {
[](#%5F%5Fcodelineno-2-13)  padding-left: var(--safe-area-inset-left, env(safe-area-inset-left, 0px));
[](#%5F%5Fcodelineno-2-14)  padding-right: var(--safe-area-inset-right, env(safe-area-inset-right, 0px));
[](#%5F%5Fcodelineno-2-15)}
`

The trailing `0px` keeps things sensible on environments where neither value is defined — desktop previews, older browsers, and the like.

If you want to combine the inset with an existing padding value, wrap the whole thing in a `calc()`:

`[](#%5F%5Fcodelineno-3-1).app-header {
[](#%5F%5Fcodelineno-3-2)  padding-top: calc(
[](#%5F%5Fcodelineno-3-3)    var(--safe-area-inset-top, env(safe-area-inset-top, 0px)) + 1rem
[](#%5F%5Fcodelineno-3-4)  );
[](#%5F%5Fcodelineno-3-5)}
`

That's the whole core of safe area handling. Everything below this is platform-specific polish.

### 4\. Configure SystemBars (Optional)[¶](#4-configure-systembars-optional "Permanent link")

Capacitor's built-in `SystemBars` API (part of `@capacitor/core`, no extra install) controls the styling and visibility of the system bars. The default `insetsHandling: "css"` is what makes the CSS variables work — leave it on.

The most common configuration is just choosing a style:

capacitor.config.ts

`[](#%5F%5Fcodelineno-4-1)import type { CapacitorConfig } from "@capacitor/cli";
[](#%5F%5Fcodelineno-4-2)
[](#%5F%5Fcodelineno-4-3)const config: CapacitorConfig = {
[](#%5F%5Fcodelineno-4-4)  plugins: {
[](#%5F%5Fcodelineno-4-5)    SystemBars: {
[](#%5F%5Fcodelineno-4-6)      style: "DARK",
[](#%5F%5Fcodelineno-4-7)    },
[](#%5F%5Fcodelineno-4-8)  },
[](#%5F%5Fcodelineno-4-9)};
[](#%5F%5Fcodelineno-4-10)
[](#%5F%5Fcodelineno-4-11)export default config;
`

`DARK` makes the icons in the status and navigation bars dark — appropriate for a light app background. `LIGHT` does the opposite, for a dark app. `DEFAULT` follows the system theme.

If you need to change the style at runtime, the API exposes `setStyle()` per bar:

`[](#%5F%5Fcodelineno-5-1)import { SystemBars, SystemBarsStyle, SystemBarType } from "@capacitor/core";
[](#%5F%5Fcodelineno-5-2)
[](#%5F%5Fcodelineno-5-3)await SystemBars.setStyle({
[](#%5F%5Fcodelineno-5-4)  bar: SystemBarType.StatusBar,
[](#%5F%5Fcodelineno-5-5)  style: SystemBarsStyle.Light,
[](#%5F%5Fcodelineno-5-6)});
`

Note that the modern `SystemBars` API does **not** include `setOverlaysWebView()` or `setBackgroundColor()`. Those still exist on the legacy `@capacitor/status-bar` plugin for backward compatibility, but as covered earlier, they're no-ops on Android 16+. If you need a colored status bar background, the recommended approach is now an HTML element with your background color and the safe-area padding applied.

There's one related gap on Android worth knowing about: coloring the **navigation bar** background. In edge-to-edge mode the gesture navigation bar is transparent, so your content shows through and there's nothing to color. But devices still using the older three-button navigation render an opaque bar — and `SystemBars` can only set its _style_ (dark or light icons), not its background color. The official `@capacitor/status-bar` plugin covers the status bar only. To color the Android navigation bar background, we built the [Navigation Bar](/docs/plugins/navigation-bar/) plugin, which exposes a [setColor(...)](/docs/plugins/navigation-bar/#setcolor) method:

`[](#%5F%5Fcodelineno-6-1)import { NavigationBar } from "@capawesome/capacitor-navigation-bar";
[](#%5F%5Fcodelineno-6-2)
[](#%5F%5Fcodelineno-6-3)await NavigationBar.setColor({ color: "#ffffff" });
`

### 5\. iOS-Specific Considerations[¶](#5-ios-specific-considerations "Permanent link")

iOS has had safe areas for nearly a decade, so most of this works out of the box. Two settings are worth verifying.

**`UIViewControllerBasedStatusBarAppearance`** in your `Info.plist` controls whether each view controller can set its own status bar style or whether the value is fixed app-wide. For Capacitor apps, leave it at the default of `YES` so `SystemBars.setStyle()` works at runtime:

ios/App/App/Info.plist

`[](#%5F%5Fcodelineno-7-1)<key>UIViewControllerBasedStatusBarAppearance</key>
[](#%5F%5Fcodelineno-7-2)<true/>
`

**`UIStatusBarStyle`** sets the initial status bar style before your JavaScript loads. Match it to your launch screen:

ios/App/App/Info.plist

`[](#%5F%5Fcodelineno-8-1)<key>UIStatusBarStyle</key>
[](#%5F%5Fcodelineno-8-2)<string>UIStatusBarStyleDarkContent</string>
`

That's all there is on iOS. The `env(safe-area-inset-*)` values have always been correct here, so the `var()` / `env()` pair from earlier resolves cleanly; the home indicator gap is handled automatically when you pad the bottom edge, and the WebView extends edge-to-edge as soon as `viewport-fit=cover` is set.

## Ionic Framework Specifics[¶](#ionic-framework-specifics "Permanent link")

If you're on Ionic, almost every component already applies safe-area padding correctly. `ion-header`, `ion-footer`, `ion-tab-bar`, `ion-toolbar`, and `ion-content` all read the same `env(safe-area-inset-*)` values (re-exposed as `--ion-safe-area-top`, `--ion-safe-area-bottom`, etc.) and lay themselves out accordingly. You usually don't have to touch anything.

The exception that bites people is [ion-fab](https://ionicframework.com/docs/api/fab). Quoting the Ionic docs directly:

> If there is no `ion-header` or `ion-footer` component, the fab may be covered by a device's notch, status bar, or other device UI. In these cases, the safe area on the top and bottom is not taken into account.

The framework only applies safe-area padding to a FAB when it can position itself relative to a header or footer. Drop a `vertical="top"` FAB onto a page without an `ion-header` and it sits at coordinate `0` — directly under the status bar. A few lines of CSS fix it:

`[](#%5F%5Fcodelineno-9-1)ion-fab[vertical="top"] {
[](#%5F%5Fcodelineno-9-2)  margin-top: var(--ion-safe-area-top, 0);
[](#%5F%5Fcodelineno-9-3)}
[](#%5F%5Fcodelineno-9-4)
[](#%5F%5Fcodelineno-9-5)ion-fab[vertical="bottom"] {
[](#%5F%5Fcodelineno-9-6)  margin-bottom: var(--ion-safe-area-bottom, 0);
[](#%5F%5Fcodelineno-9-7)}
`

Use the attribute selectors so the rule only applies where it's needed. The `0` fallback in `var()` keeps the layout sensible on platforms or devices where the variable isn't set.

## Bonus: Tailwind CSS with `tailwindcss-safe-area`[¶](#bonus-tailwind-css-with-tailwindcss-safe-area "Permanent link")

If your project uses Tailwind, hand-writing `padding-top: env(safe-area-inset-top)` everywhere stops feeling Tailwind-ish very quickly. The community package [tailwindcss-safe-area](https://github.com/mvllow/tailwindcss-safe-area) fills that gap with a clean set of utilities.

Install it (the v4 install — for Tailwind v3, see the package README):

`[](#%5F%5Fcodelineno-10-1)npm install tailwindcss-safe-area
`

Then import it in your main CSS file alongside Tailwind:

`[](#%5F%5Fcodelineno-11-1)@import "tailwindcss";
[](#%5F%5Fcodelineno-11-2)@import "tailwindcss-safe-area";
`

Make sure your viewport meta tag still has `viewport-fit=cover` — the utilities rely on `env()` under the hood and need the WebView to be in edge-to-edge mode. One caveat: because the package uses `env()` directly and doesn't read the injected `--safe-area-inset-*` variables, users on Android WebView versions below 140 will see `0` insets. For most apps that's a rapidly shrinking minority, but if you need to cover those devices, hand-roll the `var()` / `env()` fallback on the critical elements.

The most useful classes follow the same naming pattern as Tailwind's built-in spacing:

`[](#%5F%5Fcodelineno-12-1)<header class="pt-safe">...</header>
[](#%5F%5Fcodelineno-12-2)<main class="px-safe">...</main>
[](#%5F%5Fcodelineno-12-3)<footer class="pb-safe">...</footer>
`

Two variants make this far more flexible than plain `env()`:

* `pt-safe-offset-4` applies the safe-area inset _plus_ `1rem` (Tailwind's `4` spacing token), the equivalent of `calc(env(safe-area-inset-top) + 1rem)`.
* `pb-safe-or-8` applies the safe-area inset _or_ `2rem`, whichever is larger — handy when you want a minimum bottom padding on devices without a home indicator.

There are matching utilities for margins, scroll padding, borders, height (`h-screen-safe`, `h-dvh-safe`), and inset positioning (`top-safe`, `bottom-safe`). For a Tailwind-first app, this package removes nearly all hand-written safe-area CSS.

## When the Capawesome Plugin Still Fits[¶](#when-the-capawesome-plugin-still-fits "Permanent link")

The [Android Edge-to-Edge Support](/docs/plugins/android-edge-to-edge-support/) plugin is still maintained and still useful — its role has just changed. With Capacitor 8.3.2+ handling insets natively, you only need the plugin if **you don't want to implement safe-area handling in CSS at all**. The plugin keeps the traditional Android behavior: it applies the system bar insets directly to the WebView, so your content never goes edge-to-edge in the first place and you don't have to add a single line of `env(safe-area-inset-*)` CSS to your app.

That tradeoff is appealing in a couple of specific situations:

* **Legacy apps you don't want to restyle.** A long-running app with hundreds of screens, none of which were written with edge-to-edge in mind, can install the plugin and keep behaving the way it always did on Android.
* **Capacitor 7 or earlier.** The native fix only landed in 8.3.0\. If you're still on 7, the plugin is genuinely your best option until you can upgrade.
* **You want a colored status bar background on Android.** The legacy `StatusBar.setBackgroundColor()` no longer works on Android 16\. The plugin's `setBackgroundColor()`, `setStatusBarColor()`, and `setNavigationBarColor()` methods still do.

For any new project, or any project where you're willing to spend an hour adding `pt-safe` / `env(safe-area-inset-top)` to a few elements, the CSS-first approach is the cleaner long-term choice — fewer dependencies, standard CSS, and consistent behavior across iOS and Android.

## Get Started[¶](#get-started "Permanent link")

Most projects need three small changes:

1. Upgrade to Capacitor 8.3.2 or later.
2. Add `viewport-fit=cover` to the viewport meta tag in `index.html`.
3. Apply the safe-area CSS variables — `var(--safe-area-inset-*, env(safe-area-inset-*))`, or `pt-safe`, or `--ion-safe-area-*` — to your header, footer, and any other content at the screen edges.

That's the entire core of edge-to-edge in Capacitor as of 2026.

[Subscribe to the Capawesome Newsletter](https://capawesome.io/newsletter)

## Conclusion[¶](#conclusion "Permanent link")

For the last two years, "doing edge-to-edge in Capacitor" meant installing a plugin and accepting some duct tape. As of Capacitor 8.3.2, it means three lines of meta tag and CSS — the standard the web platform has had for years finally works the same way on both iOS and Android. Ionic users get most of the work for free, Tailwind users get a clean utility set with `tailwindcss-safe-area`, and the third-party Android plugin is now a deliberate choice for the few apps that want to opt _out_ of edge-to-edge entirely.

If you want to go further from here:

* [Updating to Capacitor 8](/blog/updating-to-capacitor-8/) — a walkthrough of the broader 8.x upgrade, including the changes that paved the way for native edge-to-edge support.
* [Android Edge-to-Edge Support plugin docs](/docs/plugins/android-edge-to-edge-support/) — for projects that decide the plugin is the right tradeoff.

Questions, edge cases, or war stories from your own edge-to-edge rollout? Drop into the [Capawesome Discord server](https://discord.gg/VCXxSVjefW) — that's where we're always happy to chat. And if you'd like the next deep-dive in your inbox, subscribe to the [Capawesome newsletter](https://capawesome.io/newsletter).

May 29, 2026 

 Back to top 