Problem
On iOS, the authorization flow opens as a modal sheet over the host app via
ASWebAuthenticationSession. The user never feels like they've left the app:
the host's wallpaper is visible behind the sheet, animations are continuous,
and dismissal returns control to the same screen.
On Android, the equivalent flow opens a Chrome Custom Tab in full-screen
mode. The host activity is fully obscured. To users this reads as "the app
opened a browser" rather than "the app is asking me to sign in," which is
both a UX downgrade vs. iOS and a real cognitive interruption — especially
on the first sign-in / connect-account flow.
| iOS (today, no library change) |
Android (today, no library change) |
 |
Full-screen Chrome Custom Tab — fully obscures the host app. |
Chrome 107 (Oct 2022) shipped the Partial Custom Tabs API
that closes this gap: it renders the Custom Tab as a user-resizable
bottom sheet at a caller-specified initial height. With it, the Android
side can match iOS's modal feel:
| Android with Partial Custom Tab (this proposal) |
 |
The feature is opt-in (default behavior unchanged) and gracefully degrades:
older Chrome silently ignores the extra and falls back to the full-screen
Custom Tab — i.e. zero regression risk for users on Chrome < 107.
Proposal
Add one optional AuthConfiguration field, Android-only:
androidCustomTabPartialHeightFraction?: number; // (0, 1]
Semantics:
- Unset (default) — no change.
CustomTabsIntent.Builder is built as
today; Chrome renders the full-screen Custom Tab.
- Set to a fraction in
(0, 1] (e.g. 0.85) — between
authService.createCustomTabsIntentBuilder() and .build(), the library
calls setInitialActivityHeightPx(displayHeight * fraction, CustomTabsIntent.ACTIVITY_HEIGHT_ADJUSTABLE). Chrome 107+ renders the
bottom sheet; older Chrome ignores the extra and falls back to
full-screen. The activity stays user-resizable to full screen via the
drag handle (ACTIVITY_HEIGHT_ADJUSTABLE).
- Skipped when
androidTrustedWebActivity is true (TWAs intentionally
render full-screen).
A fraction is preferred over raw pixels because it's display-agnostic — the
caller doesn't have to reach for Dimensions.get('window').height or worry
about dp vs. px. Chrome enforces a 50% floor internally, so values
below that are auto-clamped by Chrome (this is documented behavior, not a
library concern).
Implementation notes
- androidx.browser bumps
1.4.0 → 1.5.0 for the
setInitialActivityHeightPx API. The bump is API-compatible and 1.5.0
has been stable since late 2022.
- First-launch warmup caveat (worth documenting next to the new option):
Chrome's Partial CCT requires the CustomTabsIntent.Builder to be
constructed with an established CustomTabsSession to render as a
bottom sheet on the first invocation. Otherwise Chrome's first launch
falls back to full-screen and only subsequent launches get the sheet.
The library already exposes
prefetchConfiguration — calling
it on the screen that will later invoke authorize() is sufficient
(verified on a real Pixel emulator running Chrome 147). The PR's docs
update mentions this explicitly.
- No iOS counterpart needed —
ASWebAuthenticationSession already
presents as a modal sheet by design. The new option is silently ignored
on iOS (guarded by Platform.OS === 'android').
- No tests need mocking the native side — the JS layer's
responsibility is validation + correct positional argument to the
native bridge, both covered by spec additions.
Backward compatibility
- Field is optional with default
null (no behavior change when unset).
- Existing API surface untouched: no field renamed, removed, or whose
meaning shifted.
- Existing tests pass after pinning the new positional arg.
- Older Chrome / older Android versions: extra is ignored by Chrome, no
crash, falls back to current full-screen Custom Tab behavior.
PR
A PR with the full implementation (TS types, JS validation, Java wiring,
README/docs, 5 new spec tests covering validation + bridge passthrough,
plus a changeset for the minor bump) is ready and will be linked below.
Happy to iterate on the API shape (e.g. accept an object form, expose
resizeBehavior, etc.) if maintainers prefer a different surface.
Problem
On iOS, the authorization flow opens as a modal sheet over the host app via
ASWebAuthenticationSession. The user never feels like they've left the app:the host's wallpaper is visible behind the sheet, animations are continuous,
and dismissal returns control to the same screen.
On Android, the equivalent flow opens a Chrome Custom Tab in full-screen
mode. The host activity is fully obscured. To users this reads as "the app
opened a browser" rather than "the app is asking me to sign in," which is
both a UX downgrade vs. iOS and a real cognitive interruption — especially
on the first sign-in / connect-account flow.
Chrome 107 (Oct 2022) shipped the Partial Custom Tabs API
that closes this gap: it renders the Custom Tab as a user-resizable
bottom sheet at a caller-specified initial height. With it, the Android
side can match iOS's modal feel:
The feature is opt-in (default behavior unchanged) and gracefully degrades:
older Chrome silently ignores the extra and falls back to the full-screen
Custom Tab — i.e. zero regression risk for users on Chrome < 107.
Proposal
Add one optional
AuthConfigurationfield, Android-only:Semantics:
CustomTabsIntent.Builderis built astoday; Chrome renders the full-screen Custom Tab.
(0, 1](e.g.0.85) — betweenauthService.createCustomTabsIntentBuilder()and.build(), the librarycalls
setInitialActivityHeightPx(displayHeight * fraction, CustomTabsIntent.ACTIVITY_HEIGHT_ADJUSTABLE). Chrome 107+ renders thebottom sheet; older Chrome ignores the extra and falls back to
full-screen. The activity stays user-resizable to full screen via the
drag handle (
ACTIVITY_HEIGHT_ADJUSTABLE).androidTrustedWebActivityis true (TWAs intentionallyrender full-screen).
A fraction is preferred over raw pixels because it's display-agnostic — the
caller doesn't have to reach for
Dimensions.get('window').heightor worryabout
dpvs.px. Chrome enforces a 50% floor internally, so valuesbelow that are auto-clamped by Chrome (this is documented behavior, not a
library concern).
Implementation notes
1.4.0 → 1.5.0for thesetInitialActivityHeightPxAPI. The bump is API-compatible and 1.5.0has been stable since late 2022.
Chrome's Partial CCT requires the
CustomTabsIntent.Builderto beconstructed with an established
CustomTabsSessionto render as abottom sheet on the first invocation. Otherwise Chrome's first launch
falls back to full-screen and only subsequent launches get the sheet.
The library already exposes
prefetchConfiguration— callingit on the screen that will later invoke
authorize()is sufficient(verified on a real Pixel emulator running Chrome 147). The PR's docs
update mentions this explicitly.
ASWebAuthenticationSessionalreadypresents as a modal sheet by design. The new option is silently ignored
on iOS (guarded by
Platform.OS === 'android').responsibility is validation + correct positional argument to the
native bridge, both covered by spec additions.
Backward compatibility
null(no behavior change when unset).meaning shifted.
crash, falls back to current full-screen Custom Tab behavior.
PR
A PR with the full implementation (TS types, JS validation, Java wiring,
README/docs, 5 new spec tests covering validation + bridge passthrough,
plus a changeset for the minor bump) is ready and will be linked below.
Happy to iterate on the API shape (e.g. accept an object form, expose
resizeBehavior, etc.) if maintainers prefer a different surface.