Nuxt.js
Getting Started with Nuxt.js
Updated: 23 December 2025
Introduction
Nuxt.js is server-side framework based on Vue, the content here is kind of from this DesignCourse Video and this video but with TypeScript. The overall code from the video can be found here
Getting Started
To create a new app you can run:
1yarn create nuxt-app my-appYou can then just retain the default options for the most part, at this point you will also be able to select TypeScript as the language
That will initialize a new Nuxt app in the my-app folder. You can then do the following to start the application:
1cd my-app2yarn devGenerated Files
Once we have run the project creation we will see the following folders in our application:
assetscomponentslayoutsmiddlewarepagespluginsstaticstore
We also have the layouts/default.vue which has our basic page layout:
1<template>2 <div>3 <nuxt />4 </div>5</template>In here we can see the section which will render our nuxt component which is the root for our Nuxt application
Creating a Layout
We’ll create some partials for our layout in a layout/partials directory
For a basic layout we can create a nav section:
partials/nav.vue
1<template>2 <header>3 <nuxt-link to="/">NAVIGATION</nuxt-link>4 <nav>5 <ol>6 <ul>7 <nuxt-link to="/">Home</nuxt-link>8 </ul>9 <ul>10 <nuxt-link to="about">About</nuxt-link>11 </ul>12 </ol>13 </nav>14 </header>15</template>16
17<script lang="ts">18 import Vue from 'vue'19
20 export default Vue.extend({})21</script>22
23<style>24 nav {25 background-color: red;26 }27</style>Note how we make use of nuxt-link instead of the normal a. We also use Vue.extend so that we can allow typescript to infer the type correctly
Next, we can import and use the Nav component in our default layout like so:
layout/default.vue
1<template>2 <div>3 <nav />4 <nuxt />5 </div>6</template>7
8<script lang="ts">9 import Vue from 'vue'10 import Nav from './partials/nav.vue'11
12 export default Vue.extend({13 components: {14 Nav,15 },16 })17</script>Including Global Resources
To include external resources or do anything that we would normally do in our head element in HTML, we can do via the nuxt.config.js file. For example if we would like to add a link to a font we can do so by adding a link to our head
nuxt.config.js
1...2head: {3 title: process.env.npm_package_name || '',4 meta: [5 { charset: 'utf-8' },6 { name: 'viewport', content: 'width=device-width, initial-scale=1' },7 { hid: 'description', name: 'description', content: process.env.npm_package_description || '' }8 ],9 link: [10 { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },11 {rel: 'stylesheet', href: 'MY_FONT_URL'}12 ]13},14...If we would like to include a css file we have locally we can also do this in the same config file by referencing the path like @assets/styles/main.css
Pages
Nuxt makes use of file based routing, based on this our main page for our site will be located in the pages/index.vue file. We can clean up the unecessary content in this file and just leave the following:
1<template>2 <div>3 <h1>Home</h1>4 </div>5</template>6
7<script lang="ts">8 import Vue from 'vue'9
10 export default Vue.extend({})11</script>12
13<style></style>Each page component will essentially be rendered into the nuxt element. We can create another page for about and include some content that’s similar to above. This file will be called pages/about.vue
Once we have the about page we should be able to click on our links and view the different pages
Page Metadata
If we would like to provide some metadata we can export a head function from our component in which we specify some metadata. We need to do this in the component’s script tag:
pages/about.vue
1import Vue from 'vue'2
3export default Vue.extend({4 head() {5 return {6 title: 'The About Page',7 meta: [{ name: 'description', content: 'this is about the about page' }],8 }9 },10})Note that if we intend to use our metadata like we do above we need to be sure to remove the relevant meta from out nuxt.config.js as well
Router Transitions
Nuxt also has built-in router transitions, these make use of the page-enter-active and page-leave-active classes to apply the transitions. We can update our layout/default.vue file with an animation to apply it
layout/default.vue
1<template>2 <div>3 <nav />4 <nuxt />5 </div>6</template>7
8<script lang="ts">9 import Vue from 'vue'10 import Nav from './partials/nav.vue'11
12 export default Vue.extend({13 components: {14 Nav,15 },16 })17</script>18
19<style scoped>20 .page-enter-active {21 animation: bounce-in 0.8s;22 }23 .page-leave-active {24 animation: bounce-out 0.5s;25 }26 @keyframes bounce-in {27 0% {28 transform: scale(0.9);29 opacity: 0;30 }31 100% {32 transform: scale(1);33 opacity: 1;34 }35 }36 @keyframes bounce-out {37 0% {38 transform: scale(1);39 opacity: 1;40 }41 100% {42 transform: scale(0.9);43 opacity: 0;44 }45 }46</style>We can also apply transitions to specific elements on a page by exporting a transition property with the name of the transition, and including the CSS for an animation. If we create a transition like transition: 'floop', we will need to have the CSS transitions defined like aboe but using the class names floop-enter-active and floop-leave-active so they can be applied
Retrieve Data
We can retrieve data on the client or on the server using the fetch method on a component
When using fetch in Nuxt we need to take note of the following:
- Set the initial data with the
datafunction ordataproperty - Set
fetchOnServertotrueto allow for server side fetching if on the server - We can optionally set the
fetchDelayto delay the fetch on the client, e.g. to prevent flashing. If we want everything to load immediately then don’t set this - Use
isomorphic-fetchto allow for consistent fetching on both the client and server - Set the relevant data in the
fetchfunction
The code for our component will look like so:
pages/index.vue
1import Vue from 'vue'2import isoFetch from 'isomorphic-unfetch'3
4type ShowData = {5 id: string6 name: string7}8
9type TvResponse = {10 show: ShowData11}[]12
13type TvData = {14 shows: ShowData[]15}16
17export default Vue.extend({18 data() {19 return {20 shows: [],21 } as TvData22 },23 async fetch() {24 const res = await isoFetch('https://api.tvmaze.com/search/shows?q=batman')25 const data = (await res.json()) as TvResponse26
27 this.shows = data.map((el) => el.show)28
29 console.log(`Show data fetched. Count: ${this.shows.length}`)30 },31 fetchOnServer: true,32 fetchDelay: 1000,33})By using the fetch method for the component we get the ability to view the current state of the fetch when on the client, we can use the $fetchState values for $fetchState.error, fetchState.pending, and $fetchState.timestamp for the status of the fetch. We can also call the hook from a component using the $fetch function in a template
Make sure the data model in your
datafunction aligns with the populated data on thefetchfunction otherwise the SSR will not kick in and it will render on the client
We can render the component with the awareness of the fetch state like so:
pages/index.vue
1<template>2 <div>3 <h1>Home</h1>4 <div v-if="$fetchState.pending">Loading . . .</div>5 <div v-else-if="$fetchState.error">{{ $fetchState.error }}</div>6 <div v-else>7 <ol>8 <li v-for="show of shows" :key="show.id">{{ show.name }}</li>9 </ol>10 </div>11 </div>12</template>Build and Run To build and run the final application just do:
1yarn build2yarn start