Using nuxtServerInit in Vuex to Fetch Component Data

March 30th 2020

If you are using Vuex as a state management library for you Nuxt.js application you have access to the nuxtServerInit action which is called in the root (index.js) file of your vuex store located in ./store/index.js. The nuxtServerInit action is called in universal mode on the sever for every server request (including the initial request) and is used to pass data from the server to the client to be hydrated in your application pages/components during server side render. This enhances SEO performance since the data is loaded server-side and passed to the client. Also, since we are filling our Vuex store the client side navigation between pages/components will persist the data and the request will not need to be made again from the client.

Before Nuxt 2.12 this was one of the few ways to fulfill server side rendering of component’s data for components that “lived” outside of page components. In versions less than Nuxt 2.11 you could call the fetch method or asyncData methods in pages components and pass the data down as props to other components that lived outside of pages. But what if you had a global component such as a cart or navigation component that needed data and which lived outside of a page component (for instance was included in a layout)? This is where nuxtServerInit can come in handy to fill the initial state of your store and hydrate your component data server-side.

Today we’ll take a look at how to use nuxtServerInit to fill your Vuex store and hydrate component data outside of page components. We will use the example of having an Announcement component which is included in the layouts/default.vue component and gets data from an api. Our example project layout will look like:

project
|_ components
|     |_ Annoucement.vue
|_ store
|     |_ index.js
|_ layouts
      |_ default.vue

Let’s first take a look at our default.vue layout component as this will be fairly simple layout to include our Announcement.vue component:

<template>
  <div>
    <announcement />
    <nuxt />
  </div>
</template>
<script>
import Announcement from '@/components/Announcement'
export default {
  components: {
    Announcement
  }
}
</script>

All we’re doing here is importing out Announcement component and defining it in our layout. We are doing this to show what a global component (a component that lives outside pages) would look like. Now we will move on to creating our Vuex store and using the nuxtServerInit action to populate the store. Let’s create a file a ./store/index.js which will look like:

export const state = () => ({
  announcement: null
})

export const mutations = {
  SET_ANNOUNCEMENT(state, message){
    state.announcement = message
  }
}

export const actions = {
  async nuxtServerInit({ commit }) {
    const { body } = await fetch('https://jsonplaceholder.typicode.com/posts/1')
  .then(response => response.json())
  commit('SET_ANNOUNCEMENT', body)
}

The nuxtServerInit action receives the vuex store object as the first parameter and we are deconstructing that to get the commit method which we can use to call a mutation on our state and set our announcement property. Since we are making an asynchronous call using the fetch method we need to declare async nuxtServerInit so we can use the await keyword inside our action. When the promise resolves we are using commit('SET_ANNOUNCEMENT', body) to trigger our mutation, passing in the response body. Now our state object should be filled with the response we got back and available in our Vuex store for all components to access. Let’s turn our attention back to our Announcement.vue component. We will need to use a vuex helper mapState to map the state of our announcement to a variable inside our component:

<template>
  <div class="absolute w-full bg-teal-500 text-white py-1 top-0 mt-0">
    <div class="container mx-auto px-4">
      <div class="flex -mx-4">
        <div class="w-full text-center">
          <p class="text-xs md:text-sm">{{ announcement }}</p>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import { mapState } from 'vuex'
export default {
  computed: {
    ...mapState(['announcement'])
  }
}
</script>

With our computed property defined we are mapping our state.announcement to a component variable announcement. Since our vuex state is hydrated (server rendered) we are able to access the property in universal mode (both client and server) and the Announcement.vue component will be hydrated with our announcement on page render!

The nuxtServerInit action is a great way to hydrate your vuex store server side and provide component data to global components that live outside pages!

If you are just looking to fetch data in a component and you do not need to fill the vuex store and you are using Nuxt 2.12 or greater you can use a new hook called fetch in any of your Vue components. But if you want to hydrate your Vuex store server side than nuxtServerInit action is the way to go! Until next time, stay curious, stay creative.