TransWikia.com

Array list from AsyncData/fetch rendered by v-for throw: The client-side rendered virtual DOM tree is not matching server-rendered

Stack Overflow Asked by Daniel von Mirbach on December 22, 2020

I am using Nuxt.js for my application. In one of my pages I use AsyncData to asynchronously fetch an array of data objects from my Api, which I render in my template using v-for. This is working fine when I don’t use nuxt-link or <a href=".."></a> inside the v-for. As soon as I include nuxt-link or <a href=".."></a> inside my v-for, I get the following error:

The client-side rendered virtual DOM tree is not matching server-rendered content. 
This is likely caused by incorrect HTML markup, for example nesting block-level elements inside <p>, or missing <tbody>. 
Bailing hydration and performing full client-side render.

A basic example of what I try to accomplish:

<template>
<div>
  <div
    class="place-card"
    :key="place._id"
    v-for="place in places"
  >
    <nuxt-link
      :to="{
        name: 'places-title',
        params: { title: place.title }
      }"
    >
      <PlaceCard :place="place" />
    </nuxt-link>
  </div>
</div>
</template>

<script>
export default {
  asyncData() {
    // Some preprocessing
    return { places: [{...}, ..., {...}] }
  }
}
</script>

I was able to get it working, when I wrap the whole v-for div into <client-only>...</client-only> as described here in @Mohsens answer as well as here.

Unfortunately, <client-only></client-only> prevents server-side-rendering of the async data. However, I do need pre-fetching my data on the server-side, because of SEO.

Does anyone know another solution for this problem?

EDIT 12.10.2020
Extended log

Original code of PlaceCard component:

<template>
  <div class="bg-white rounded overflow-hidden shadow-lg">
    <img :src="place.images[0]" :alt="place.title" class="w-full h-40" />
    <div class="px-6 pt-4">
      <div class="font-bold text-base">{{ place.title }}</div>
    </div>
    <div class="px-6 pb-4">
      <nuxt-link :to="'/'"
        >#{{ place.placeType.split(' ').join('') }}</nuxt-link
      >
      <nuxt-link :to="'/'">#{{ place.address.country }}</nuxt-link>
      <nuxt-link :to="'/'" v-if="place.vegan">#vegan</nuxt-link>
      <nuxt-link :to="'/'" v-else>#not-vegan</nuxt-link>
      <nuxt-link :to="'/'" v-if="place.vegetarian">#vegetarian</nuxt-link>
      <nuxt-link :to="'/'" v-else>#not-vegetarian</nuxt-link>
    </div>
    <div class="author flex items-center py-3 px-6">
      <div class="user-logo">
        <img
          class="w-8 h-8 object-cover rounded-full mr-2 shadow"
          :src="place.creator.photoURL"
          :alt="place.creator.displayName"
        />
      </div>
      <div class="block text-xs">
        Added by<a href="#" class="ml-1">{{
          place.creator.displayName.split(' ').join('')
        }}</a>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    place: {
      type: Object,
      default: null
    }
  }
}
</script>

2 Answers

I figured it out. According to here and here, I had a nested link, the one inside my v-for div and another inside my PlaceCard component, which isn't allowed.

Correct answer by Daniel von Mirbach on December 22, 2020

Template should only contain 1 element thats fine. But you should not loop the top parent element. Try to wrap it in an div

<div>
  <div
    class="place-card"
    :key="place._id"
    v-for="place in places"
  >
    <nuxt-link
      :to="{
        name: 'places-title',
        params: { title: place.title }
      }"
    >
      <PlaceCard :place="place" />
    </nuxt-link>
   </div>
</div>

Answered by Ifaruki on December 22, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP