'Nuxtjs: how to use img[src] rendered with ssr (for seo) and lazy loading img[data-src] on the client side?

Is it possible to use both - ssr and lazy-loading for images on the client side. Here is my situation. My markup is rendered with img[src], in my chrome browser I press ctrl+U and can see all images have an src attribute (seo robots will recognize them and I'm happy), but on the client side I need to have img[data-src] for lazy-loading. If I have a data-src attribute on the server-side, the seo robots won't find any src (pressing ctrl + U will prove it), but all images will be displayed with lazy loading... Should I filter the markup before receiving on the client side (actually I'm sure how to implement it in nuxt). Please suggest any ideas or directions to solve the issue?



Solution 1:[1]

Here is how to achieve the following:

  • fetching some images from an API
  • display 1 image when on a mobile viewport (< 1000px) represented by the red line
  • if your viewport is wider, you have 4 images (desktop viewport)
  • loading="lazy" is doing the heavy lifting here, it's working fine on not-so old browsers
  • if you start on mobile, open your network tab, filter by images named 6, you will see that you'll get some network requests going out if increasing the viewport > 1000px (lazy-loaded when scrolling still)
<template>
  <div>
    <pre>all the interesting images: {{ pokemon.sprites.other }}</pre>
    <div class="reference-breakpoint"></div>

    <p>Down below are all the remaining images ??</p>
    <img :src="pokemon.sprites.other.dream_world.front_default" />
    <img
      class="hide-when-mobile"
      loading="lazy"
      :src="pokemon.sprites.other.home.front_default"
    />
    <img
      class="hide-when-mobile"
      loading="lazy"
      :src="pokemon.sprites.other.home.front_shiny"
    />
    <img
      class="hide-when-mobile"
      loading="lazy"
      :src="pokemon.sprites.other['official-artwork'].front_default"
    />
  </div>
</template>

<script>
export default {
  async asyncData({ $axios }) {
    const pokemon = await $axios.$get(
      'https://pokeapi.co/api/v2/pokemon/charizard'
    )
    return { pokemon }
  },
}
</script>

<style scoped>
img {
  display: block;
  height: 100vh;
  width: auto;
}
.reference-breakpoint {
  width: 1000px;
  height: 1rem;
  background-color: red;
}
@media (max-width: 1000px) {
  .hide-when-mobile {
    display: none;
  }
}
</style>

Not satisfied with this one? Here is an intersectionObserver example, a bit more complex but also more powerful/flexible.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1