You want to conditionally render components on the server side based on the device type (mobile, tablet, desktop). The goal is to avoid rendering all components server-side and hiding them with CSS, and to prevent client-side JavaScript from causing a flash of content as it loads.
Create a hook that determines the device type based on window size and stores it in a cookie. Use this context to conditionally render components according to the device type.
'use client'
import * as React from 'react'
type DeviceType = 'mobile' | 'tablet' | 'desktop' | undefined
const useDeviceSize = ({ defaultDeviceSize }: { defaultDeviceSize: DeviceType }) => {
const [deviceSize, setDeviceSize] = React.useState<DeviceType>(defaultDeviceSize)
const getDeviceSize = (): DeviceType => {
if (window.matchMedia('(min-width: 1024px)').matches) return 'desktop'
if (window.matchMedia('(min-width: 768px)').matches) return 'tablet'
if (window.matchMedia('(min-width: 0px)').matches) return 'mobile'
}
React.useEffect(() => {
const handleResize = () => {
const currentDeviceSize = getDeviceSize()
setDeviceSize(currentDeviceSize)
document.cookie = `device-size=${JSON.stringify(currentDeviceSize)}`
}
handleResize()
window.addEventListener('resize', handleResize)
return () => window.removeEventListener('resize', handleResize)
}, [])
return { deviceSize }
}
export { useDeviceSize, type DeviceType }
import { cookies } from 'next/headers'
import { DynamicNav } from '@/components/dynamic-nav'
import { type DeviceType } from '@/lib/hooks/useDeviceSize'
export default function SiteLayout({ children }: Readonly<{ children: React.ReactNode }>) {
const deviceSizeCookie = cookies().get('device-size')
let defaultDeviceSize: DeviceType
if (deviceSizeCookie) defaultDeviceSize = JSON.parse(deviceSizeCookie.value) as DeviceType
return (
<>
{/* Sample Usage */}
<DynamicNav defaultDeviceSize={defaultDeviceSize}>{children}</DynamicNav>
</>
)
}
'use client'
import * as React from 'react'
import { useDeviceSize, type DeviceType } from '@/lib/hooks/useDeviceSize'
const DynamicNav: React.FC<{
children: React.ReactNode
defaultDeviceSize: DeviceType
}> = ({ children, defaultDeviceSize }) => {
const { deviceSize } = useDeviceSize({ defaultDeviceSize })
if (!deviceSize) {
return <h1>loading...</h1>
}
if (deviceSize === 'mobile') {
return (
<>
<h1>mobile</h1>
{children}
</>
)
}
if (deviceSize === 'tablet') {
return (
<>
<h1>Tablet</h1>
{children}
</>
)
}
return (
<div>
<h1>Hello World</h1>
{children}
</div>
)
}
export { DynamicNav }
If you guys want to use tailwind sizes. modify the
device-type-provider.tsx
as follows: