Screen Orientation
Reading screen orientation works in every browser. Locking it -- forcing portrait or landscape -- is a different tier. Here is the API, what values it returns, and the one orientation to never use.
Most APIs are either available or not. Screen orientation is a rare case where it depends which part of the API you mean.
Reading the current orientation is green tier -- it works everywhere, on every phone and tablet, including iOS. Locking the orientation so users cannot rotate out of it is light-green -- stable in Chromium, still experimental in Safari on iOS and iPad.
Those two operations look like they belong together. They sit in different compatibility tiers.
Reading the Orientation
console.log(screen.orientation.type)
// "portrait-primary" | "portrait-secondary"
// "landscape-primary" | "landscape-secondary"portrait-primary is the phone held upright as normal. landscape-primary is rotated 90° to the right. The -secondary variants are the opposite rotations -- landscape-secondary is rotated left, portrait-secondary is upside down.
Listen for changes:
screen.orientation.addEventListener('change', () => {
console.log(screen.orientation.type)
})This fires every time the user rotates the device. It works in every browser, including mobile Safari, with no permission required.
Locking the Orientation
screen.orientation.lock() prevents the OS from rotating the content when the device rotates. It takes an orientation string and returns a Promise.
async function lockToPortrait() {
try {
await screen.orientation.lock('portrait')
} catch (err) {
// Not supported, or not in a context that allows locking
}
}
function releaseLock() {
screen.orientation.unlock()
}Valid lock values: 'portrait', 'landscape', 'portrait-primary', 'portrait-secondary', 'landscape-primary', 'landscape-secondary', 'natural', 'any'.
ExpandScreen Orientation API: read orientation vs lock orientation, values, and the portrait-secondary warning
Do not use portrait-secondary. That is upside-down. The user's home button, gesture strip, and camera are now at the top of the screen. They have no way to know they are in a locked inverted state. Lock to 'portrait' or 'portrait-primary' instead.
Where Locking Works
The lock API is available in Chromium today. Safari on iOS and iPadOS marks it as experimental -- users can enable it in the Safari experimental features settings, which means it will become stable at some point.
Even on Chromium, locking typically requires the page to be in full-screen mode or installed as a PWA. The browser chrome (the address bar, back buttons) competes with the lock, so the OS usually ignores the lock when the browser UI is visible. If you lock orientation for a game, do it after calling document.documentElement.requestFullscreen().
Before the Lock API: CSS Media Queries
The CSS-only workaround still works everywhere and is appropriate when you cannot rely on lock support:
@media (orientation: portrait) and (max-width: 768px) {
.rotate-prompt {
display: flex;
}
.app-content {
display: none;
}
}Show a "please rotate your device" overlay in portrait mode for a landscape-only experience. It is not as clean as a real lock -- users can still rotate back -- but it is reliable across all browsers today.
Screen orientation deals with the device as a whole. The next topic goes one level down: individual finger contacts. Touch events and pointer events are two separate APIs for the same physical action -- and understanding why both exist is the interesting part.
The Essentials
screen.orientation.typereports current orientation as one of four string values. Green tier -- works everywhere.screen.orientation.lock()returns a Promise and requires full-screen or PWA context on most platforms. Light-green -- experimental in Safari iOS.- Never lock to
portrait-secondary(upside down). Users lose access to home and gesture controls. - Locking is the only API solution. For cross-browser today, use a CSS media query to show a "rotate your device" prompt instead.
Further Reading and Watching
- Lock Screen Orientation In HTML Javascript (YouTube) -- short walkthrough of
screen.orientation.lock()and how to combine it with full-screen mode - Screen Orientation API -- MDN -- full specification with browser support table and all valid lock type values
Keep reading