User-Agent Based Rendering

Sometimes the desktop version of our application differs a lot from our mobile version, because the UI is different or because we load different scripts, styles, etc. We want to decide which page to load based on the User-Agent header without loading unnecesary assets for the current viewport.

Folder structure

We will rewrite our user to different pages based on its User-Agent so we need to have a different page for every viewport we want to support.

The example has a pages/_viewport folder with pages for mobile and desktop, alongside a root middleware (/middleware.js) that will handle all requests to our pages:

/middleware.ts
/pages
  /_viewport
    /mobile.tsx
    /desktop.tsx

Checking the User-Agent

In the middleware, we now check the User-Agent header and rewrite to the correct page:

import { NextRequest, NextResponse, userAgent } from 'next/server'

// Set pathname where middleware will be executed
export const config = {
  matcher: '/',
}

export default function middleware(req) {
  // Parse user agent
  const { device } = userAgent(req)

  // Check the viewport
  const viewport = device.type === 'mobile' ? 'mobile' : 'desktop'

  // Update the expected url
  req.nextUrl.pathname = `_viewport/${viewport}`

  // Return rewrited response
  return NextResponse.rewrite(req.nextUrl)
}

Now, everytime a request comes in we will check the User-Agent and rewrite the user to the correct page:

Middleware logging implementation

Result

This page is using this strategy, try it out in different devices and you will see the message below changing accordingly:

This page was loaded on a desktop device.