Fullscreen: The Right Way to Go Immersive
Building immersive, app-like experiences on the web means getting fullscreen right. But "going fullscreen" can mean several different things: the browser's dedicated Fullscreen API, launching via an installed shortcut, or simply hiding the browser's UI. The method you choose dramatically changes the user's experience, and—in a world where the modern app model supports "installed web apps"—the choice matters more than ever.
Requesting the Browser's Fullscreen API
The primary tool for programmatic fullscreen is the standard Fullscreen API: element.requestFullscreen(), document.exitFullscreen(), and the document.fullscreenElement property. Support isn't uniform across platforms—iOS Safari still lacks a fullscreen API for web content—and you'll find prefixed versions in Chrome, Firefox, and IE. Firefox, meanwhile, uses cancelFullScreen() to exit.
Once the API grants the fullscreen state, the user loses the browser's native controls entirely: there is no Back or Forward button, no Refresh, no address bar. Your experience must provide its own way out. This makes it critical to react to the fullscreen state with CSS selectors, so your site can adapt its UI to the hidden chrome.
Beware a vendor-prefix trap. While the API looks simple, the production code gets surprisingly tangled:
<button id="goFS">Go fullscreen</button>
<script>
var goFS = document.getElementById('goFS');
goFS.addEventListener(
'click',
function () {
document.body.requestFullscreen();
},
false,
);
</script>
The real implementation requires careful handling of the multiple vendor prefixes and the differences in API naming:
function toggleFullScreen() {
var doc = window.document;
var docEl = doc.documentElement;
var requestFullScreen =
docEl.requestFullscreen ||
docEl.mozRequestFullScreen ||
docEl.webkitRequestFullScreen ||
docEl.msRequestFullscreen;
var cancelFullScreen =
doc.exitFullscreen ||
doc.mozCancelFullScreen ||
doc.webkitExitFullscreen ||
doc.msExitFullscreen;
if (
!doc.fullscreenElement &&
!doc.mozFullScreenElement &&
!doc.webkitFullscreenElement &&
!doc.msFullscreenElement
) {
requestFullScreen.call(docEl);
} else {
cancelFullScreen.call(doc);
}
}
This complexity is what libraries like Screenfull.js solve, wrapping the FF and blink/webkit variations into one consistent interface.
Working with the Right Element
Calling requestFullscreen() on the body element seems intuitive, but it doesn't work well. On WebKit/Blink engines, a fullscreen body gets unexpectely shrunk to fit its content. On Gecko this won't happen. Your best bet is to use the document element instead:
document.documentElement.requestFullscreen();
Video If You're Not Careful
Video fullscreen works the same way, but you have to account for the controls. Call requestFullscreen() on the video element to leverage the browser's own player controls:
<video id="videoElement"></video>
<button id="goFS">Go Fullscreen</button>
<script>
var goFS = document.getElementById('goFS');
goFS.addEventListener(
'click',
function () {
var videoElement = document.getElementById('videoElement');
videoElement.requestFullscreen();
},
false,
);
</script>
If you're rendering your own control buttons (e.g., you're skipping the controls attribute), putting fullscreen on just the video leaves those controls vanished from view. Wrapping everything in a container is safer:
<div id="container">
<video></video>
<div>
<button>Play</button>
<button>Stop</button>
<button id="goFS">Go fullscreen</button>
</div>
</div>
<script>
var goFS = document.getElementById('goFS');
goFS.addEventListener(
'click',
function () {
var container = document.getElementById('container');
container.requestFullscreen();
},
false,
);
</script>
This container approach also lets you change the controls based on fullscreen state using the CSS pseudo selector, like hiding the goFS button after you enter fullscreen mode:
<style>
#goFS:-webkit-full-screen #goFS {
display: none;
}
#goFS:-moz-full-screen #goFS {
display: none;
}
#goFS:-ms-fullscreen #goFS {
display: none;
}
#goFS:fullscreen #goFS {
display: none;
}
</style>
Keep the environment in mind: with no browser URL in sight, plan for some backup navigation. At a minimum, add a link back to the starting point and a way to close any dialogs or travel backwards through your flow.
Fullscreen via Home Screen or App Icon
You can't auto-launch a fullscreen site just by typing a URL. Browsers block that to ensure the user's gesture is needed, so no mis-used fullscreen on every page load. But installing a page explicitly is a user out-of-the-app signal—and all the major mobile platforms let you configure that launch experience.
iOS
Since the first iPhone, iOS home-screen web apps have been configurable with meta tags. Full-screen mode is opt-in via the apple-mobile-web-app-capable tag set to yes. When running fullscreen, the read-only property window.navigator.standalone returns true in your page, which you can use to tailor your UI for the installed experience.
<meta name="apple-mobile-web-app-capable" content="yes" />
Chrome on Android: Two Paths
Chrome on Android supports a similar meta tag trigger, which launches the web app in full-screen "app mode" from an Add to Home Screen shortcut. Setting the page up this way conflates two separate ideas: presence as a launcher icon and what that app should look like when it launches.
<meta name="mobile-web-app-capable" content="yes" />
A better route is the modern Web App Manifest—a plain JSON file supported by Chrome, Opera, Firefox, and Samsung. With it, you control the app name (short_name, name), its icon set, and the launch parameters (start_url, display, orientation). One file applies those values consistently on every installed shortcut, all in a progressive enhancement way.
With a manifest deployed, tell your pages about it with a <link> reference:
<link rel="manifest" href="https://web.dev/manifest.json" />
Here is a condensed example manifest just highlighting the essentials
{
"short_name": "Kinlan's Amaze App",
"name": "Kinlan's Amazing Application ++",
"icons": [
{
"src": "launcher-icon-4x.png",
"sizes": "192x192",
"type": "image/png"
}
],
"start_url": "/index.html",
"display": "standalone",
"orientation": "landscape"
}
Remember that installing an app is a strong intent signal. When a user adds your site to the home screen, don't land them on some generic landing page—send them straight into your product's useful state. On a sign-in app, direct them to the login flow.
Display Modes by Context
What's appropriate fullscreen for one type of site is a hostile experience for another.
Utility apps almost always feel more integrated when they run standalone, with no browser bar. Games usually want the very same treatment but with a locked orientation, e.g., your game stays in portrait or always in landscape.
"display": "standalone"
For a portrait-only vertical runner, lock it:
"display": "fullscreen",
"orientation": "portrait"
But for an X-Com-style puzzler, your game always uses landscape. The same strategy handles that case.
"display": "fullscreen",
"orientation": "landscape"
Even content-heavy news sites benefit from a manifest. A great experience there varies; some want the familiar "browser tab" chrome:
"display": "browser"
Or you can kill the web-like chrome entirely with display: standalone, matching how dedicated news apps feel in their main flows.
"display": "standalone"
Auto-hiding the Address Bar: The Fallback
When you don't have an install, a third path exists: you try to fake fullscreen and auto-hide the browser bar—simulating "full bleed" view while you can still use the History API normally.
window.scrollTo(0, 1);
The premise is reasonable, but this functionality isn't standardized and support is limited. You have to work around its quirkiest bits: browsers sometimes restore the scroll or specific positions on navigating back, and these edge cases are a constant problem. window.scrollTo overrides that behavior. To handle this, you'd have to save the page scroll position frequently to localStorage, and correctly manage edge cases like when the page is open in multiple windows at once. Those complexities usually make the Fullscreen API or the manifest a cleaner path forward.
Designing for the edges of the screen
Going fullscreen changes more than the viewport. It removes the user's familiar navigation cues, which means the experience you build needs to carry more of the weight. A few UX rules keep that from becoming a dead end.
Don’t rely on browser navigation controls
On iOS, there is no hardware back button or refresh gesture. If you take over the whole screen, you must build a clear path forward and back inside your own UI. To do that, you first need to know what mode you are running in, and the detection method differs per platform.
iOS
On iOS, the navigator.standalone boolean reports whether the user launched the page from the home screen.
if (navigator.standalone == true) {
// My app is installed and therefore fullscreen
}
Chrome, Opera and Samsung Internet
When launched as an installed app, Chrome does not run in a true fullscreen state: document.fullscreenElement returns null and the CSS fullscreen selectors do not apply. When a user requests fullscreen via a gesture on your page, however, the standard fullscreen APIs and CSS pseudo selector behave normally, letting you react to the state change in your styles:
selector:-webkit-full-screen {
display: block; // displays the element only when in fullscreen
}
selector {
display: none; // hides the element when not in fullscreen mode
}
For installed launches, the display-mode media query reflects the mode declared in the Web App Manifest. A pure fullscreen manifest yields:
@media (display-mode: fullscreen) {
}
A standalone manifest, in contrast, matches the standalone query:
@media (display-mode: standalone) {
}
Firefox
Firefox offers the standard fullscreen APIs and the CSS pseudo selector both for gesture-requested fullscreen and for fullscreen app launches:
selector:-moz-full-screen {
display: block; // hides the element when not in fullscreen mode
}
selector {
display: none; // hides the element when not in fullscreen mode
}
Internet Explorer
IE’s CSS pseudo class is spelled without a hyphen but otherwise acts the same as in Chrome and Firefox:
selector:-ms-fullscreen {
display: block;
}
selector {
display: none; // hides the element when not in fullscreen mode
}
The specification
The CSS spec matches IE’s hyphen-less spelling:
selector:fullscreen {
display: block;
}
selector {
display: none; // hides the element when not in fullscreen mode
}
Keep the user inside the fullscreen experience
Browsers deliberately make fullscreen easy to escape, so you cannot build a multi-page site that holds the user in fullscreen. Navigation events break out of the state:
- Setting
window.locationto a new URL exits fullscreen. - Following an external link exits fullscreen.
- Calling the
navigator.pushStateAPI also exits fullscreen.
Two approaches let you keep a persistent fullscreen feel: use installable web app mechanisms, or manage application state in the URL fragment. By updating window.location = "#somestate" and listening to window.onhashchange, you get the browser’s history stack for free—hardware back buttons work, and you can expose a simple programmatic back button via the history API:
window.history.go(-1);
Let the user choose fullscreen
Intercepting the first touch to call requestFullscreen() is a poor first impression. It also risks hitting a permission prompt—browsers may eventually ask the user whether this site is allowed to take over the screen. If you want an app-like launch, prefer the native install experiences for each platform instead of hijacking the first interaction.
Don’t nag about installing to the home screen
If your fullscreen experience depends on users installing the app, ask politely and sparingly:
- Keep the prompt discreet, such as a banner or footer.
- Once dismissed, do not show it again.
- Wait for a positive interaction on the first visit before asking; a new user is unlikely to install immediately.
- Frequent visitors who have not installed are unlikely to change their minds—stop asking.
Conclusion
There is no single standardized fullscreen API implemented everywhere yet, but combining the installable-web-app path with the detection techniques above lets you build experiences that reliably use the full screen across major clients.



