Skip to main content

Server-side rendering

Works everywhere there's a UA header

Node, Bun, Deno, Cloudflare Workers, Vercel Edge, Fastly Compute@Edge, AWS Lambda. The library is platform-neutral — same import, same call shape.

get-browser is built for SSR from the ground up. The library:

  • Never touches window or navigator at import time. Importing get-browser in a Node module or worker is safe.
  • Falls back gracefully when there's no UA available — every detector returns false, detect() returns 'unknown'.
  • Accepts an explicit { userAgent, vendor } on every function, so you can pin detection to the incoming request.
import { detect, isMobile } from 'get-browser';

const browser = detect({ userAgent: req.headers['user-agent'] ?? '' });

When you pass an explicit userAgent, the library ignores globalThis.navigator entirely — handy for unit tests that run inside happy-dom / jsdom but want a clean UA.

Framework recipes

Pick your stack:

app/api/browser/route.ts
import { detect, isMobile } from 'get-browser';

export async function GET(req: Request) {
const userAgent = req.headers.get('user-agent') ?? '';
return Response.json({
browser: detect({ userAgent }),
mobile: isMobile({ userAgent }),
});
}

Hydration mismatches

If you render different markup on the server (based on the incoming UA) than on the client (based on navigator), React will warn about hydration mismatches. Two safe options:

  1. Pass the server-resolved browser down as a prop / context. Don't call detect() again on the client for the initial render — use the server's answer.
  2. Wrap browser-specific UI in a "client only" boundary (e.g. useEffect or Next.js dynamic import with { ssr: false }).
import { type Browser } from 'get-browser';
const ServerBrowserContext = React.createContext<Browser>('unknown');

function App({ initialBrowser }: { initialBrowser: Browser }) {
return (
<ServerBrowserContext.Provider value={initialBrowser}>
<Page />
</ServerBrowserContext.Provider>
);
}
Pattern: detect once, propagate

On the server, call detect({ userAgent }) once per request and stash it on the context. On the client, the same value flows down via props — zero re-detection, zero mismatch.

See also