Nuxt.js: Server Side Rendering for components called from layouts

Created on 12 Aug 2017  路  6Comments  路  Source: nuxt/nuxt.js

Hello ! on my site (https://yineo.fr/), my right sidebar is a component called from layouts/default.vue

Because i don't want to call it in every page component.

I know asyncData() is only for pages and not layouts but maybe they are some workarounds ?

So my sidebar with last articles is not server side rendered and google bot can not see it ( see picture below, the left one is what google bot see and probably index )

How can i have server side rendering for my "Sidebar" Component called from layouts/default.vue ( code below the picture ) ?
thanks !

capture d ecran 2017-08-12 a 04 24 47

<!-- main layout -->
<template>
  <div>
    <AppNavigation />
    <AppHeader/>

    <div class="container">
      <div class="columns">

        <div class="column is-two-thirds">
          <nuxt/>
        </div>

        <div class="column">
          <section class="section">
            <TwitterFollowMe/>
            <hr/>
            <h2 class="title is-2">Derniers billets</h2>
            <PostsSidebar :posts="posts" />
          </section>
        </div>

      </div>
    </div>
    <AppFooter />
  </div>
</template>

<script>
import AppHeader from '~/components/AppHeader'
import AppNavigation from '~/components/AppNavigation'
import AppFooter from '~/components/AppFooter'
import TwitterFollowMe from '~/components/TwitterFollowMe'
import PostsSidebar from '~/components/PostsSidebar'
import { getPosts } from '~/services/wpContentApi'

export default {
  components: { AppHeader, AppNavigation, AppFooter, TwitterFollowMe, PostsSidebar },
  data () {
    return {
      posts: []
    }
  },
  async created () {
    this.posts = await getPosts(20)
  }
}
</script>

This question is available on Nuxt.js community (#c1197)
question

Most helpful comment

@pi0 that works !

Here is my full code to help the others :

store/index.js :

import Vuex from 'vuex'

/**
 * This our global state for our app.
 */
const createStore = () => new Vuex.Store({
  state: {
    menuMobileIsOpened: false,
    postsSidebar: []
  },
  mutations: {
    setMenuMobileIsOpened (state, value) {
      state.menuMobileIsOpened = value
    },
    setPostsSidebar (state, value) {
      state.postsSidebar = value
    }
  }
})

export default createStore 

plugins/hydrate-layout-data.js

import axios from "axios"
import { getPosts } from '~/services/wpContentApi'

/**
 * We do this to achieve server side rendering for
 * content displayed by layouts components
 * ( layouts does not have an asyncData() method )
 */
export default async function( { store } ) {
  let posts = await getPosts(20)
  store.commit('setPostsSidebar', posts)
}

layouts/default.vue

<!-- main layout -->
<template>
  <div>
    <AppNavigation />
    <AppHeader/>

    <div class="container">
      <div class="columns">

        <div class="column is-two-thirds">
          <nuxt/>
        </div>

        <div class="column">
          <section class="section">
            <TwitterFollowMe/>
            <hr/>
            <h2 class="title is-2">Derniers billets</h2>
            <PostsSidebar :posts="this.$store.state.postsSidebar" />
          </section>
        </div>

      </div>
    </div>
    <AppFooter />
  </div>
</template>

<script>
import AppHeader from '~/components/AppHeader'
import AppNavigation from '~/components/AppNavigation'
import AppFooter from '~/components/AppFooter'
import TwitterFollowMe from '~/components/TwitterFollowMe'
import PostsSidebar from '~/components/PostsSidebar'
import { getPosts } from '~/services/wpContentApi'

export default {
  components: { AppHeader, AppNavigation, AppFooter, TwitterFollowMe, PostsSidebar },
}
</script>

nuxt.config.js : add call to plugin

  plugins: [
    '~/plugins/hydrate-layout-data'
  ]

All 6 comments

Hi. The best workaround is using global Vuex store and using either nuxtServerInit or a Plugin with exported function to fill store or a middleware which one is more suitable depending on your needs

@pi0 hi, thanks ! suppose i want to add 3 ou 4 blocks (with async datas) on the right sidebar, which one if the three solutions would be the better according to you : VueServerInit, Plugin or middleware ?

If I was, would choose plugins as nuxtServerInit is not working on SPA mode. We can fetch async data and call store commits within plugin like this:

export default async function( { store, app: { $axios }} ) {
  let posts = (await $axios.get('...')).data
  store.commit('posts/update', posts)

 // Or delegate it to store actions
 await store.dispatch('posts/fetch', { $axios })
}

@pi0 that works !

Here is my full code to help the others :

store/index.js :

import Vuex from 'vuex'

/**
 * This our global state for our app.
 */
const createStore = () => new Vuex.Store({
  state: {
    menuMobileIsOpened: false,
    postsSidebar: []
  },
  mutations: {
    setMenuMobileIsOpened (state, value) {
      state.menuMobileIsOpened = value
    },
    setPostsSidebar (state, value) {
      state.postsSidebar = value
    }
  }
})

export default createStore 

plugins/hydrate-layout-data.js

import axios from "axios"
import { getPosts } from '~/services/wpContentApi'

/**
 * We do this to achieve server side rendering for
 * content displayed by layouts components
 * ( layouts does not have an asyncData() method )
 */
export default async function( { store } ) {
  let posts = await getPosts(20)
  store.commit('setPostsSidebar', posts)
}

layouts/default.vue

<!-- main layout -->
<template>
  <div>
    <AppNavigation />
    <AppHeader/>

    <div class="container">
      <div class="columns">

        <div class="column is-two-thirds">
          <nuxt/>
        </div>

        <div class="column">
          <section class="section">
            <TwitterFollowMe/>
            <hr/>
            <h2 class="title is-2">Derniers billets</h2>
            <PostsSidebar :posts="this.$store.state.postsSidebar" />
          </section>
        </div>

      </div>
    </div>
    <AppFooter />
  </div>
</template>

<script>
import AppHeader from '~/components/AppHeader'
import AppNavigation from '~/components/AppNavigation'
import AppFooter from '~/components/AppFooter'
import TwitterFollowMe from '~/components/TwitterFollowMe'
import PostsSidebar from '~/components/PostsSidebar'
import { getPosts } from '~/services/wpContentApi'

export default {
  components: { AppHeader, AppNavigation, AppFooter, TwitterFollowMe, PostsSidebar },
}
</script>

nuxt.config.js : add call to plugin

  plugins: [
    '~/plugins/hydrate-layout-data'
  ]

And here is the result in google search explorer , sidebar is now here :

capture d ecran 2017-08-12 a 20 15 54

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mikekidder picture mikekidder  路  3Comments

bimohxh picture bimohxh  路  3Comments

vadimsg picture vadimsg  路  3Comments

nassimbenkirane picture nassimbenkirane  路  3Comments

uptownhr picture uptownhr  路  3Comments