skip to Main Content

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


  1. Chosen as BEST ANSWER

    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.


  2. 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>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search