6

I have <MobileLayout />, <DesktopLayout />. I'm using Next.js for Server Side Rendering. And I noticed there are many famous ui library has mobile detection components like <Respnosive /> component in Semantic-UI-React. But all of this is client side method, not working properly on SSR

I read some documents the conclusion is I should check user-agent of server side req.headers. In Next.js, What is proper way to detect device and conditonally render one of MobileLayout / DesktopLayout?

What I tried

in _app.js

import isMobile from 'ismobilejs'

...

function Homepage({ Component, pageProps, mobile }){
  return (
    mobile ? 
      <MobileLayout><Component {...pageProps} /></MobileLayout> : 
      <DesktopLayout><Component {...pageProps} /></DesktopLayout>
  )
}

HomePage.getInitialProps = async (appContext) => {
  const userAgent = appContext.ctx.req.headers['user-agent']
  const mobile = isMobile(userAgent).any
  const appProps = await App.getInitialProps(appContext)
  return { ...appProps, mobile }
}

But the problem is getIntialProps on _app.js executed every page load. with moving page with client, the appContext.ctx is undefined so it will omit error. and I think this method might block some nextjs builtin optimizations.

Error in error page getInitialProps: TypeError: Cannot read property 'headers' of undefined

So what is propery way to check device in Next.js?

ton1
  • 7,238
  • 18
  • 71
  • 126

2 Answers2

2

If you want to detect the user's device using userAgent, your best bet is this answer:

IndexPage.getInitialProps = ({ req }) => {
  let userAgent;
  if (req) { // if you are on the server and you get a 'req' property from your context
    userAgent = req.headers['user-agent'] // get the user-agent from the headers
  } else {
    userAgent = navigator.userAgent // if you are on the client you can access the navigator from the window object
  }
}

(Note you should actually be using getServerSideProps or getStaticProps when possible, if you have Next 9.3 or newer, but sometimes there is no replacement for the getInitialProps functionality.)

However, the folks at Mozilla advise:

It's worth re-iterating: it's very rarely a good idea to use user agent sniffing. You can almost always find a better, more broadly compatible way to solve your problem!

The maker of the isMobile package you're importing even warns:

You might not need this library. In most cases, responsive design solves the problem of controlling how to render things across different screen sizes.

So, see if you can use CSS3 media queries to conditionally render certain elements or change their size, etc., rather than having completely separate mobile and desktop layout components. But it's possible you have an edge case where you can't make any alternative option work.

If you are going to keep your current setup and use your two layouts on other pages, you might consider combining them into a parent <Layout> component that conditionally renders one or the other so you don't have to copy that logic into every page:

export const Layout = (props) => {
      return (
          props.mobile ? 
          <MobileLayout>{props.children}</MobileLayout> :
          <DesktopLayout>{props.children}</DesktopLayout>
      )
}
jdaz
  • 5,964
  • 2
  • 22
  • 34
  • I don't think getInitialProps works in client side, so the navigator never defined. and there was someone who told this in origial answer. – ton1 Jun 07 '20 at 01:16
  • That comment in the original answer is incorrect. From the [docs](https://nextjs.org/docs/api-reference/data-fetching/getInitialProps): "For the initial page load, getInitialProps will run on the server only. getInitialProps will then run on the client when navigating to a different route via the next/link component or by using next/router." – jdaz Jun 07 '20 at 02:15
  • Have you found the solution for this? We are currently to migrate our reactjs to nextjs and this issue quickly came up.. we even have 2 react files and we use "react-device-detect" to detect and use it in react-router as `` but with Nextjs we need a better solution for these :D – Aung Myint Thein Oct 12 '20 at 05:23
  • You should be able to use a version of the conditional Layout component I mention at the end of my answer. For the homepage, just put that in `index.js` in your `pages` folder and you should be good to go. – jdaz Oct 12 '20 at 15:48
  • @AungMyintThein Did you find a solution for the mobile detection and nextjs? – james emanon Feb 04 '21 at 06:24
  • we tried to migrate all of our pages to be responsive. hence, not needed for mobile detection. however, in one of the project, we realized that it is not possible to detect the mobile in the server. So, we detect and change some layout in `useEffect()`. Since majority are from mobile, we set mobile view as default and change to desktop in useEffect if it is desktop. Hope this helps! – Aung Myint Thein Feb 05 '21 at 07:16
0

you can use "next-useragent"

Give access to user-agent details anywhere using withUserAgent method.

next-useragent npm package