@davaux/user-agent
Parses the incoming User-Agent header and exposes structured device and browser information through ctx.state.userAgent. Useful for serving different content to mobile vs. desktop clients, blocking bots, or analytics.
Installation
npm install @davaux/user-agent
Setup
// davaux.config.ts
import { defineConfig } from 'davaux/config'
import { userAgent } from '@davaux/user-agent'
export default defineConfig({
middleware: [userAgent()],
})
Available fields
After the middleware runs, ctx.state.userAgent exposes:
| Field | Type | Description |
|---|---|---|
raw | string | The original User-Agent string |
isMobile | boolean | true if the client is a mobile device (excludes tablets) |
isTablet | boolean | true if the client is a tablet (iPad, Android without Mobile token) |
isDesktop | boolean | true if the client is a desktop/laptop (not bot, mobile, or tablet) |
isBot | boolean | true if the client appears to be a crawler/bot |
botName | string | undefined | Bot name when isBot is true, e.g. 'Googlebot', 'curl' |
deviceType | 'mobile' | 'tablet' | 'desktop' | 'bot' | 'unknown' | Normalized device category |
browserName | string | undefined | e.g. 'Chrome', 'Firefox', 'Safari' |
browserVersion | string | undefined | e.g. '124.0.0' |
osName | string | undefined | e.g. 'Windows', 'macOS', 'iOS', 'Android' |
osVersion | string | undefined | e.g. '14.6' |
engine | string | undefined | Rendering engine: 'Blink', 'Gecko', 'WebKit' |
partial | boolean | true when signals are conflicting or insufficient — e.g. a non-browser runtime on a mobile/tablet OS, or a Mozilla UA with no detectable browser or OS |
Usage examples
Redirect mobile users
// src/middleware.ts
import { defineMiddleware, redirect } from 'davaux'
export default defineMiddleware(async (ctx, next) => {
const { isMobile } = ctx.state.userAgent
if (isMobile && !ctx.url.hostname.startsWith('m.')) {
redirect(`https://m.example.com${ctx.url.pathname}`)
}
return next()
})
Block bots from specific routes
// src/routes/api/_middleware.ts
import { defineMiddleware } from 'davaux'
export default defineMiddleware(async (ctx, next) => {
if (ctx.state.userAgent.isBot) {
return new Response('Forbidden', { status: 403 })
}
return next()
})
Serve different layouts based on device
// src/routes/_layout.tsx
import { defineLayout } from 'davaux'
import type { LayoutProps } from 'davaux'
export default defineLayout(({ children, ctx }: LayoutProps) => {
const { isMobile } = ctx.state.userAgent
return (
<html>
<body class={isMobile ? 'mobile-layout' : 'desktop-layout'}>
{children}
</body>
</html>
)
})
Analytics middleware
// src/middleware.ts
import { defineMiddleware } from 'davaux'
export default defineMiddleware(async (ctx, next) => {
const ua = ctx.state.userAgent
const result = await next()
// Log structured analytics data after the response
if (!ua.isBot) {
analytics.track({
path: ctx.url.pathname,
device: ua.deviceType,
browser: ua.browserName,
os: ua.osName,
})
}
return result
})
TypeScript
The package ships its own module augmentation, so ctx.state.userAgent is fully typed as soon as you import @davaux/user-agent anywhere in your project. No extra declarations needed.
If you need the type directly (e.g. for a helper function), import it from the package:
import type { UserAgent } from '@davaux/user-agent'
function logDevice(ua: UserAgent) {
console.log(ua.deviceType, ua.browserName)
}
Notes
- Parsing happens once per request and is cached in
ctx.state - Bot detection uses a regularly maintained list of known bot user-agent patterns
- For highly accurate bot detection or advanced device fingerprinting, consider a dedicated service alongside this middleware