Recently, there has been an increase in the adoption of Vue’s Composition API in the ecosystem. Its ability to maintain components as they grow and also its encouragement of reusing components comes in handy in lots of large projects. Due to server-side rendering, projects written in Nuxt have a hard time using Vue’s Composition API. The Nuxt team observed this and put together a composition API for projects written in Nuxt. In this article, we’ll take a look at how the Nuxt Composition API works, its leverage over Vue’s Composition API, and best practices for using it in projects.
To add Nuxt’s Composition API to your project, install it like you would install a plugin:
npm install @nuxtjs/composition-api --save /* For Yarn users */ yarn add @nuxtjs/composition-api
When this is successfully done, enable the module in your Nuxt config file:
{ buildModules: [ '@nuxtjs/composition-api' ] }
The main challenges users encounter when using Vue’s Composition API in Nuxt projects comes from data not being returned during communication between the server and client sides of the application. Let’s say we have data that we want to retrieve from an API and display on a Nuxt app:
import { ref } from '@nuxtjs/composition-api' function useTodo () { const todo = ref({}) const fetchPost = async (id) => { fetch('https://gorest.co.in/public-api/todos/' + user_id) .then(response => response.json()) .then(json => post.value = json) } return { todo, fetchTodo } }
When we try to import and use the useTodo
function in our Nuxt application, the data retrieved from the API fails to get to the server-side because the basic action of fetching the data is asynchronous while, by default, server-side implementations run synchronously. Thus the code sample below will return an empty div
:
<template> <div>{{ data.title }}</div> </template> <script> import { useTodo } from '@/composables' export default defineComponent({ setup (props, { root }) { const { todo, fetchTodo } = useTodo($root.route.params.user_id) fetchTodo() return { todo } } }) </script>
Nuxt’s Composition API provides a wrapper called useFetch
for fetching data from the server side and relaying it to the client side. It does a useful job in preventing multiple asynchronous network calls as it ensures navigation is done just once on the server side and the resulting data is retrieved by our client-side code and used as its initial state. Using a mock REST API sample, let’s take a look at how we can retrieve data with the useFetch
wrapper:
import { defineComponent } from '@nuxtjs/composition-api' import { ref, useFetch } from '@nuxtjs/composition-api' function useTodo (id) { const todo = ref({}) const { fetch: fetchTodo } = useFetch(async () => fetch('https://gorest.co.in/public-api/todos/' + user_id) .then(response => response.json()) .then(json => todo.value = json) ) return { post, fetchTodo } }
In the code block above, useFetch
returns fetchTodo
– a fetch function used to make our asynchronous call. Once this call is made the first time, our server-side code will contain our list of todos:
<div key="0"> Aggredior the testimony of the multitude, therefore I will restore the color of whic h I see. </div>
The Composition API also provides the ability to access properties provided by Nuxt Context such as a store, route, or environmental variables. It does this via a wrapper called useContext
– which can also access Nuxt module properties. Let’s see how context
makes use of the nuxtjs/axios
module to make a request. The first thing we’ll do is install the module we want to access properties from:
npm install @nuxtjs/axios
Next, we’ll add it to our Nuxt configuration file:
// nuxt.config.js export default { modules: [ '@nuxtjs/axios', ], axios: { // proxy: true } }
Then we call the $axios
property to make use of the module. Let’s try to swap the native fetch
method in our previous fetchTodo
example with @nuxt/axios
:
import { defineComponent } from '@nuxtjs/composition-api' import { ref, useFetch, useContext } from '@nuxtjs/composition-api' function useTodo (id) { const todo = ref({}) const { @axios } = useContext() const { fetch: fetchTodo } = useFetch(async () => todo.value = await $axios.$get('https://gorest.co.in/public-api/todos/' + id) return { post, fetchTodo } }
The Composition API provides a usemeta()
Hook which enable you to interact directly with your application’s header properties and meta tags. With useMeta()
, you can set and restrict the modification of your header’s state to one component only. Let’s take a look at how this works:
<template> <div> Welcome Back <nuxt /> </div> </template> import { defineComponent, useContext, useMeta, watchEffect, } from "nuxt-composition-api"; export default defineComponent({ name: "Home", head: {}, setup() { const { route } = useContext(); const { title } = useMeta(); watchEffect(() => { title.value = route.value.path; }); }, }); </script>
In the code sample above, we make use of the useMeta()
tag to set our header value to the route defined by the useContext
wrapper – we can go a step further by defining the routes and personalizing their headers as well:
// StudentProfile.vue <template> <div> Student's Profile <nuxt-link to="/Grades">Grades</nuxt-link> </div> </template> <script lang="ts"> import { defineComponent, useMeta } from "nuxt-composition-api"; export default defineComponent({ name: "StudentProfile", head: {}, setup() { const { title } = useMeta(); title.value = "Here is your profile"; }, }); </script>
The value assigned to title.value
via the useMeta
tag then defines our header for whichever route is being viewed. Also, note that it’s important that the head
property is declared for the useMeta
tag to work:
// Grades.vue <template> <div> Grades <nuxt-link to="/StudentProfile">Student's Profile</nuxt-link> </div> </template> <script lang="ts"> import { defineComponent, useMeta } from "nuxt-composition-api"; export default defineComponent({ name: "Grades", head: {}, setup() { const { title } = useMeta(); title.value = "View your grades here"; }, }); </script>
On the browser, this is how it would look:
Screen Recording 2020 11 03 at 07 05 47
Uploaded by Raphael Ugwu on 2020-11-03.
Nuxt’s Composition API is strict in fact-checking and as such, there are rules to be adhered to during usage. One of such rules is to ensure a unique key is used alongside helper functions such as useAsync
and ssrRef
when they are applied within global composables. Failure to impose a unique key for the concerned function could lead to constants or variables adopting values not assigned to them. Take a look at the code sample below:
function useTodo() { // Only one unique key is generated, no matter how many times this function is called. const todo = ssrRef('') return todo } const a = useTodo() const b = useTodo() b.value = 'Buy Gas'
In the above code sample, if a unique key isn’t set, the value of a
will also be initialized to Buy Gas
on the client-side. This can be prevented if the useTodo
function is assigned a key:
function useTodo(path: string) { const task = useAsync( () => fetch(`https://api.com/slug/${path}`).then(r => r.json()), path, ) return { task } } export default useTodo
Nuxt’s Composition API was created out of the necessity of handling composition in server-side applications more seamlessly. Though its adoption is still in the early stages – it wouldn’t be surprising to see it support huge projects and have similar success to Vue’s Composition API. Should you need to take a look at the code demo for an example in this post – you can check it out here.
Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens in your Vue apps, including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.
The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error and what state the application was in when an issue occurred.
Modernize how you debug your Vue apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to implement one-way and two-way data binding in Vue.js, using v-model and advanced techniques like defineModel for better apps.
Compare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.