Reputation: 123
in my Next.js app i want to render a mobile-/ desktop-header based on the width of the device. In general this is a simple task, i did many times in normal react, using hooks like useWindowDimensions, where I check the width of the window object and use this parameter for a simple condition. In Next.js however I ran into the problem, that my app is pre-rendert on the server, so of course there is no window object, that I could use for the condition. Things I tried:
useEffect({...}, [])
which then check the real condition and trigger a re-render with the correct condition. This would solve my SEO-thing, and also the ugly content mismatch warning. BUT I run into a noticeable layout-shift, where the user sees the mobile-header first and after half a second the header changes to the desktop-header. ugly stuff..So, to make things short, there has to be a good way to render components in Next.js without the above problems. I just couldn't figure out yet, what is best practice for this kind of stuff.
I would appreciate any hints, tipps or advices in this topic ❤️
Upvotes: 3
Views: 1764
Reputation: 183
I too had to render totally separate layout on desktop vs tablet / mobile. I also really wanted to take advantage of SSR.
So here's a solution that works well for me. I'm using Next 14, TS 5.2.2, and the newer app router, but this could also work with page router.
It's based on the the last approach listed above, and is the most bullet proof and "kosher" that I could find, but it does involve the downside of writing to search params.
// src/middleware.ts
import { NextRequest, NextResponse, userAgent } from 'next/server'
import { getSelectorsByUserAgent } from 'react-device-detect'
export const middleware = async (request: NextRequest) => {
const ua = userAgent(request)
const { isMobileOnly, isTablet, isDesktop } = getSelectorsByUserAgent(ua.ua)
const agent = isMobileOnly ? 'phone' : (isTablet ? 'tablet' : (isDesktop ? 'desktop' : 'unknown'))
const { nextUrl: url } = request
url.searchParams.set('agent', agent)
return NextResponse.rewrite(url)
}
// src/app/[slug]/page.tsx
import React from 'react'
import { Desktop, TouchDevice } from './scroll-snap'
// From nextjs source
type PageProps = {
params: { slug: string };
searchParams?: { [key: string]: string | string[] | undefined };
}
const Page: React.FC<PageProps> = ({ searchParams }) => {
// see src/middleware.ts
const agent = searchParams?.agent
if (agent === 'desktop') {
return <Desktop />
}
else if (agent === 'tablet') {
return <TouchDevice isTablet={true} />
}
return <TouchDevice isTablet={false} />
}
export default Page
Upvotes: 0
Reputation: 1
It can be solved using css by creating 2 components, one for mobile, one for desktop and then hiding the mobile header on desktop view, hiding the desktop header on mobile view
Upvotes: -1