Feature detection that survives browser updates

Use existence checks, secure-context checks and a real operation to decide whether an API is usable in the current page.

Finding an API name on window or navigator is the first step in feature detection. It is not the last. A clipboard interface can exist while a write fails, a storage interface can exist while a transaction runs out of space, and a graphics interface can exist while context creation returns no usable result.

A dependable application checks the capability at the point where it is needed, attempts an appropriate operation and handles failure. This approach survives browser updates better than a list of browser names because it asks what the current context can actually do.

Use three layers of evidence

The browser capabilities checker reports whether selected API entry points are exposed and whether the page is a secure context. It does not request device access or exercise every interface. Read its count as exposed entry points, rather than a percentage of web-platform compatibility.

The first layer is interface presence. The second is the context and policy that govern its use. The third is the result of a real operation with realistic input. Skipping the last two layers turns a useful preliminary check into an unreliable promise.

For example, the presence of navigator.clipboard does not show that an asynchronous clipboard write will succeed after an arbitrary delay. User activation, browser rules and document focus may matter. The application must handle the returned promise and provide a usable alternative if it rejects.

Prefer the capability over the browser label

A user-agent string can help support identify a browser family, but it is a weak basis for deciding which code path to execute. Browsers change, versions can be reduced or modified, and related browsers can expose different features under policy or platform constraints.

Test the actual property or method you need. If a feature has optional parts, test those parts separately. Finding a constructor is not evidence that every extension, codec or option associated with it works. A small successful operation gives more specific evidence than a broad family name.

async function copyText(text) {
  if (!navigator.clipboard?.writeText) return false;
  try {
    await navigator.clipboard.writeText(text);
    return true;
  } catch {
    return false;
  }
}

This example reports whether the write completed. Call it from a suitable user action and keep a visible, selectable copy of the text as a fallback. Do not show a copied confirmation merely because the method existed or because the promise was created.

Check where the code runs

A feature can be available on HTTPS and unavailable on ordinary HTTP. Some local development origins are treated as potentially trustworthy, so localhost success does not establish that an insecure staging hostname will work. Inspect isSecureContext in the actual document and test the production deployment path.

Frames introduce another boundary. A top-level page may support a feature while an embedded document is restricted by Permissions Policy, sandboxing or origin rules. Run the detection in the frame that performs the operation. A result gathered in the parent window can describe the wrong execution context.

Workers also expose a different environment from window. A property available in a page may be absent in a particular worker type. Feature detection should live close to the code path that uses it, with a clear fallback appropriate to that environment.

Presence checks and useful operation checks
CapabilityPreliminary evidenceMore specific evidence
ClipboardwriteText is exposedA user-triggered write resolves
WebGLGraphics interfaces existThe required context and extensions are obtained
StorageStorage APIs are presentA representative transaction completes
Media playbackA type receives a support hintThe actual asset loads and plays
File pickerThe picker method existsThe user's selected operation succeeds or is cancelled cleanly

Design the fallback before the error occurs

Suppose an export button offers a native file picker when supported. Its fallback can download a file through an ordinary link or object URL. Both paths should produce the same document bytes and a clear filename. The user should not lose the prepared report because the preferred save interface was unavailable.

Distinguish cancellation from failure. Closing a picker can be an intentional user choice, not a broken browser. Do not automatically launch another disruptive dialog after cancellation. Keep the report available and let the user choose another action.

For expensive operations, preserve input and explain the next step. A graphics preview may fall back to a static image, while an offline editor may offer export when durable storage is unavailable. The fallback should protect the user's work and fit the feature, rather than display a generic unsupported-browser page.

Test failures as deliberately as success

Create a short matrix covering a supported context, an unsupported interface, a denied permission, invalid input and a runtime rejection. These failures can produce different outcomes. A missing interface should not cause a property-access exception, and a rejected promise should not become an unhandled error.

Use controlled test doubles for narrow application behaviour when appropriate, then confirm important flows in real browsers. A mocked successful promise cannot reveal a browser's activation rules. A real browser test without a controlled failure path can miss whether the interface recovers correctly when permission is denied.

Include navigation and backgrounding where relevant. An operation that begins after the page loses focus can behave differently. Record whether the test ran in a normal profile, a private context or an embedded frame, since policy and persistence can change independently of API presence.

Avoid treating a capability as permanent state

Some observations are stable for a document, while others can change during its lifetime. Permission settings can change, devices can disconnect and graphics contexts can be lost. Store capability hints if they simplify rendering, but handle each operation's actual result.

Do not retain an old browser-support decision across upgrades without rechecking it. A long-lived account preference that says clipboard unavailable can become wrong when the user changes devices. Capabilities belong to the current execution context, not to the person's account identity.

When logging failures, include the operation and error category rather than dumping the entire browser object. A report such as clipboard write rejected after focus changed is more useful than an unexplained capability count. It also avoids collecting unrelated browser characteristics.

Keep the initial page useful

Render ordinary navigation and readable content in HTML. Enhance the page with capabilities where they add value. A documentation page should not become blank because a storage API failed while saving a theme choice. A download link should remain discoverable if a richer save workflow cannot initialise.

Feature detection can also guide loading. Do not download a large graphics application merely to discover that its required context cannot be created. A small preliminary check can offer the appropriate path, while the application still performs final operation checks after loading.

Keep the user's intent in the interface. Name the action, such as download report or choose file, instead of exposing implementation labels like invoke native picker. Technical details belong in diagnostics when they help explain a failure, not in every routine product control.

Handle two clicks while an operation is pending

Capability detection does not prevent an application race. If a user clicks an export button twice, two supported operations may start and complete in a different order. Decide whether to disable the button temporarily, cancel the earlier operation or allow both with clear filenames.

Keep the pending state tied to the actual promise. Restore the control in a finally block so both success and failure leave the interface usable. If the page supports cancellation, distinguish a cancelled request from a completed export when updating its status text.

Test this with a deliberately slow operation. A fast local machine can hide duplicate-click problems that appear on a phone or under load. The feature may be fully supported while the surrounding interface still mishandles its lifecycle.

Frequently asked questions

Is a high capability count better?

The count only reflects the selected entry points exposed to this page. It is not a benchmark, security grade or complete compatibility measure. A browser may intentionally restrict a feature that you do not need. Evaluate the specific workflow instead of ranking browsers by the number of properties in a diagnostic list.

Should I polyfill every missing API?

A polyfill can reproduce some behaviours, but it cannot grant permissions, create unavailable hardware or bypass browser security boundaries. Review what the replacement actually implements and whether it changes performance or data handling. A straightforward fallback may be more reliable than an incomplete imitation of a powerful API.

Why does the feature work in the console?

The console experiment may run in a different frame, at a different time or after an interaction that changed state. Compare the exact document, input and call timing. Move the minimal test into the application's real event path before concluding that the application and console are exercising identical conditions.

Can I infer privacy mode from a missing feature?

A missing or restricted feature can have several causes, including policy, browser version and platform support. This checker does not identify private browsing mode. Treat the result as a capability observation and provide a usable fallback without attempting to classify the user's private context.

What belongs in a compatibility bug report?

Include the required API and method, the document origin, secure-context status, whether it is framed, the exact operation and the returned error. Add a minimal reproduction and the browser build. This gives maintainers a concrete path to test instead of asking them to interpret a broad unsupported label.

Sources and further reading

50 useful tools · ↑ ↓ to navigate · Enter to open · Esc to close