Fullscreen experiences are most compelling when the content benefits from maximum focus: a web game, a video player, a slide deck, a data dashboard, or any interface where browser chrome and surrounding apps become distractions. The Fullscreen API is the browser feature that enables this by letting a page present a specific DOM element (and its descendants) in fullscreen and then return to normal windowed mode when appropriate.
What the API actually does
Rather than being a standalone object, the Fullscreen API adds capabilities onto existing web platform types, primarily Document and Element. In practice, that means:
- You ask for fullscreen on a particular element
- The browser enters fullscreen if allowed, and you can later request to exit.
- You can query the document to learn whether anything is currently fullscreen.
This element-based model is useful because it aligns fullscreen with the thing the user cares about (a <video>, a <canvas>, a container for a presentation) without forcing everything else on the page to be treated as the fullscreen target.
Key concepts
Entering and exiting fullscreen is request-based
Going fullscreen is initiated by requesting it on an element, while leaving fullscreen is requested via the document. Both are designed as asynchronous operations (they return promises), reflecting the fact that the user agent may need to perform checks, prompt the user, or refuse the request.
State is observable via fullscreenElement
To know whether fullscreen is active—and which element is currently presented—use the document’s fullscreenElement. If it’s null, nothing is in fullscreen.
Capability can be gated with fullscreenEnabled
Not every environment will allow fullscreen. The API provides a way to check whether fullscreen engagement is even possible in the current context. This is particularly relevant for embedded contexts (e.g., iframes) or restrictive environments.
There are events for lifecycle and failure
Fullscreen transitions and failures generate dedicated events:
- fullscreenchange when an element transitions into or out of fullscreen
- fullscreenerror when something goes wrong attempting to switch modes
These events are what you use to keep UI in sync—update buttons, show hints, pause animations, or restore layout after exit.
Permissions and embedding: why fullscreen sometimes “doesn’t work”
Fullscreen isn’t just a technical capability; it’s also controlled by browser policy. The feature is governed by Permissions Policy under the name "fullscreen", with a default allowlist of "self". In plain terms: fullscreen is generally allowed for top-level pages, and for same-origin nested contexts by default—but embedding scenarios can restrict it depending on how the page is framed and which policies are applied.
If you’ve ever seen fullscreen behave inconsistently across an embedded widget versus the same page opened directly, policy controls are often the reason.
UX realities: users can (and will) exit on their own
A good fullscreen implementation assumes the user remains in control:
- Users can exit fullscreen via ESC (and commonly F11), so your UI should tell them this option exists.
- Fullscreen can also end due to normal user navigation—switching tabs, changing pages, or app-switching (e.g., Alt-Tab) can cause fullscreen to exit. Plan for this by treating fullscreen as a mode that may end at any time, and make your interface resilient to that.
Fullscreen API Demo
Fullscreen API Demo
Click “Enter fullscreen” (or press Enter) to toggle fullscreen for the stage element.
The log will show fullscreenchange events and any errors.
The Stage Element
Try toggling fullscreen. Some browsers require a user gesture (click/keypress), and some platforms (notably iOS) have limitations.
Checking support…
We request fullscreen on a specific element, and the document tracks the current fullscreen element.”
:root {
--bg: #0b1220;
--panel: #111a2e;
--text: #e8eefc;
--muted: #a9b5d6;
--accent: #3e8c60;
--danger: #ff5a67;
--border: rgba(255, 255, 255, 0.12);
}
body {
margin: 0;
font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
color: var(--text);
background: radial-gradient(1200px 700px at 20% 10%, #16254a, var(--bg));
}
.wrap {
max-width: 980px;
margin: 0 auto;
padding: 28px 16px 40px;
display: grid;
gap: 16px;
}
header h1 {
margin: 0 0 6px; font-size: 22px;
}
header p {
margin: 0; color: var(--muted); line-height: 1.4;
}
.row {
display: grid;
grid-template-columns: 1.2fr 0.8fr;
gap: 16px;
}
@media (max-width: 860px) {
.row {
grid-template-columns: 1fr;
}
}
.card {
background: color-mix(in srgb, var(--panel) 88%, transparent);
border: 1px solid var(--border);
border-radius: 14px;
padding: 14px;
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.25);
}
/* This is the element we’ll toggle into fullscreen */
#stage {
border-radius: 12px;
border: 1px solid var(--border);
overflow: hidden;
min-height: 320px;
display: grid;
place-items: center;
position: relative;
/* Visually interesting, but simple (no external assets needed) */
background: radial-gradient(600px 240px at 30% 25%, rgba(62, 140, 96, 0.35), transparent),
radial-gradient(500px 260px at 80% 70%, rgba(120, 180, 255, 0.25), transparent),
linear-gradient(135deg, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0.02));
}
#stage .hint {
text-align: center;
padding: 18px;
max-width: 52ch;
}
#stage kbd {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
padding: 2px 8px;
border-radius: 8px;
border: 1px solid var(--border);
background: rgba(0, 0, 0, 0.25);
}
.controls {
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: center;
margin-top: 12px;
}
button {
appearance: none;
border: 1px solid var(--border);
border-radius: 12px;
padding: 10px 12px;
font-weight: 650;
color: var(--text);
background: rgba(255, 255, 255, 0.06);
cursor: pointer;
}
button.primary {
background: color-mix(in srgb, var(--accent) 40%, rgba(255, 255, 255, 0.06));
border-color: color-mix(in srgb, var(--accent) 60%, var(--border));
}
button:disabled {
opacity: 0.55;
cursor: not-allowed;
}
.pill {
font-size: 12px;
color: var(--muted);
border: 1px solid var(--border);
border-radius: 999px;
padding: 6px 10px;
background: rgba(0, 0, 0, 0.18);
}
.status {
display: grid;
gap: 10px;
}
.status .line {
display: flex;
justify-content: space-between;
gap: 10px;
border-bottom: 1px dashed rgba(255, 255, 255, 0.14);
padding: 8px 0;
}
.status .label {
color: var(--muted);
}
.status .value {
font-weight: 650;
}
#log {
height: 190px;
overflow: auto;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
font-size: 12px;
line-height: 1.35;
padding: 10px;
border-radius: 12px;
border: 1px solid var(--border);
background: rgba(0, 0, 0, 0.28);
white-space: pre-wrap;
}
.error {
color: color-mix(in srgb, var(--danger) 82%, white);
font-weight: 650;
}
/* Optional: show a visible outline while in fullscreen */
:fullscreen #stage {
outline: 6px solid rgba(62, 140, 96, 0.55);
outline-offset: -6px;
border-radius: 0;
/* many demos remove rounding in fullscreen */
}
/**
* Fullscreen API quick demo
*
* - Element.requestFullscreen() to enter fullscreen (user gesture required in most browsers)
* - Document.exitFullscreen() to leave fullscreen
* - document.fullscreenElement tells you *which* element is fullscreen right now
* - fullscreenchange fires on document when entering/exiting succeeds
* - fullscreenerror fires when it fails (permissions, platform limitations, etc.)
*/
const stage = document.querySelector('#stage');
const toggleBtn = document.querySelector('#toggleBtn');
const exitBtn = document.querySelector('#exitBtn');
const clearLogBtn = document.querySelector('#clearLogBtn');
const enabledVal = document.querySelector('#enabledVal');
const elementVal = document.querySelector('#elementVal');
const eventVal = document.querySelector('#eventVal');
const logEl = document.querySelector('#log');
const supportPill = document.querySelector('#supportPill');
const timeStamp = () => {
return new Date().toLocaleTimeString();
};
const log = (message, { type = 'info' } = {}) => {
const prefix = `[${timeStamp()}] `;
const line = prefix + message;
const div = document.createElement('div');
div.textContent = line;
if (type === 'error') div.className = 'error';
logEl.appendChild(div);
logEl.scrollTop = logEl.scrollHeight;
};
const updateUI = (lastEvent = '—') => {
enabledVal.textContent = String(document.fullscreenEnabled);
elementVal.textContent = document.fullscreenElement
? `#${document.fullscreenElement.id || document.fullscreenElement.tagName.toLowerCase()}`
: 'null';
eventVal.textContent = lastEvent;
const isFullscreen = Boolean(document.fullscreenElement);
toggleBtn.textContent = isFullscreen ? 'Exit fullscreen' : 'Enter fullscreen';
// If the browser indicates fullscreen is not enabled, disable the controls.
// (Note: Some browsers may still allow fullscreen in certain contexts even if this is false,
// but generally it’s a good signal.)
const supported =
typeof stage.requestFullscreen === 'function' &&
typeof document.exitFullscreen === 'function';
toggleBtn.disabled = !supported;
exitBtn.disabled = !supported || !isFullscreen;
};
const detectSupport = () => {
const supported =
typeof stage.requestFullscreen === 'function' &&
typeof document.exitFullscreen === 'function';
if (!supported) {
supportPill.textContent = 'Fullscreen API not supported here';
supportPill.style.borderColor = 'rgba(255,90,103,0.55)';
log('Fullscreen API not supported in this browser/context.', {
type: 'error'
});
} else {
supportPill.textContent = 'Fullscreen API supported';
supportPill.style.borderColor = 'rgba(62,140,96,0.55)';
log('Fullscreen API supported. Try clicking the button or pressing Enter.');
}
};
// Toggle function that handles both enter and exit paths.
const toggleFullscreen = async () => {
try {
// If something is already fullscreen, exit.
if (document.fullscreenElement) {
await document.exitFullscreen();
// UI will also update via fullscreenchange, but we can log intent here.
log('Requested exitFullscreen()');
return;
}
// Otherwise, request fullscreen on our stage element.
// navigationUI is optional; supported in some browsers.
// Remove the options object if you want maximum compatibility.
await stage.requestFullscreen({ navigationUI: 'hide' });
log('Requested stage.requestFullscreen()');
} catch (err) {
// Common reasons:
// - Not initiated by a user gesture (click/keypress)
// - Platform limitation (e.g., iOS Safari)
// - Browser policy / permissions
log(`Fullscreen request failed: ${err.name}: ${err.message}`, {
type: 'error'
});
updateUI('error');
}
};
// --- Event listeners ---
document.addEventListener('fullscreenchange', () => {
log(
`fullscreenchange → fullscreenElement is now ${document.fullscreenElement ? 'SET' : 'null'}`
);
updateUI('fullscreenchange');
});
document.addEventListener('fullscreenerror', () => {
// Some browsers provide little/no extra info; still useful to surface.
log('fullscreenerror → The browser reported a fullscreen error event.', {
type: 'error'
});
updateUI('fullscreenerror');
});
// Click controls
toggleBtn.addEventListener('click', toggleFullscreen);
exitBtn.addEventListener('click', async () => {
try {
if (document.fullscreenElement) {
await document.exitFullscreen();
log('Requested exitFullscreen() via Exit button');
}
} catch (err) {
log(`Exit failed: ${err.name}: ${err.message}`, { type: 'error' });
}
});
clearLogBtn.addEventListener('click', () => {
logEl.textContent = '';
log('Log cleared.');
updateUI('cleared');
});
// Keyboard demo: press Enter to toggle fullscreen
window.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
// Prevent accidental form submission if you embed this in a bigger page
e.preventDefault();
toggleFullscreen();
}
// Optional: let Escape exit fullscreen (most browsers already do this by default)
if (e.key === 'Escape' && document.fullscreenElement) {
// Let the browser handle it; we just log user intent.
log('Escape pressed (browser typically exits fullscreen automatically).');
}
});
// Initialize
detectSupport();
updateUI('init');
// Give focus to stage for natural keyboard interactions
stage.focus();
When the Fullscreen API shines
Use fullscreen when it meaningfully improves the experience:
- Media: video players, live streams, playback with fewer distractions
- Games & interactive canvases: maximizing immersion and input space
- Presentations & learning: slide viewers, diagrams, interactive lessons
- Focused tools: drawing apps, editors, dashboards in “presentation mode”
The best fullscreen experiences feel intentional: a clear entry point, clear exit instructions, and UI that stays coherent whether fullscreen is granted, denied, or ended unexpectedly.

