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.