Detect mobile

In my slice, I need to detect if the device is mobile.

"next": "^15.1.6"
"react": "^19.0.0"

I think I might need userAgent. How do I wire that in?

Hi @g.homewood ,

Welcome to the community :slight_smile:

In your Next.js project using Prismic slices, you can detect if the device is mobile using the userAgent from the request headers on the server or using the window object on the client.

Option 1: Server-Side Detection (Recommended for SSR/SSG)

Next.js provides the headers() function (in App Router) or context.req.headers['user-agent'] (in Pages Router) to detect the user agent on the server.

Implementation in a Server Component (App Router)

If you’re using the App Router (app/ directory), you can use headers():

import { headers } from 'next/headers';

export default function MySliceComponent(props) {
  const headersList = headers();
  const userAgent = headersList.get('user-agent') || '';
  const isMobile = /iPhone|iPad|iPod|Android|Mobile/i.test(userAgent);

  return <div>{isMobile ? 'Mobile View' : 'Desktop View'}</div>;
}

Option 2: Client-Side Detection (For CSR)

If your slice is rendered client-side only, you can detect mobile using the navigator.userAgent or window.innerWidth:

Using useEffect in the Slice Component

import { useEffect, useState } from 'react';

export default function MySlice({ slice }) {
  const [isMobile, setIsMobile] = useState(false);

  useEffect(() => {
    const userAgent = navigator.userAgent || navigator.vendor;
    setIsMobile(/iPhone|iPad|iPod|Android|Mobile/i.test(userAgent));
  }, []);

  return <div>{isMobile ? 'Mobile View' : 'Desktop View'}</div>;
}

Using window.innerWidth

For a more flexible approach, you can check the screen width:

import { useEffect, useState } from 'react';

export default function MySlice({ slice }) {
  const [isMobile, setIsMobile] = useState(false);

  useEffect(() => {
    const handleResize = () => setIsMobile(window.innerWidth < 768);
    handleResize(); // Initial check
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return <div>{isMobile ? 'Mobile View' : 'Desktop View'}</div>;
}

Which Method Should You Use?

• If your slice is server-rendered, use Option 1 (Server-Side Detection).
• If your slice is hydrated client-side, use Option 2 (Client-Side Detection).
• If you need responsive behavior on resize, use window.innerWidth.

Thanks for the detailed response!

I am using option 1 and it seems to work, except I get a warning

Property 'get' does not exist on type 'Promise'.ts(2339)

So I added an await and made the function async (?)

You’re right that headers() returns an object, but it does not return a Promise. You do not need await or async. The issue is likely due to an incorrect usage of headers().

Fix: Ensure headers() is used correctly

The headers() function in Next.js App Router returns a synchronous Headers object (not a Promise). You should be able to call .get('user-agent') directly.

:white_check_mark: Correct Implementation (No async needed)

import { headers } from 'next/headers';

export default function MySliceComponent() {
  const headersList = headers(); // This is NOT a Promise
  const userAgent = headersList.get('user-agent') || '';
  const isMobile = /iPhone|iPad|iPod|Android|Mobile/i.test(userAgent);

  return <div>{isMobile ? 'Mobile View' : 'Desktop View'}</div>;
}

Why did you get the error?

If you used await headers(), you might have accidentally done:

const headersList = await headers(); // ❌ Incorrect: headers() is not async

or

const headersList = headers; // ❌ Incorrect: This assigns the function itself

The correct approach is:

const headersList = headers(); // ✅ Correct: `headers()` returns an object, not a Promise

Let me know if the warning persists! :rocket: