Detecting a visitor's browser or device type in Angular relies on the same plain browser JavaScript navigator.userAgent string every other framework uses — Angular adds no special API here, just a wrapper worth building for testability and SSR-safety.
Reading the raw user agent string
console.log(navigator.userAgent);
// "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36..."
A basic mobile-vs-desktop check
isMobileDevice(): boolean {
return /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
}
Wrapping it in an injectable service
@Injectable({ providedIn: 'root' })
export class DeviceDetectionService {
isMobile(): boolean {
return /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
}
isSafari(): boolean {
return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
}
isIOS(): boolean {
return /iPad|iPhone|iPod/.test(navigator.userAgent);
}
}
Wrapping these checks in a service, rather than calling navigator.userAgent directly from components, keeps device detection mockable in unit tests and gives one place to update the detection logic if it ever needs refinement.
Handling server-side rendering safely
import { isPlatformBrowser } from '@angular/common';
import { PLATFORM_ID, Inject } from '@angular/core';
constructor(@Inject(PLATFORM_ID) private platformId: Object) {}
isMobile(): boolean {
if (!isPlatformBrowser(this.platformId)) {
return false;
}
return /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
}
navigator doesn't exist during Angular Universal's server-side rendering pass — checking isPlatformBrowser() first prevents a genuine runtime error when this code runs during SSR, the same platform-check pattern needed for localStorage access covered elsewhere on this site.
Why user agent parsing is notoriously unreliable
Browsers have a long history of adding other browsers' names into their own user agent string for compatibility reasons (Chrome's string contains "Safari", Edge's contains "Chrome") — this is exactly why naive substring checks (like the Safari regex above, which has to explicitly exclude "chrome") are fragile and can misidentify a browser; a dedicated, actively maintained parsing library is more reliable than hand-rolled regex for anything beyond the broadest mobile/desktop distinction.
Using a dedicated library for more reliable detection
npm install ua-parser-js
import { UAParser } from 'ua-parser-js';
const parser = new UAParser();
const result = parser.getResult();
console.log(result.browser.name); // "Chrome"
console.log(result.os.name); // "Windows"
console.log(result.device.type); // "mobile" | "tablet" | undefined (desktop)
Feature detection as a more robust alternative
const supportsTouchEvents = 'ontouchstart' in window;
For many real use cases (like deciding whether to show touch-friendly UI), checking whether a specific browser feature is actually supported (feature detection) is more robust than inferring it from the device type via user agent — a laptop with a touchscreen, for instance, would be misclassified as "desktop, no touch" by user agent alone, while feature detection correctly identifies its actual touch capability.