●DEADLINE — Five days remain before Google Play requires Android 16 (API level 36). From August 31 it applies to new apps and updates alike●EXTENSION — If you qualify, you can request an extension through November 1 via a Play Console form, but the request itself has to be filed before the deadline●EXPO — Expo SDK 57 reached 57.0.9 with React Native 0.86.2. SDK 57 is primarily the jump from React Native 0.85 to 0.86●HERMES — The Hermes V1 memory regression introduced in SDK 56 has been fixed, which matters most for apps pulling in react-native-worklets or react-native-reanimated●IOS — The seventh iOS 27 developer beta shipped on August 24. The public release lands in September and finally lets you reply to an individual message from an Android sender●RORK — Rork Max generates native Swift and compiles on a cloud Mac fleet, targeting iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and iMessage●DEADLINE — Five days remain before Google Play requires Android 16 (API level 36). From August 31 it applies to new apps and updates alike●EXTENSION — If you qualify, you can request an extension through November 1 via a Play Console form, but the request itself has to be filed before the deadline●EXPO — Expo SDK 57 reached 57.0.9 with React Native 0.86.2. SDK 57 is primarily the jump from React Native 0.85 to 0.86●HERMES — The Hermes V1 memory regression introduced in SDK 56 has been fixed, which matters most for apps pulling in react-native-worklets or react-native-reanimated●IOS — The seventh iOS 27 developer beta shipped on August 24. The public release lands in September and finally lets you reply to an individual message from an Android sender●RORK — Rork Max generates native Swift and compiles on a cloud Mac fleet, targeting iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and iMessage
What Android 16 ignores on tablets, and why app.json gives you no way out
Targeting API level 36 means screenOrientation is ignored on displays at least 600dp wide. Here is how to check your project without a tablet, why app.json cannot express a fix, and what the opt-out property actually restores.
I opened a tablet emulator only after noticing that August 31 was five days out.
The wallpaper apps I run as an indie developer are all built around a portrait grid. Bumping targetSdkVersion to 36 for the Play deadline was already done. What was left was checking what changes once you are actually on 36.
I rotated the emulator, and the grid I had locked to portrait spread sideways. I had not touched the manifest.
Eight values stop being honored
For apps targeting Android 16 (API level 36), orientation, aspect ratio, and resizability restrictions are ignored on displays whose smallest width is at least 600dp. That covers tablets, the inner displays of large-screen foldables, and desktop windowing.
The part I missed on first read was what the table leaves out. unspecified, locked, nosensor, sensor, user, and fullSensor are not on the ignore list.
There are exceptions. Displays smaller than sw600dp — most phones, and the outer displays of foldables — are unaffected. Apps flagged as games through android:appCategory are excluded. So are cases where the user has explicitly opted into your app's default behavior through the device's aspect ratio settings.
Which means "I checked on my phone" proves nothing here. That is exactly why I did not notice until I opened an emulator.
Predictive back is a separate Android 16 default change with a different opt-out mechanism. I covered that one in the predictive back decision for Android 16. Treating both as one task tends to leave both half-done.
Check your project before you touch a device
Before borrowing a tablet, read your own configuration. In a Rork or Expo project, the orientation value lives in two places: app.json, and the AndroidManifest.xml produced by prebuild.
Those two can disagree. If you edit the manifest by hand after prebuild, app.json no longer tells you the truth. If you regenerate native directories every time, app.json is authoritative.
I wrote a small script that reads both and reports the value that actually applies.
// check-orientation.mjs — run with: node check-orientation.mjsimport { readFileSync, existsSync } from 'node:fs';// screenOrientation values Android 16 (API 36) ignores at sw600dp and aboveconst IGNORED = new Set([ 'portrait', 'landscape', 'reversePortrait', 'reverseLandscape', 'sensorPortrait', 'sensorLandscape', 'userPortrait', 'userLandscape',]);function fromAppJson(path) { if (!existsSync(path)) return null; const json = JSON.parse(readFileSync(path, 'utf8')); const cfg = json.expo ?? json; // app.json only accepts 'default' | 'portrait' | 'landscape' // 'default' becomes android:screenOrientation="unspecified" const o = cfg.orientation; if (!o) return null; return o === 'default' ? 'unspecified' : o;}function fromManifest(path) { if (!existsSync(path)) return null; const xml = readFileSync(path, 'utf8'); // if you hand-edit after prebuild, this is the real value const m = xml.match(/android:screenOrientation="([^"]+)"/); return m ? m[1] : null;}const manifestPath = 'android/app/src/main/AndroidManifest.xml';const manifestValue = fromManifest(manifestPath);const appJsonValue = fromAppJson('app.json') ?? fromAppJson('app.config.json');const effective = manifestValue ?? appJsonValue ?? 'unspecified (not set)';const source = manifestValue ? manifestPath : (appJsonValue ? 'app.json' : 'not set anywhere');console.log(`screenOrientation = ${effective} (source: ${source})`);if (manifestValue && appJsonValue && manifestValue !== appJsonValue) { console.log(`Warning: app.json says ${appJsonValue}, manifest says ${manifestValue}`);}if (IGNORED.has(effective)) { console.log('Affected: this value is ignored on displays at or above sw600dp'); process.exitCode = 1;} else { console.log('Not on the ignore list');}
With only "orientation": "portrait" in app.json, you get:
screenOrientation = portrait (source: app.json)
Affected: this value is ignored on displays at or above sw600dp
On a prebuilt project where the manifest has been rewritten to unspecified:
screenOrientation = unspecified (source: android/app/src/main/AndroidManifest.xml)
Warning: app.json says portrait, manifest says unspecified
Not on the ignore list
It sets an exit code, so if you maintain several apps you can loop over directories and get the affected list in one pass. That is what I did before touching any hardware.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦Determine whether your project is affected by Android 16's orientation override without buying or borrowing a tablet
✦Know before you submit that the opt-out property does not bring your portrait lock back
✦Move a setting from app.json into a config plugin without confusing property with meta-data
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
portrait passes through untouched and lands as android:screenOrientation="portrait" — the first entry on the ignore list.
So of the three values available to you, two are ignored on large screens and the third means giving up on orientation entirely. There is no middle setting. Values that are not on the ignore list, such as locked or nosensor, simply cannot be expressed from app.json.
This is where I think most solo developers will stall. Staring at the config file, there is nothing to change.
The opt-out property does not restore your portrait lock
There is an official escape hatch: declare the android.window.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY property in your manifest.
Here is the part I had backwards.
That property does not lock the display orientation, and it does not prevent rotation on large displays. Android's documentation states this directly for apps targeting API level 36 or higher.
What comes back is the resizability side of the restrictions. "Add one line before August 31 and stay portrait" is not what this property does.
I understood it the wrong way round until I had written the config plugin and checked the result on the emulator. The property was in the manifest and the app still rotated, so I went looking for a bug in my plugin. There wasn't one.
The escape hatch also expires. For apps targeting API level 37, the opt-out is removed entirely and the restrictions are always ignored at sw600dp and above. Play requires API 36 as of August 2026 and API 37 as of August 2027, so the runway is about a year.
Target API level
Affected devices
Opt-out
36 (Android 16)
Large screens, smallest width 600dp or more
Allowed (but no orientation lock)
37 (Android 17)
Same
Not allowed
Building that table changed my plan. Bending an implementation around a mechanism that disappears in a year costs more than fixing the screens that break in landscape.
Adding the property through a config plugin
Restoring the resizability side alone is still a legitimate call, particularly if layout work will not land before the deadline. That takes a config plugin, because Expo offers no way to declare a <property> element from app.json.
To scope it to a single activity, swap getMainApplicationOrThrow for getMainActivityOrThrow and push the same object. Whether you go app-wide or per-screen depends on how many of your screens actually break in landscape.
One note for TypeScript users: the ManifestApplication type in @expo/config-plugins has no property field. It has meta-data and activity, but not property. It works at runtime because xml2js serializes whatever you hand it, so you will need a type extension or a cast on that one line.
Writing it as meta-data fails silently
This is the part I would flag hardest.
@expo/config-plugins exports a helper called addMetaDataItemToMainApplication. Search for a way to add an entry to the manifest and it is the first thing you find. Pass it the property name and it accepts it happily.
It accepts it, but the output is not a <property> element.
The first line is correct. The second is what addMetaDataItemToMainApplication gives you. Both came out of one manifest I generated locally to put them side by side.
Android treats <property> and <meta-data> as different things. Write a property value as meta-data and the build succeeds, no warning appears, and nothing throws on device. It just does nothing.
Misconfigurations that pass silently are the expensive kind to diagnose later. I have lost enough hours to that category that I now verify the generated manifest by eye immediately after adding anything to it.
# after prebuild, confirm it really came out as a property elementnpx expo prebuild --platform android --no-installgrep -n "PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY" \ android/app/src/main/AndroidManifest.xml
If the matched line starts with <property, you are fine. If it starts with <meta-data, you wrote it the wrong way.
Where I drew the line
Three things decide this for you.
Only large screens are affected. If your app is phone-only in practice, this is separable from the August 31 deadline itself. But "I never designed for tablets" and "tablets cannot install it" are different statements. If you distribute to tablets, you are affected.
The opt-out does not restore orientation. The property does not keep tablets in portrait. If screens break in landscape, you end up fixing them regardless.
The runway is one year. Once the opt-out disappears at API 37, the choice disappears with it.
I chose to fix the screens that genuinely break in landscape and to skip the property entirely. The wallpaper grid just needed its column count derived from available width. The detail screen needed a max width on the image and nothing else.
If you cannot scope the fix before August 31, the call changes: ship the property to clear the deadline and fix layouts in the next release. Even then, verify before submission on the assumption that the app will be in landscape.
Run the check script once at the root of your project. If it prints portrait, you are affected — and from there, count how many screens actually break in landscape before deciding anything else.
I did not treat this change as my problem until I rotated an emulator. If that saves someone else the same detour, it was worth writing down.
Share
Thank You for Reading
Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.