Understanding the VirtualKeyboard API
The VirtualKeyboard API is a web platform feature that gives developers granular control over how their applications respond to on-screen virtual keyboards. As mobile web usage continues to dominate, ensuring a seamless user experience when virtual keyboards appear and disappear becomes essential.
Why the VirtualKeyboard API Matters
Mobile users frequently encounter frustrating experiences when virtual keyboards disrupt page layouts, hide important content, or interfere with form interactions. Traditional browser behavior automatically resizes the viewport when keyboards appear, but this one-size-fits-all approach often fails for complex applications.
The VirtualKeyboard API addresses these challenges by providing:
- Programmatic control over keyboard visibility
- Geometry information for precise layout calculations
- CSS integration through environment variables
- Flexible policies for contenteditable elements
Three Main Components
The API consists of three interconnected parts that work together to give you comprehensive control:
- VirtualKeyboard Interface - JavaScript API accessed via
navigator.virtualKeyboard - CSS Environment Variables - Declarative keyboard geometry information accessible through the
env()function - virtualkeyboardpolicy Attribute - HTML control for keyboard behavior on contenteditable elements
When a virtual keyboard appears, browsers traditionally resize the viewport to make space for it. This automatic resizing can cause content to reflow unexpectedly, scroll positions to shift, and layout calculations to become unreliable. The VirtualKeyboard API, as defined by the W3C VirtualKeyboard Specification, gives you the ability to opt out of this automatic behavior and implement custom adaptation strategies that better suit your application's needs.
As noted in the Chrome for Developers guide, this API is particularly valuable for applications that require precise control over their visual layout, such as games, canvas-based editors, and complex form interfaces. For mobile-first web applications, mastering this API is essential for delivering professional-grade user experiences that compete with native mobile apps.
Everything you need to build keyboard-aware web applications
Keyboard Geometry Access
Retrieve exact position and dimensions of the virtual keyboard using boundingRect property. Enable precise content positioning and layout calculations.
Custom Overlay Control
Prevent automatic viewport resizing with overlaysContent. Take full control over how your layout responds to keyboard appearance.
Geometry Change Events
Listen for geometrychange events to react to keyboard show/hide events. Implement reactive layout updates that adapt instantly.
Programmatic Keyboard Control
Show or hide the keyboard on demand with show() and hide() methods. Build custom keyboard interaction patterns.
CSS Environment Variables
Access keyboard insets through CSS env() function. Create declarative layouts that adapt without JavaScript overhead.
ContentEditable Policy
Control keyboard behavior on contenteditable elements with virtualkeyboardpolicy attribute. Decouple focus from keyboard visibility.
The VirtualKeyboard Interface
The VirtualKeyboard interface is your primary gateway to controlling on-screen keyboard behavior. Accessed through navigator.virtualKeyboard, this interface provides all the properties and methods needed to build keyboard-aware web applications.
Accessing the API
Before using the VirtualKeyboard API, you should verify browser support and ensure the application is running in a secure context:
// Feature detection
if ('virtualKeyboard' in navigator) {
// VirtualKeyboard API is available
const virtualKeyboard = navigator.virtualKeyboard;
// Configure overlay behavior
virtualKeyboard.overlaysContent = true;
// Set up event listener
virtualKeyboard.addEventListener('geometrychange', handleKeyboardChange);
} else {
// Implement fallback strategy for unsupported browsers
console.warn('VirtualKeyboard API not supported');
}
Secure Context Requirement
The VirtualKeyboard API is only available in secure contexts (HTTPS). This security requirement prevents malicious scripts from exploiting keyboard geometry information for fingerprinting or other attacks. When developing locally, you may need to use localhost or configure HTTPS. The MDN Web Docs emphasize that this API requires a secure context as a fundamental security measure.
Browser Compatibility
The API has varying support across browsers. Chrome and Edge (Chromium-based) provide the most complete implementation, while Firefox and Safari have limited or no support at this time.
| Browser | Support Level | Notes |
|---|---|---|
| Chrome | Full | Complete implementation with overlaysContent |
| Edge | Full | Chromium-based, full support |
| Firefox | Limited | Experimental or partial support |
| Safari | Limited | Limited or no support |
Interface Properties
The VirtualKeyboard interface inherits from EventTarget and adds several key properties and methods:
- boundingRect (DOMRect, read-only) - Returns the current keyboard geometry
- overlaysContent (boolean) - Controls whether keyboard overlays content or triggers viewport resize
- show() (method) - Programmatically shows the keyboard
- hide() (method) - Programmatically hides the keyboard
- ongeometrychange (event handler) - Fires when keyboard geometry changes
The geometrychange event fires not only when the keyboard appears or disappears but also when it resizes--such as when switching between keyboard layouts or modes. When implementing cross-browser compatibility, always include fallback mechanisms for browsers that don't support this API.
The overlaysContent Property
The overlaysContent property is a boolean that controls whether the browser automatically resizes the viewport when the virtual keyboard appears. Setting this to true tells the browser to keep the viewport size fixed and allow the keyboard to overlay your content.
How overlaysContent Works
By default, browsers resize the viewport when the virtual keyboard appears, which can cause:
- Content to be squeezed or reflowed unexpectedly
- Scroll position changes that disorient users
- Layout recalculations that impact performance
- Fixed positioning elements to jump around
When you set overlaysContent = true, you opt out of this automatic behavior and take responsibility for adapting your layout. This gives you precise control but requires implementing keyboard-aware layouts in your application.
┌─────────────────────────────────────────┐
│ Viewport (Default) │
│ ┌─────────────────────────────┐ │
│ │ Page Content │ │
│ │ │ │
│ └─────────────────────────────┘ │
│ ┌─────────────────────────────┐ │
│ │ Virtual Keyboard │ │
│ │ (Viewport shrinks) │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ Viewport (overlaysContent) │
│ ┌─────────────────────────────┐ │
│ │ Page Content │ │
│ │ │ │
│ │ │ │
│ └─────────────────────────────┘ │
│ ┌─────────────────────────────┐ │
│ │ Virtual Keyboard │ │
│ │ (Overlays content) │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────────┘
Implementation Example
// Opt out of automatic keyboard handling
if ('virtualKeyboard' in navigator) {
navigator.virtualKeyboard.overlaysContent = true;
// Now you can respond to keyboard geometry changes
navigator.virtualKeyboard.addEventListener('geometrychange', handleKeyboardChange);
}
Use Cases for overlaysContent
- Canvas-based applications that need precise control over rendering areas
- Custom scrolling containers that shouldn't be affected by viewport changes
- Games and interactive experiences with fixed coordinate systems
- Dual-screen applications that need consistent behavior across screens
- Complex layouts where automatic resizing causes visual issues
Setting this property early in your application initialization ensures consistent behavior as users interact with input elements throughout the experience. This pattern is particularly important when building responsive web applications that must work flawlessly across all device types.
1// Reading keyboard geometry with boundingRect2const keyboardRect = navigator.virtualKeyboard.boundingRect;3 4console.log('Keyboard position:', {5 x: keyboardRect.x, // X coordinate from left edge6 y: keyboardRect.y, // Y coordinate from top edge7 width: keyboardRect.width, // Keyboard width in pixels8 height: keyboardRect.height // Keyboard height in pixels9});10 11// Using the geometry for layout calculations12function positionInputAboveKeyboard() {13 const vk = navigator.virtualKeyboard;14 const rect = vk.boundingRect;15 16 // Calculate where the input should be positioned17 const inputBottom = window.innerHeight - rect.height - 20; // 20px padding18 19 return inputBottom;20}The boundingRect Property
The boundingRect property returns a DOMRect representing the intersection of the virtual keyboard with the document's viewport. This read-only property provides the exact geometry of the keyboard, enabling precise layout calculations.
Understanding the DOMRect
The DOMRect object contains four key values:
| Property | Description |
|---|---|
x | Horizontal distance from the left edge of the viewport |
y | Vertical distance from the top edge of the viewport |
width | Width of the keyboard in pixels |
height | Height of the keyboard in pixels |
Practical Applications
Use boundingRect to:
- Position form inputs above the keyboard
- Calculate available viewport space
- Scroll elements into view when keyboard appears
- Adjust fixed-position elements dynamically
- Create layout-aware components
Important Considerations
The DOMRect values are relative to the viewport, not the document. This means scrolling the page doesn't affect the coordinates. When the keyboard is hidden, all values return to zero.
When boundingRect Returns Zero Values
When the virtual keyboard is hidden, the
boundingRectproperty returns a DOMRect with all values set to zero (x: 0, y: 0, width: 0, height: 0). This is expected behavior and can be used to detect the keyboard state:const rect = navigator.virtualKeyboard.boundingRect; const isKeyboardVisible = rect.height > 0; if (isKeyboardVisible) { // Keyboard is visible, adapt layout } else { // Keyboard is hidden, normal layout }
Understanding this behavior is crucial when building interactive web applications that must gracefully handle keyboard transitions without jarring visual disruptions.
1// Listening for keyboard geometry changes2if ('virtualKeyboard' in navigator) {3 // Add event listener for geometry changes4 navigator.virtualKeyboard.addEventListener('geometrychange', (event) => {5 const { x, y, width, height } = event.target.boundingRect;6 7 if (height > 0) {8 console.log('Keyboard appeared:', width, 'x', height);9 // Adapt layout for keyboard visibility10 adaptLayoutForKeyboard(height);11 } else {12 console.log('Keyboard hidden');13 // Restore normal layout14 restoreNormalLayout();15 }16 });17}18 19// Using ongeometrychange property20navigator.virtualKeyboard.ongeometrychange = (event) => {21 console.log('Geometry changed');22};The geometrychange Event
The geometrychange event fires whenever the virtual keyboard's geometry changes. This includes:
- Keyboard appearing (transition from hidden to visible)
- Keyboard disappearing (transition from visible to hidden)
- Keyboard resizing (height changes due to different keyboard modes)
Event Handling Patterns
You can add event listeners in two ways:
- Using addEventListener: Allows multiple handlers and proper cleanup
- Using ongeometrychange property: Single handler assignment
Reacting to Geometry Changes
When the event fires, the boundingRect property already contains the new geometry. Use this information to:
- Update CSS custom properties
- Adjust element positioning
- Scroll content into view
- Trigger animations
- Save/restore scroll positions
Performance Considerations
The geometrychange event can fire frequently during keyboard transitions. Optimize your handlers by:
- Using
requestAnimationFramefor visual updates - Debouncing rapid changes
- Minimizing DOM reads and writes
- Batching layout changes together
Avoiding Layout Thrashing
When handling geometry changes, read the
boundingRectproperty only once and cache the values for all subsequent calculations:navigator.virtualKeyboard.addEventListener('geometrychange', () => { // Read once and use for all calculations const rect = navigator.virtualKeyboard.boundingRect; // Apply all layout changes using cached values element.style.bottom = `${rect.height}px`; scrollToElement(element); updateAriaAttributes(rect); });Reading
boundingRectmultiple times or interleaving reads with style writes can cause layout thrashing and impact performance, especially on lower-powered mobile devices.
Implementing efficient event handlers is essential for maintaining smooth performance in mobile-optimized web applications. The geometrychange event, when used properly, enables seamless user experiences that adapt instantly to keyboard state changes.
1// Programmatic keyboard control2 3// Show the keyboard (requires sticky activation and manual policy)4async function showKeyboard() {5 try {6 await navigator.virtualKeyboard.show();7 console.log('Keyboard shown successfully');8 } catch (error) {9 console.error('Failed to show keyboard:', error);10 }11}12 13// Hide the keyboard14function hideKeyboard() {15 navigator.virtualKeyboard.hide();16}17 18// Example with contenteditable element19const editor = document.getElementById('editor');20 21editor.addEventListener('dblclick', async () => {22 // Double-click to show keyboard on custom event23 await navigator.virtualKeyboard.show();24});25 26editor.addEventListener('focusout', () => {27 // Hide when losing focus28 navigator.virtualKeyboard.hide();29});The show() and hide() Methods
The show() and hide() methods provide programmatic control over keyboard visibility. These methods allow you to decouple keyboard appearance from focus events.
Method Requirements
Both methods require:
- Secure context (HTTPS)
- Sticky activation (user gesture such as click or tap)
- virtualkeyboardpolicy="manual" on the target element
When to Use These Methods
Use show() and hide() when you need:
- Keyboard on custom gestures (double-tap, long-press)
- Delayed keyboard appearance
- Explicit control over keyboard state
- Custom input workflows
Error Handling
The show() method may reject if:
- There's no sticky activation (not called from a user gesture)
- The focused element doesn't have virtualkeyboardpolicy="manual"
- The platform doesn't support programmatic control
Platform-Specific Restrictions
The
show()andhide()methods behave differently across platforms:
- Chrome/Edge: Full support with proper error handling
- Firefox: May not support programmatic control
- iOS Safari: Not supported; keyboard shows on focus regardless
Always implement proper error handling and feature detection:
async function tryShowKeyboard(element) { if (!('virtualKeyboard' in navigator)) { console.warn('VirtualKeyboard API not available'); return; } try { await navigator.virtualKeyboard.show(); } catch (error) { console.log('Programmatic keyboard control not supported'); // Fallback: element.focus() will still show keyboard element.focus(); } }
These programmatic control methods open up possibilities for building sophisticated AI-powered chat interfaces and conversational applications where keyboard behavior needs to be carefully orchestrated as part of the user experience.
CSS Environment Variables
The VirtualKeyboard API exposes six CSS environment variables that provide keyboard geometry information directly in your stylesheets. These variables enable declarative layout adaptations without requiring JavaScript.
Available Environment Variables
| Variable | Description | Fallback |
|---|---|---|
keyboard-inset-top | Distance from viewport top to keyboard top | 0px |
keyboard-inset-right | Distance from viewport right to keyboard right | 0px |
keyboard-inset-bottom | Distance from viewport bottom to keyboard bottom | 0px |
keyboard-inset-left | Distance from viewport left to keyboard left | 0px |
keyboard-inset-width | Width of the keyboard | 0px |
keyboard-inset-height | Height of the keyboard | 0px |
Using env() Function
The environment variables work with the CSS env() function, which accepts a fallback value:
.keyboard-area {
height: env(keyboard-inset-height, 0px);
}
The fallback value is used when the keyboard is hidden or the API is unavailable.
CSS Grid Layout Example
body {
display: grid;
height: 100vh;
margin: 0;
grid-template:
"content" 1fr
"input" auto
"keyboard" env(keyboard-inset-height, 0px);
}
#messages {
grid-area: content;
overflow-y: auto;
}
#input {
grid-area: input;
}
#keyboard-spacer {
grid-area: keyboard;
}
This layout automatically adjusts to reserve space for the keyboard when it appears, creating a seamless user experience without requiring any JavaScript.
1/* Using keyboard insets with media queries */2 3/* When keyboard is visible */4@media (env(keyboard-inset-bottom) > 0) {5 .input-container {6 margin-bottom: calc(env(keyboard-inset-height) + 20px);7 padding: 20px;8 background: white;9 }10 11 .submit-button {12 position: fixed;13 bottom: calc(env(keyboard-inset-height) + 20px);14 }15}16 17/* Responsive adjustments based on keyboard height */18@media (env(keyboard-inset-height) >= 300px) {19 .keyboard-shortcuts {20 display: none; /* Hide shortcuts when keyboard is tall */21 }22}23 24/* Foldable device with keyboard on one screen */25@media (env(keyboard-inset-right) <= env(fold-left)) {26 .search-box {27 margin-bottom: calc(env(keyboard-inset-height) + 20px);28 }29}Responsive Keyboard Adaptations
The environment variables work seamlessly with CSS media queries, enabling responsive layouts that adapt to keyboard state:
@media (env(keyboard-inset-bottom) > 0) {
/* Styles when keyboard is visible */
}
This approach allows you to:
- Show/hide elements based on keyboard state
- Adjust spacing and padding
- Reposition fixed elements
- Change layout grid configurations
- Optimize for different keyboard sizes
Combining with CSS Custom Properties
Use keyboard insets with CSS custom properties for dynamic theming and complex adaptations:
:root {
--keyboard-offset: env(keyboard-inset-height, 0px);
--input-padding: 20px;
}
.input-wrapper {
padding-bottom: calc(var(--keyboard-offset) + var(--input-padding));
transition: padding-bottom 0.2s ease;
}
/* Update custom property when keyboard changes */
@media (env(keyboard-inset-bottom) > 0) {
:root {
--keyboard-offset: env(keyboard-inset-height);
--input-padding: 30px;
}
}
Combining with Other Features
Use keyboard insets with:
- CSS Grid for complex layout structures
- Flexbox for component-level adaptations
- Container queries for component-based design
- CSS custom properties for dynamic theming
This combination creates a powerful system for building responsive, keyboard-aware interfaces that adapt seamlessly to user input methods. These techniques are essential for modern responsive web design that prioritizes mobile user experience.
The virtualkeyboardpolicy Attribute
The virtualkeyboardpolicy attribute controls how the virtual keyboard behaves on contenteditable elements. This attribute allows you to decouple focus events from keyboard visibility.
Attribute Values
| Value | Behavior |
|---|---|
"auto" | Default. Keyboard appears on focus/tap |
"manual" | Decouples focus from keyboard. Requires programmatic control |
Basic Usage
<!-- Default behavior -->
<div contenteditable virtualkeyboardpolicy="auto" id="auto-editor">
Keyboard shows automatically on focus
</div>
<!-- Manual control -->
<div contenteditable virtualkeyboardpolicy="manual" id="manual-editor">
Keyboard only shows when explicitly requested
</div>
Manual Control Pattern
When using "manual" policy, you need to handle keyboard visibility yourself:
const editor = document.getElementById('manual-editor');
// Show keyboard on double-click
editor.addEventListener('dblclick', () => {
navigator.virtualKeyboard.show();
});
// Keep keyboard visible while editing
editor.addEventListener('focus', () => {
navigator.virtualKeyboard.show();
});
// Optional: hide on specific actions
editor.addEventListener('blur', () => {
navigator.virtualKeyboard.hide();
});
Use Cases for Manual Policy
- Custom input workflows that require pre-processing before keyboard appears
- Rich text editors with special input modes or tool palettes
- Games that use contenteditable for text entry
- Demo experiences that want to control focus and keyboard behavior
The manual policy gives you complete control over when the keyboard appears, enabling creative interaction patterns that go beyond standard form input behavior. This level of control is particularly valuable when building SEO-optimized forms and interactive content that must provide exceptional user experiences to improve engagement metrics.
Implementation Patterns
Pattern 1: Chat Application Layout
Chat applications need to keep messages visible while positioning the input field above the keyboard:
// Setup VirtualKeyboard for chat
function setupChatKeyboard() {
if (!('virtualKeyboard' in navigator)) return;
navigator.virtualKeyboard.overlaysContent = true;
navigator.virtualKeyboard.addEventListener('geometrychange', () => {
const { height } = navigator.virtualKeyboard.boundingRect;
const messages = document.getElementById('messages');
const inputArea = document.getElementById('input-area');
if (height > 0) {
// Keyboard is visible - adjust padding
inputArea.style.paddingBottom = `${height + 20}px`;
// Scroll to bottom of messages
scrollToMessageBottom();
} else {
// Keyboard hidden - reset
inputArea.style.paddingBottom = '20px';
}
});
}
Pattern 2: Form with Validation
Forms that need to keep submit buttons accessible when the keyboard appears:
function setupFormKeyboard() {
if (!('virtualKeyboard' in navigator)) return;
navigator.virtualKeyboard.overlaysContent = true;
navigator.virtualKeyboard.addEventListener('geometrychange', () => {
const { height } = navigator.virtualKeyboard.boundingRect;
const submitBtn = document.getElementById('submit');
if (height > 0) {
// Position button above keyboard
submitBtn.style.position = 'fixed';
submitBtn.style.bottom = `${height + 20}px`;
} else {
// Reset to normal positioning
submitBtn.style.position = 'static';
}
});
}
Pattern 3: Canvas-Based Application
Canvas applications can use geometry changes to redraw content:
function setupCanvasKeyboard() {
navigator.virtualKeyboard.overlaysContent = true;
navigator.virtualKeyboard.addEventListener('geometrychange', () => {
requestAnimationFrame(() => {
const { x, y, width, height } = navigator.virtualKeyboard.boundingRect;
// Reposition active cell above keyboard
repositionActiveCell();
// Redraw canvas
canvas.draw();
});
});
}
These patterns demonstrate the versatility of the VirtualKeyboard API for different application types, from communication tools to creative applications. Each pattern can be adapted and combined to create sophisticated user experiences in advanced web applications.
Best Practices for Performance
Optimize Event Handlers
The geometrychange event can fire rapidly during keyboard transitions. Follow these performance guidelines:
Do:
// Batch layout changes
navigator.virtualKeyboard.addEventListener('geometrychange', () => {
requestAnimationFrame(() => {
// Read geometry once
const { height } = navigator.virtualKeyboard.boundingRect;
// Apply all changes
applyLayoutChanges(height);
});
});
Avoid:
// Reading inside animation frame repeatedly
navigator.virtualKeyboard.addEventListener('geometrychange', () => {
requestAnimationFrame(() => {
// Reading multiple times causes layout thrashing
const h1 = navigator.virtualKeyboard.boundingRect.height;
const h2 = navigator.virtualKeyboard.boundingRect.height;
const h3 = navigator.virtualKeyboard.boundingRect.height;
});
});
Feature Detection and Fallbacks
Always detect support and provide fallbacks:
function setupKeyboardHandling() {
if ('virtualKeyboard' in navigator) {
// Use VirtualKeyboard API
navigator.virtualKeyboard.overlaysContent = true;
setupAdvancedKeyboardHandling();
} else {
// Use traditional focus-based approach
setupFallbackKeyboardHandling();
}
}
Accessibility Considerations
- Maintain visible focus indicators when customizing keyboard behavior
- Ensure interactive elements remain keyboard accessible
- Test with screen readers and assistive technologies
- Provide visual feedback for keyboard state changes
- Don't break standard keyboard navigation patterns
Implementing proper accessibility ensures that all users can effectively interact with your application, regardless of their input method or assistive technology requirements. These performance and accessibility best practices are fundamental to delivering high-quality web applications that serve all users effectively.
Troubleshooting Common Issues
Issue: API Not Available
Problem: navigator.virtualKeyboard is undefined
Solutions:
- Verify the page is served over HTTPS
- Check browser support (Chrome/Edge only)
- Ensure not in an iframe (unless same-origin)
Issue: Keyboard Geometry Returns Zeros
Problem: boundingRect returns all zeros
Solutions:
- Keyboard may be hidden (this is expected behavior)
- Check if
overlaysContentis set totrue - Verify the keyboard actually appeared on screen
Issue: show() Method Fails
Problem: show() throws an error or does nothing
Solutions:
- Requires sticky activation (user gesture)
- Element must have
virtualkeyboardpolicy="manual" - Check browser console for error messages
- Verify platform supports programmatic control
Issue: Layout Not Updating
Problem: Layout doesn't change when keyboard appears
Solutions:
- Verify
overlaysContentis set before adding event listeners - Check that event handlers are actually firing
- Ensure CSS changes are being applied (check computed styles)
- Test on actual mobile device, not desktop emulation
Debugging Tips
// Add comprehensive logging
if ('virtualKeyboard' in navigator) {
console.log('VirtualKeyboard API available');
navigator.virtualKeyboard.addEventListener('geometrychange', (event) => {
console.log('Geometry changed');
console.log('boundingRect:', event.target.boundingRect);
console.log('overlaysContent:', navigator.virtualKeyboard.overlaysContent);
});
console.log('Initial overlaysContent:', navigator.virtualKeyboard.overlaysContent);
}
Using detailed logging during development helps identify issues early and ensures your keyboard handling logic is working as expected. When troubleshooting complex keyboard interactions, systematic debugging approaches help isolate problems quickly and lead to more robust implementations.