Feature Detection vs User-Agent Detection
A practical guide to feature detection and User-Agent detection in JavaScript, including browser capability checks, navigator.userAgent, User-Agent Client Hints, compatibility decisions and common mistakes.
Web applications sometimes need to make decisions based on the environment in which they are running. A feature may only work when a particular browser API is available, a layout may require a certain CSS capability, or a server may need to know something about the client making an HTTP request. This raises an important question: should the application detect the actual feature it needs, or should it identify the browser and operating system?
These approaches are usually called feature detection and User-Agent detection. They solve different problems. Feature detection asks whether a specific capability is available. User-Agent detection tries to identify the software or device making the request.
For application functionality, feature detection is usually the more direct approach because it checks the condition that actually matters. User-Agent information still has legitimate uses, especially for analytics, debugging, compatibility workarounds and server-side behavior, but it should not automatically be treated as proof that a particular browser feature exists.
What Is Feature Detection?
Feature detection is the practice of checking whether a specific browser capability is available before using it. Instead of asking which browser the visitor uses, the application asks whether the required API, property, method or CSS feature exists.
if ("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition(showPosition);
} else {
showLocationFallback();
}The code does not care whether the visitor is using Chrome, Firefox, Safari or another browser. It only cares about the capability required by the application.
This distinction is important because browser identity and browser capabilities are not the same thing. Two browsers can expose different capabilities, and the same browser can behave differently depending on its version, operating system, security context, permissions or configuration.
What Is User-Agent Detection?
User-Agent detection identifies information about the client from a User-Agent string or related browser metadata. In a browser, JavaScript can access navigator.userAgent. On the server, the User-Agent is normally received as an HTTP request header.
console.log(navigator.userAgent);A User-Agent string can contain information that helps identify the browser family, browser version, operating system and sometimes device-related details. However, these strings are not a reliable representation of every capability available to the browser.
Mozilla/5.0 (...) AppleWebKit/537.36 (...) Chrome/140.0.0.0 Safari/537.36The exact contents vary between browsers and versions, and modern User-Agent strings often contain historical compatibility tokens. This is one reason manually parsing them can become complicated quickly.
The Core Difference
| Feature detection | User-Agent detection |
|---|---|
| Checks a specific capability | Identifies the client or browser |
| Usually performed at runtime | Can be performed in the browser or on the server |
| Directly answers whether a feature is available | Provides information from client identification metadata |
| Usually preferred for feature-dependent behavior | Useful for analytics, diagnostics and specific compatibility cases |
| Less dependent on browser naming | Requires interpretation of browser metadata |
A simple way to remember the distinction is: feature detection asks 'Can I do this?', while User-Agent detection asks 'What does this client claim to be?'
Why Feature Detection Is Usually Preferred
Suppose an application wants to use a particular browser API. The useful information is whether that API exists and can be used in the current environment.
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js");
}A User-Agent check would require maintaining a list of browsers and versions that are believed to support service workers. That list can become outdated and may not account for unusual configurations.
Feature detection avoids that assumption by testing the capability directly.
Feature Detection With typeof
The typeof operator is useful when checking whether a global function or object exists.
if (typeof IntersectionObserver !== "undefined") {
const observer = new IntersectionObserver(callback);
}This is useful for globals that may not exist at all in some environments.
Feature Detection With the in Operator
The in operator can determine whether a property exists on an object or in its prototype chain.
if ("clipboard" in navigator) {
// Clipboard API may be available.
}However, the existence of a property does not always guarantee that every operation will succeed. APIs can also depend on permissions, secure contexts, user gestures or other runtime conditions.
Feature Detection Is Not Always Enough
Feature detection is powerful, but it should not be interpreted as a guarantee that an operation will succeed in every situation.
Some browser APIs are conditionally available. A browser may expose an API but require HTTPS, user permission or a specific document context. Other features can be available but restricted by browser settings or security policies.
if ("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition(
showPosition,
showError
);
}The capability check tells us that the API exists. It does not guarantee that the user will grant permission or that the request will succeed. Good feature detection is therefore usually combined with proper error handling.
Detecting CSS Features
Feature detection is not limited to JavaScript APIs. CSS provides its own mechanisms for checking whether a browser understands a particular CSS feature.
@supports (display: grid) {
.layout {
display: grid;
}
}JavaScript can also use CSS.supports() when a runtime decision is necessary.
if (CSS.supports("display", "grid")) {
document.documentElement.classList.add("supports-grid");
}This is generally preferable to maintaining a list of browsers that are assumed to support CSS Grid.
Feature Detection and Progressive Enhancement
Feature detection works especially well with progressive enhancement. The application can provide a basic experience and then enable an enhanced version when the required capability is available.
if ("IntersectionObserver" in window) {
enableLazyLoading();
} else {
loadImagesNormally();
}The important principle is that the fallback should still provide an appropriate experience. Feature detection tells the application when an enhancement can be activated; progressive enhancement defines how the application behaves when it cannot.
Why User-Agent Detection Is Complicated
At first glance, User-Agent detection seems simple. An application can read a string and search for browser names such as Chrome, Firefox or Safari. In practice, browser identification is considerably more complicated.
Browser User-Agent strings have accumulated compatibility tokens over many years. A string may mention technologies or browser identifiers that do not correspond directly to the actual browser product.
const userAgent = navigator.userAgent;
if (userAgent.includes("Chrome")) {
// This is not a reliable way to identify Chrome.
}For example, Chromium-based browsers can contain Chrome-related tokens while still being different browser products. Similar compatibility behavior exists elsewhere in the browser ecosystem.
Why Browser Name Does Not Tell You Capability
Imagine that an application has a rule saying that Chrome version 100 or newer supports a particular feature. Even if the version information is parsed correctly, the rule may still be an indirect approximation.
The application does not actually need to know whether the browser is Chrome 100. It needs to know whether the capability it wants to use is available.
A browser may disable a feature, expose it behind a permission, implement it differently or behave differently under a specific platform configuration. A browser version is therefore often an imperfect proxy for the condition that actually matters.
When User-Agent Detection Is Useful
Despite its limitations, User-Agent information is not obsolete. There are legitimate situations where identifying the client is useful.
- Analytics and traffic analysis
- Debugging browser-specific problems
- Investigating compatibility reports
- Server-side logging
- Security monitoring and diagnostics
- Applying a narrowly targeted browser workaround
- Understanding the client environment when no direct feature check is available
The key is to use User-Agent information for a problem that actually concerns client identification rather than using it as a substitute for capability detection.
A Legitimate Browser-Specific Workaround
There are cases where a known browser-specific bug requires a targeted workaround. In such a situation, browser identification can be justified.
For example, suppose a specific browser release has a documented rendering bug that affects one component. The application may need a workaround for that browser even though the underlying CSS feature exists.
User-Agent Detection in Server-Side Applications
User-Agent detection can be more useful on the server because the server cannot directly execute browser feature checks before sending the initial response.
A server can inspect the HTTP User-Agent header and use the information for logging, analytics, diagnostics or carefully chosen response behavior.
GET /products HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (...) Chrome/140.0.0.0 Safari/537.36Even here, developers should avoid assuming that the User-Agent proves support for a particular client-side feature. The server can use it as a signal, but the browser should still make runtime capability decisions when appropriate.
User-Agent Client Hints
Modern browsers also provide User-Agent Client Hints as a more structured mechanism for exposing selected client information. In JavaScript, navigator.userAgentData may be available in supporting browsers.
if ("userAgentData" in navigator) {
console.log(navigator.userAgentData);
}Client Hints can provide structured information such as browser brands and platform data without requiring applications to parse a large traditional User-Agent string.
However, User-Agent Client Hints do not turn browser identification into feature detection. They provide client metadata; they do not replace capability checks.
User-Agent Reduction and Privacy
Browsers have also moved toward reducing the amount of detailed information exposed through traditional User-Agent strings. This is part of a broader effort to reduce passive fingerprinting and unnecessary exposure of client characteristics.
As a result, applications that depend on extracting increasingly detailed information from User-Agent strings can become more fragile over time.
When detailed client information is genuinely needed, modern browser mechanisms such as Client Hints can be more appropriate than increasingly complex User-Agent parsing. When only a capability matters, feature detection remains the more direct solution.
Feature Detection vs User-Agent Detection: Examples
| Requirement | Preferred approach | Reason |
|---|---|---|
| Use Geolocation API | Feature detection | The application needs to know whether the API exists |
| Use CSS Grid | CSS feature detection | The layout depends on CSS capability |
| Log browser information | User-Agent or Client Hints | The goal is client identification |
| Investigate browser-specific rendering bug | User-Agent detection may be appropriate | The workaround targets a known client-specific issue |
| Enable a JavaScript API | Feature detection | Browser identity is only an indirect signal |
| Analyze traffic by browser | User-Agent or Client Hints | Analytics require client classification |
A Common Mistake: Detecting Chrome to Use a Feature
One of the most common mistakes is writing code like this:
if (navigator.userAgent.includes("Chrome")) {
useAdvancedFeature();
}This code combines two unrelated questions. The application does not really need to know whether the browser contains the word Chrome. It needs to know whether useAdvancedFeature can safely run.
A better implementation is to check the capability directly.
if ("someRequiredApi" in navigator) {
useAdvancedFeature();
} else {
useFallback();
}The exact check depends on the feature. The important principle is to detect the dependency itself.
Another Mistake: Assuming a Browser Version Is Enough
const isModernChrome =
navigator.userAgent.includes("Chrome/120");
if (isModernChrome) {
enableFeature();
}This type of check is fragile because it depends on string formatting, browser identification and a hard-coded version assumption.
If the application can directly test the required capability, that test is usually more meaningful.
When You Need Both Approaches
Feature detection and User-Agent detection are not mutually exclusive. An application can use both when they answer different questions.
For example, a server can record the User-Agent for diagnostics while the browser performs feature detection before enabling an optional API. The server-side metadata and client-side capability check serve different purposes.
const browserInfo = navigator.userAgent;
if ("IntersectionObserver" in window) {
enableLazyLoading();
} else {
loadImagesNormally();
}
sendDiagnostics({
browser: browserInfo,
});The browser information is used for diagnostics, while the feature check controls application behavior.
Feature Detection in React
React applications need to consider when feature detection runs. Browser APIs do not exist during server-side rendering, so accessing them directly during server rendering can cause errors.
import { useEffect, useState } from "react";
export function LocationButton() {
const [supported, setSupported] = useState(false);
useEffect(() => {
setSupported("geolocation" in navigator);
}, []);
if (!supported) {
return <p>Location is not available.</p>;
}
return <button>Use my location</button>;
}The exact implementation depends on the application's rendering strategy, but the general rule is important: browser-only APIs should be accessed in a browser context.
Feature Detection in Next.js
The same principle applies to Next.js applications. Server components and server-rendered code cannot assume that browser globals such as window or navigator exist.
"use client";
import { useEffect, useState } from "react";
export function ClipboardButton() {
const [supported, setSupported] = useState(false);
useEffect(() => {
setSupported(
"clipboard" in navigator &&
window.isSecureContext
);
}, []);
if (!supported) {
return null;
}
return <button>Copy</button>;
}Notice that checking for the API is only part of the decision. Clipboard functionality also has security and permission requirements, so the actual operation should still handle failures.
Do Not Overuse Feature Detection
Feature detection is useful, but adding checks everywhere can make code unnecessarily complicated. If a feature is guaranteed by the application's supported browser baseline, there may be no reason to test it repeatedly.
The right approach depends on the project's browser support policy. Compatibility data should be used to establish the baseline, while runtime detection should be used for capabilities that can legitimately vary within that environment.
Do Not Parse User-Agent Strings Manually Unless Necessary
Manual parsing quickly becomes difficult because browser strings contain compatibility tokens, version formats and product-specific behavior. If an application genuinely needs browser classification, using a maintained parser is generally safer than creating a large collection of regular expressions.
A User Agent Parser can help turn a raw User-Agent string into structured information such as browser, operating system and device-related fields. A User-Agent Generator can also be useful when testing how an application responds to different User-Agent values.
Testing Feature Detection
Feature detection should be tested against the environments that matter to the application. A feature can exist but still behave differently depending on browser version, operating system, permissions and security context.
- Test the supported browser baseline.
- Test browsers where the feature is unavailable.
- Test permission-denied scenarios.
- Test insecure and secure contexts when relevant.
- Test API failures rather than only API existence.
- Test server-rendered and client-rendered execution separately.
- Test fallback behavior.
Browser support data can help determine which environments need explicit fallback testing. Runtime feature detection then protects the application when the actual environment differs from expectations.
Choosing the Right Detection Strategy
A useful decision process is to first identify what information the application actually needs.
- If you need to know whether a capability exists, use feature detection.
- If you need to classify the client for analytics or diagnostics, User-Agent information can be appropriate.
- If you need a CSS capability, prefer @supports or CSS.supports().
- If you need a narrowly targeted workaround for a documented browser bug, browser identification may be justified.
- If you need detailed client metadata, consider User-Agent Client Hints where supported.
- If the feature is part of your guaranteed browser baseline, avoid unnecessary runtime checks.
Feature Detection vs Browser Support Data
Browser support databases and compatibility tables are useful during development, but they serve a different purpose from runtime feature detection.
Compatibility data helps developers decide whether a feature is appropriate for the project's target browsers. Feature detection handles runtime variation in the actual environment.
| Tool or technique | When it is useful |
|---|---|
| Browser support data | Planning browser compatibility before implementation |
| Feature detection | Making a runtime capability decision |
| User-Agent parsing | Classifying a client from its metadata |
| User-Agent logging | Investigating real-world browser traffic |
Security Considerations
Neither User-Agent detection nor client-side feature detection should be treated as a security boundary.
A User-Agent header is supplied by the client and can be changed. Browser-side JavaScript is also under the control of the client. Therefore, access control, authentication and authorization decisions must not depend on a browser claiming to be a particular product.
A Practical Architecture
A resilient application can use several layers of information without confusing their purposes. During development, compatibility data establishes the supported baseline. At runtime, feature detection determines whether optional capabilities are available. Server-side User-Agent data can provide diagnostics and analytics. Client Hints can provide structured client metadata when that information is actually needed.
This separation keeps the application logic clearer. Each mechanism answers a different question instead of forcing one source of information to perform every job.
Summary
Feature detection and User-Agent detection are both useful, but they should not be treated as interchangeable techniques. Feature detection checks whether a capability exists, while User-Agent detection identifies the client making the request.
When application behavior depends on a browser capability, checking that capability directly is usually the most robust solution. This avoids maintaining browser-specific assumptions and works naturally with progressive enhancement.
User-Agent information remains useful for analytics, debugging, diagnostics and narrowly targeted compatibility workarounds. Modern User-Agent Client Hints can also provide structured client information in supported environments.
The most important rule is simple: detect the thing you actually need. If the application needs a feature, detect the feature. If it needs information about the client, User-Agent metadata may be appropriate.
Frequently Asked Questions
Should I use feature detection instead of User-Agent detection?
For decisions about whether a browser capability can be used, feature detection is generally the more direct approach. User-Agent detection is still useful when the application actually needs to identify or classify the client.
Is navigator.userAgent reliable?
It can provide useful client metadata, but it should not be treated as a complete or authoritative description of browser capabilities. User-Agent strings also contain compatibility information and can change over time.
Can User-Agent detection be used for browser-specific fixes?
Yes, when there is a documented browser-specific problem that requires a targeted workaround. Such checks should be narrow and isolated rather than becoming the main compatibility strategy.
Is User-Agent detection a security mechanism?
No. User-Agent information is supplied by the client and can be changed. It should never be used to establish trust, authorization or access to sensitive server-side functionality.
What are User-Agent Client Hints?
User-Agent Client Hints are browser-provided mechanisms for exposing selected client information in a more structured way. They can provide information such as browser brands and platform data in supporting environments.
Does feature detection guarantee that an API call will succeed?
No. An API may exist but still require permissions, HTTPS, user interaction or other conditions. Feature detection should be combined with error handling and checks for relevant runtime requirements.
Should React applications use feature detection?
Yes. React applications can use feature detection for browser APIs and other optional capabilities, but browser-only globals should be accessed in a client-side execution context rather than during server rendering.
Helpful Browser and HTTP Tools
When working with browser compatibility, a Browser Feature Lookup can help investigate specific APIs and browser capabilities, while a Browser Support Checker can help review compatibility across browser versions. These tools are useful during development before deciding whether a fallback or runtime feature check is necessary.
When you need to inspect or test User-Agent information, a User Agent Parser can turn raw User-Agent strings into structured browser and platform information, while a User-Agent Generator can produce different User-Agent values for compatibility testing. An HTTP Header Viewer is useful when investigating the User-Agent header and other request or response headers involved in a real HTTP exchange.
Conclusion
Feature detection and User-Agent detection answer different questions. Feature detection is about capabilities, while User-Agent detection is about client identity and metadata.
For most application features, checking the capability directly leads to simpler and more resilient code. User-Agent information remains valuable when browser identity itself is relevant, particularly for diagnostics, analytics and carefully scoped compatibility workarounds.
A modern web application does not need to choose one technique exclusively. The important part is using each technique for the problem it is actually designed to solve and avoiding browser identity as a substitute for capability detection.