Building server-rendered apps in Vue using Nuxt.js

This post will walk you through the process of building server-rendered webpages in Vue — a progressive JavaScript framework that is simple to start using. I’ll also discuss Nuxt, a tool that makes the server-rendering process seamless. Finally, as a practical example, we will build an app that shows pictures of dogs based on their breed.

We are going to build a server rendered Vue app that loads very quickly, even before JavaScript is loaded, and is discoverable by search engines.

To better understand the problem, open an SPA built with any framework and try to inspect the page. Here is an example of one of my Vue demo projects:

Regardless of the project’s size, relevance, or beauty, this is what you end up with:

The content you see in your page source is the server response. Notice how it’s just the div#id tag that’s shown and all other contents, including those pretty images, are nowhere to be found.

That’s a problem.

Actually, there are a lot of potential problems we could discuss.

Let’s focus on the most glaring — web crawling. Search engines and other web crawlers don’t know much about the visuals of your website after they are rendered. They just care about that ugly HTML sent as a response from your server. Here, when a crawler visits our website, it goes home with nothing but that div tag and some contents in the head tag.

How about a more subtle problem? Unsurprisingly, JavaScript frameworks (in our case, Vue) heavily depend on JavaScript. As a matter of fact, their views in single-page applications (SPAs) are generated and controlled with JavaScript. When things go wrong with JS loading, your user is stuck with a blank screen.

Why Nuxt.js

There is a Vue library that you can use in your Vue project to serve the contents on the server. This library is known as vue-server-renderer and can be installed via npm. It also has good documentation.

Going this route is a little more difficult when compared to first getting started with Vue. Even though the documentation does its best to simplify things, SSR in general is a tricky concept.

The Webpack’s Prerender SPA plugin could be an option when vue-server-render is hard to get started with. Unfortunately, it’s only reliable for simple SEO marketing pages ( “about” or “contact us”, for example). When you’re working with lots of routes/dynamic routes , the plugin bails on you.

Nuxt looks at all of these problems and gives you a platform to build SPA projects that are server-rendered and easy to setup. Drop in a command line instruction and you are good to go. It was inspired by React’s Next which is an alternative for React developers to implement server-side rendering (SSR).

Enough talk. Let’s walk the walk.

Setting up a Nuxt project

Earlier, I mentioned that all it takes is a command line instruction to get your Nuxt project going. Let’s see how true that is:

vue init nuxt/starter know-your-dogs

The command relies on the Vue CLI. If you don’t have it installed, run:

npm install -g vue-cli

This will generate a gigantic project folder structure. Don’t worry about it. With time, you will learn what folders you need and what to put where.

Layouts and route page

Run the generated boilerplate and you’ll see this:


Now navigate to ./layouts/default.vue and update the template with the following:

<div class="layout">
<h1>Know Your Dog!</h1>

With some update to the [styles](), you should see the view change to:

Notice how the “Know Your Dog!” text added to the layout was included in the page rendered at the root URL of the app. This is because the default.vue file is a layout file. It wraps other pages and provides generic information that is shared among a group of pages. The <nuxt/> tag in the default.vue will be replaced with the page at run time.


Unlike your usual Vue project, you don’t have to configure routes in Nuxt. Any component found in the pages directory is automatically mapped to a route based on its name. The index.vue page is mapped to the root URL / but if we had a page called dogs.vue, it will be mapped to /dogs.

We will see more on routing when we starting fleshing out our app. For now, let’s fetch a list of dogs from the API.

Loading async data

In Vue, you would normally fetch async data from the created lifecycle method. This is not the case in Nuxt because this data also needs to be rendered on the server. For that reason, another method called asyncData exists which renders the data fetched on both the server and the client.

Install Axios — a popular library for making HTTP requests:

yarn add axios

In pages/index.vue, add the following method in the component object:

import axios from 'axios'
export default {
async asyncData () {
const { data } = await axios.get('')
return { breeds: data.message }

We are harnessing the power of async/await feature. When a response is returned, we bind the value to the view by returning an object containing the response.

You can still stick to callbacks if that’s where you’re most comfortable:

export default {
asyncData ({ params }, callback) {
.then((res) => {
callback(null, { breeds: })

You can now render the returned response data using the component’s template:

<section class="container">
<a v-for="breed in breeds" :key="breed" class="breed">

Here is the new look:

Now view the page source and notice how the contents are also rendered to the server:

Dynamic routes

We need to implement what happens when any of the breeds is clicked. What we want to do is to navigate to dogs/:id and show a random image of the breed that was selected.

Create a folder named dogs in “pages” and add a file with the _breed.vue in the folder.

In the script tag, fetch a random image of the dog from the API based on a parameter:

import axios from 'axios'
export default {
async asyncData ({params}) {
const { data } = await axios.get(`${params.breed}/images/random`)
return { breed: data.message, name: params.breed }

asyncData receives an object where params is a property. The params property is also an object that contains all the parameters passed into a page (in our case breed). The page was configured to receive parameter by prefixing its file name with an underscore (_). We use this parameter to fetch a random image and set the image and breed name to the view.

Let’s display this image and name:

<img :src="breed" alt="">

Now you can see a little detail of the breed you selected:

When you view the page source, you will still see your markup available on the server:

Final words

You might not always need Nuxt, but when you do, what you have learned in this article is enough for you to get started. But there’s always more to learn. For that, head to the Nuxt website to learn more features like plugins, store, etc). If there’s interest, we’ll cover more review more Nuxt in future posts.

Plug: LogRocket, a DVR for web apps

LogRocket is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single page apps.