Skip to content

Instantly share code, notes, and snippets.

@buddalee
Created May 27, 2020 06:36
Show Gist options
  • Save buddalee/d3d7a46cfab71e072fb6c3bed074bc58 to your computer and use it in GitHub Desktop.
Save buddalee/d3d7a46cfab71e072fb6c3bed074bc58 to your computer and use it in GitHub Desktop.
Nextjs in server side to detect device with user agent
Nextjs in server side to detect device with user agent
static async getInitialProps ({ Component, ctx }) {
const isServer = !!ctx.req
const userAgent = isServer
? ctx.req.headers['user-agent']
: navigator.userAgent
const isLine = /\bLine\//i.test(userAgent) || false
const isMobile = /(iPad|iPhone|Android|Mobile)/i.test(userAgent) || false
const rules = [
'WebView',
'(iPhone|iPod|iPad)(?!.*Safari/)',
'Android.*(wv|.0.0.0)'
]
const regex = new RegExp(`(${rules.join('|')})`, 'ig')
const isInApp = Boolean(userAgent.match(regex))
console.log('=================')
console.log('userAgent: ', userAgent)
console.log('isLine: ', isLine)
console.log('isMobile: ', isMobile)
console.log('isInApp: ', isInApp)
console.log('=================')
}
@aravindanve
Copy link

For app router:

I wanted to pre-render a page conditionally based on the device type on React/NextJS/SSR. I was able to do it by storing the user-agent in a context and reading it from a hook useIsMobile() which returned true or false based on the parsed user-agent header on the server side.

We create a context to store the parsed user-agent header.

// userAgentContext.ts
import { userAgent } from "next/server";
import { createContext } from "react";

export type UserAgent = ReturnType<typeof userAgent>;
export const UserAgentContext = createContext<UserAgent | undefined>(undefined);

We create a hook to consume the context and check device type.

// useIsMobile.ts
import { useContext } from "react";
import { UserAgentContext } from "./userAgentContext.ts";

export const useIsMobile = () => {
  const userAgent = useContext(UserAgentContext);
  if (!userAgent) {
    throw new Error("useIsMobile must be used within a UserAgentContext.Provider");
  }

  return userAgent.device.type === "mobile";
};

We can use a sever component as the layout (app router) to get the user agent from request headers

// layout.tsx
"use server";

import { headers } from "next/headers";
import { userAgent } from "next/server";
import { LayoutClient } from "./layoutClient.ts";

export default async function Layout({ children }: { children: React.ReactNode }) {
  const reqUserAgent = userAgent({ headers: headers() });
  return <LayoutClient reqUserAgent={reqUserAgent}>{children}</LayoutClient>
}

We can then pass the user agent to a client component that will provide the initial value for the context. Since client components with "use client" directive are still rendered once on the server with initial values then hydrated on the client.

// layoutClient.tsx
"use client";

import { UserAgentContext } from "./userAgentContext.ts";

export default function LayoutClient({ reqUserAgent, children }: { reqUserAgent: UserAgent, children: React.ReactNode }) {
  return <UserAgentContext.Provider value={reqUserAgent}>{children}</UserAgentContext.Provider>;
}

Now when this page is rendered for the first time on the server with initial values, useIsMobile() should accurately return a value based on the user-agent device type.

// page.tsx
"use client";

export default function Page() {
  const isMobile = useIsMobile();

  return isMobile ? <>Mobile</> : <>Desktop</>;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment