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('=================')
}
@danilockthar
Copy link

danilockthar commented Mar 10, 2022

Hi buddalee. What it means isLine ? Thank you!

@buddalee
Copy link
Author

buddalee commented Jun 3, 2022

Hi buddalee. What it means isLine ? Thank you!

It's a chat application name which is popular in Taiwan and Japan.

The code trace request whether come from mobile browser or line application In-App browser

=> https://line.me/en/

@gregg-cbs
Copy link

Just want to say that I am using this and thank you!

When using a hook that works of the window size it causes elements to jump in on page load which is not okay. This should be the offered solution on all the forums.

@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