One Logo Pool Ball
wwwwwwwwwwwwwwwwwww

Screen Orientation

Read, lock, and watch the interface orientation

One.ScreenOrientation reads the orientation your app’s window actually uses, requests a portrait or landscape lock while a screen needs one, and reports changes. These values describe the interface, which can differ from the physical device orientation when rotation lock is on.

On iOS it reads effectiveGeometry and requests geometry updates on the active window scene. On Android it derives the value from display rotation and locks through the activity’s requested orientation. On web it reads screen.orientation and locks through the browser API.

Usage

import { useEffect } from 'react'
import { One } from 'one'
function useLandscape() {
useEffect(() => {
One.ScreenOrientation.lock('landscapeLeft')
return () => {
void One.ScreenOrientation.unlock()
}
}, [])
}

Locking while a screen mounts and unlocking on unmount keeps the preference scoped to the screen that needs it.

How it works

getOrientation returns portrait, portrait upside down, landscape left, landscape right, or unknown. lock takes the four concrete orientations or landscape, which lets the platform pick a side, and resolves with the orientation once the window reports a match. unlock releases the request and restores manifest behavior, resolving with the current orientation. The change listener fires only when the effective interface orientation changes; remove it when its owner unmounts.

On iOS the request goes to the active window scene on the main thread. Without an active scene the call rejects with E_SCREEN_ORIENTATION_SCENE. The manifest bounds what iOS may use: set native.app.orientation to allow both portrait and landscape, since a portrait-only or landscape-only manifest limits runtime requests, and an orientation outside the manifest or window rejects with E_SCREEN_ORIENTATION_UNSUPPORTED. If the scene never reaches the requested orientation, the call rejects with E_SCREEN_ORIENTATION_TIMEOUT after ten seconds.

On Android a runtime lock overrides the manifest, so a manifest conflict never rejects; unsupported fires only on a display that does not rotate. The lock verifies against display rotation for ten seconds before timing out. On web, primary portrait and landscape map to portrait and landscape left, secondary values to the upside-down and right variants, and changes follow the orientation change event. Browser locks reject when unsupported, blocked by fullscreen policy, or unreached, and unlock rejects when the browser lacks it. Server rendering resolves unknown for reads, locks, and unlocks, and listeners return an inert remover.

API reference

MethodsTypeDescription
getOrientation() => Promise<ScreenOrientationValue>The current interface orientation.
() => Promise<ScreenOrientationValue>The current interface orientation.
lock(orientation: ScreenOrientationLock) => Promise<ScreenOrientationValue>Request an orientation. Resolves once it matches.
(orientation: ScreenOrientationLock) => Promise<ScreenOrientationValue>Request an orientation. Resolves once it matches.
unlock() => Promise<ScreenOrientationValue>Release the lock. Resolves with the current value.
() => Promise<ScreenOrientationValue>Release the lock. Resolves with the current value.
addChangeListener(onChange: (orientation: ScreenOrientationValue) => void) => () => voidHear effective changes. Returns a remover.
(onChange: (orientation: ScreenOrientationValue) => void) => () => voidHear effective changes. Returns a remover.

The value is unknown, portrait, portraitUpsideDown, landscapeLeft, or landscapeRight. The lock adds landscape for either landscape side.

Platform differences

iOSAndroidWeb
SourceWindow sceneDisplay rotationScreen API
LockScene geometryActivity requestBrowser lock
ManifestBounds requestsOverriddenNo manifest
TimeoutTen secondsTen secondsBrowser decides

Limits and gotchas

Examples

Show a landscape video screen

import { useEffect } from 'react'
import { One } from 'one'
function useVideoOrientation() {
useEffect(() => {
async function prefer() {
try {
await One.ScreenOrientation.lock('landscape')
} catch {
// stay in whatever orientation the device allows
}
}
prefer()
return () => {
void One.ScreenOrientation.unlock()
}
}, [])
}

landscape lets the platform pick the side that matches how the viewer holds the device. A rejected lock falls back to the current orientation instead of breaking the screen.

Mirror the orientation in UI

import { useEffect, useState } from 'react'
import { Text } from 'react-native'
import { One } from 'one'
function OrientationLabel() {
const [orientation, setOrientation] = useState('unknown')
useEffect(() => {
One.ScreenOrientation.getOrientation().then(setOrientation)
return One.ScreenOrientation.addChangeListener(setOrientation)
}, [])
return <Text>Interface: {orientation}</Text>
}

The initial read seeds the state before the first change arrives, and the listener’s remover doubles as the effect cleanup. The listener fires only on effective changes, so renders stay quiet otherwise.

Edit this page on GitHub.