Reputation: 2025
Live example available here
I'm trying to make a basic layout where, on mobiles, only the latest posts appear. On desktop, the left column should be the posts and the right column the top categories and most popular posts.
Here is the layout:
const IndexLayout: React.FC<IndexLayoutProps> = ({}) => {
const cols = useScreenType()
return cols === '2-cols' ? (
<div className="w-full flex justify-between items-start">
<ListPosts data-comp="ListPosts" className="w-4/6" />
<div className="sticky ml-12 w-2/6 flex flex-col">
<TopCategories data-comp="TopCategories" className="w-full" />
<PopularPosts data-comp="PopularPosts" className="mt-4" />
</div>
</div>
) : (
<ListPosts data-comp="ListPosts" className="w-full" />
)
}
Here's the useScreenType
hook:
import { useMediaQuery } from 'react-responsive'
export const useScreenType = () => {
const is2Cols = useMediaQuery({ minWidth: 1300 })
const is1Cols = useMediaQuery({ minWidth: 800 })
if (is2Cols) {
return '2-cols'
}
if (is1Cols) {
return '1-cols'
}
return 'fullscreen'
}
And I keep getting this error:
Warning: Expected server HTML to contain a matching <div> in <div>.
div
ListPosts@webpack-internal:///./components/posts/ListPosts.tsx:31:19
div
IndexLayout@webpack-internal:///./components/layout/IndexLayout.tsx:28:149
div
Index@webpack-internal:///./pages/index.tsx:24:149
ApolloProvider@webpack-internal:///./node_modules/@apollo/client/react/context/ApolloProvider.js:13:18
s@webpack-internal:///./node_modules/next-apollo/dist/index.es.js:26:1911
div
div
MyApp@webpack-internal:///./pages/_app.tsx:37:19
ErrorBoundary@webpack-internal:///./node_modules/@next/react-dev-overlay/lib/internal/ErrorBoundary.js:23:47
ReactDevOverlay@webpack-internal:///./node_modules/@next/react-dev-overlay/lib/internal/ReactDevOverlay.js:73:20
Container@webpack-internal:///./node_modules/next/dist/client/index.js:155:20
AppContainer@webpack-internal:///./node_modules/next/dist/client/index.js:643:18
Root@webpack-internal:///./node_modules/next/dist/client/index.js:779:19
Now I think the issue is due to the useScreenType
hook not being able to get a width because window
isn't defined on the server. But how can I fix this issue? And not only do I get an error, but my HTML renders weirdly.
The final render ends up being something like this (when it renders as '2-cols'):
<div class="flex flex-col justify-start items-start w-full">
<div class="mt-6 w-full"></div>
<div class="mt-4 flex items-center cursor-pointer transform transition hover:scale-105 text-sm">
<div class="w-full p-6 rounded-lg flex flex-col dark:bg-gray-800 shadow-md"></div>
<div class="mt-4 p-6 rounded-lg flex flex-col dark:bg-gray-800 shadow-md"></div>
</div>
</div>
Note: I am using Next.js v10.2.0
Code can be found on GitHub
Upvotes: 0
Views: 618
Reputation: 2025
For anyone wondering how I fixed this, I ditched the responsive design with logic and switched to CSS. Here is my layout post fix (changed some classes with the lg
prefix [documentation]):
const IndexLayout: React.FC<IndexLayoutProps> = ({}) => {
return (
<div className="mt-12 lg:mt-24 w-5/6 mx-auto flex items-start">
<div className="w-full flex justify-between items-start">
<ListPosts className="lg:w-4/6 w-full" />
<div className="hidden sticky ml-12 w-2/6 lg:flex flex-col">
<TopCategories className="w-full" />
<PopularPosts className="mt-4" />
</div>
</div>
</div>
)
}
Upvotes: 0
Reputation: 1785
As you notice, you cant access window object on server, so if you want to server-render something based on window object - you must hardcode these values.
The only thing you can rely on is user-agent in request headers, which gives you some understanding of user device.
For example this way you can detect user device in _app.js:
const device = deviceDetector.detect(isServer() ? ctx.req.headers['user-agent'] : window.navigator.userAgent)
deviceDetector is any kind of device detection implementation based on user agent
Upvotes: 1