Getting Started with Nuxt for Vue Server-Side Rendering

Follow us on LinkedIn for our latest data and tips!

,

Getting Started with Nuxt for Vue Server-Side Rendering

Nuxt.js is a framework + command-line tool that allows you to create Vue web apps that are rendered on the server, at least for the first request. This is done to improve SEO and perceived performance (how fast it seems for the user). Many developers may want to do this, but have apprehensions because it seems complicated to do, but I’m here to show you that’s it’s rather simple. So, let’s get started building your first Nuxt-powered, Server-Side-Rendered web application!

Setting Up

The first thing we need to do is set up a new project with Nuxt. I’m going to assume you have Node installed and are comfortable typing a few commands into your console. If that’s true, the first thing we should do is ensure we have Vue CLI installed by entering the following into your console:

npm install -g @vue/cli
# or if you use Yarn instead
yarn global add @vue/cli

Once you’ve got that installed, we need to use the Vue CLI to create our project. We’ll give it the template provided by the Nuxt community.

vue init nuxt-community/starter-template <project-name>

You can replace <project-name> with whatever you want the name of the folder to be where your project is stored. Then it’ll ask you a few questions, which you can answer however you wish. They shouldn’t affect much that you can’t easily change later. Now let’s finish the setup by navigating to the new folder and installing the dependencies:

cd <project-name>
npm install # or yarn install

Now if you run npm run dev, it’ll start up the development server. Then navigate to localhost:3000 in your browser and you should see something similar to this, except it should have your project name and description. There! You now have a working Vue web app with server-side rendering!

What Next?

Of course, we’re not finished. This only has one page and it doesn’t even do anything useful. But before you can start throwing your own code in, you need to understand a few things about Nuxt. We’ll start by explaining what all these folders are for.

FolderWhat It’s For
layoutsVue components that specify the outer shell/layout for the pages
pagesVue components that are for specific routes/pages. The subfolder/file structure and names affect routes, which we’ll get into more later
componentsVue components that aren’t used to specify a layout or page
pluginsVue plugins you wish to use; you’ll need to specify in the Nuxt configuration which ones you’re using
middlewarecustom functions that can be run before rendering either a page or a layout
storedefinitions of your Vuex stores
assetsuncompiled source code that will be compiled/bundled. It’s pretty much any scripts or files you want webpack to deal with that don’t belong in any of the above folders
staticstatic files that will be copied verbatim to the server and be available at the root of the app URL

 

Those last two aren’t really specific to Vue apps, so we won’t really get into them, but we really need to look at the top three in this article. We might explore the more advanced topics related to Nuxt in a later article, but if you need to know now (or right after you finish reading this article), you can check out the official docs for some great information.

As a final note about the folders, ~ and @ are aliases for the main source folder where the above folders are located, so you can import from any of these directories by prefixing the path with ~/ rather than using a relative path.

Pages

When a request comes in, Nuxt uses the router to determine which page component to render. The router isn’t configured manually, though (it can be, but in most cases it’s unnecessary). Instead of configuring it yourself, it is created based off the folders and component files that exist inside the pages folder. The routes are based off the names of the folders and files, so a file structure like this:

pages/
--| user/
-----| index.vue
-----| profile.vue
--| index.vue
--| about.vue

will result in a router configuration that looks like this:

{
  routes: [
    {
      name: 'index',
      path: '/',
      component: 'pages/index.vue'
    },
    {
      name: 'about',
      path: '/about',
      component: 'pages/about.vue'
    },
    {
      name: 'user',
      path: '/user',
      component: 'pages/user/index.vue'
    },
    {
      name: 'user-profile',
      path: '/user/profile',
      component: 'pages/user/profile.vue'
    }
  ]
}

Of course, these are all static routes, but you can also create dynamic routes. When prefixing the name of a folder or file with an underscore (_) the name of the file then becomes the name of a parameter given to the page instead of being the URL slug that will take you to that route. For example:

pages/
--| _slug/
-----| comments.vue
-----| index.vue
--| users/
-----| _id.vue
--| index.vue

will become:

{
  routes: [
    {
      name: 'index',
      path: '/',
      component: 'pages/index.vue'
    },
    {
      name: 'users-id',
      path: '/users/:id?',
      component: 'pages/users/_id.vue'
    },
    {
      name: 'slug',
      path: '/:slug',
      component: 'pages/_slug/index.vue'
    },
    {
      name: 'slug-comments',
      path: '/:slug/comments',
      component: 'pages/_slug/comments.vue'
    }
  ]
}

Note that there is a question mark in the users-id route. This is because when you name the file with an underscore, the parameter becomes optional, so if you just went to /users, the pages/users/_id.vuecomponent will still get rendered but the id parameter will be empty. In this case, if you wanted to make the id parameter required, you could rename the file to index.vue, then add a folder named _id under usersand move index.vue into it, so your folder structure would look like this:

pages/
--| _slug/
----| comments.vue
----| index.vue
--| users/
----| _id/
------| index.vue
--| index.vue

This would result in the exact same router configuration, except that the question mark would disappear and the component property of that route would point to the new file.

Of course, if you’re using dynamic routes with parameters, you may need to validate that the parameter is legitimate. To do this, add a validate function to your component and Nuxt will call it:

export default {
  validate ({ params }) {
    // Must be a number
    return /^\d+$/.test(params.id)
  }
}

If validate returns false, an error page will be displayed instead of the page matching the route. You can learn more about validation or routing in the official documentation, but for now, we know enough to keep moving.

Beyond the validate method, there are some more methods that are automatically called by Nuxt on page components. These don’t work on other components, only page components.

MethodWhat It’s For
asyncDataThis is called before pretty much anything else when the component is created. From it you can return an object to initialize the data for the component (just like the data method). Nuxt passes in a “context” object so you have all the information you should need. asyncData can return a Promise in order to be asynchronous. The returned data will merge with the object from data and with asyncData‘s data taking precedence.
fetchSimilar to asyncData in that it runs early on when component is created, but it will not set any data. It is meant to be used to fetch data to put into your Vuex store, but can be used for any operation to be done prior to calling beforeCreate or created methods. Return a Promise to make it asynchronous.
headReturn an object describing the tags and attributes to be used inside the head of the document. Uses vue-meta internally, so read their docs to learn the available options. This is available in any component, not just pages.

Layouts

After the page has been determined, it is rendered within a layout. Layouts allow you to wrap your pages with common parts of your interface such as your header, navigation, and footer. By default, it’ll use layouts/default.vue, but you can specify which layout to use on the page:

export default {
  layout: 'blog'
}

That page would then use the layout at layouts/blog.vue. If the specified layout doesn’t exist, it’ll just go back to the default.

There are only two things that are special about a layout when compared to a standard component:

  1. The file resides in the layouts folder
  2. The template must have a nuxt component in the template

Being in the layouts folder is just a convention to make sure the layout can be found. Using the nuxt component within the layout’s template is required because that component is where the page is injected into the layout. It’s essentially equivalent to the router-view from vue-router.

Browser Component vs Server Component

In the browser, Vue components are dynamic. They can be mounted to the DOM, they can be updated, they can be destroyed and replaced by other components, but that doesn’t happen on the back end. Vue has hooks built into components to allow you to run code at certain points during their life cycle, but most of those will never get called on the server. In fact, of all the life cycle hooks provided by Vue, only beforeCreate and created will ever be called on the server.

This means that the code you write inside the other hooks is free to reference browser APIs. The created hook is probably one of the most used hooks, though, so be careful what you write in it. If you write browser-only code, you will cause an error on the server, and vice versa.

If you can’t avoid this problem, you can often find a tool that will work on both the server and in the browser, such as axios for requests, and if that doesn’t work, try creating a barebones mock of the API and include it in the environment that doesn’t support that API:

// In a component
import localStorage from '~/assets/localStorage'

export default {
  created () {
    localStorage.setItem('foo', 'bar')
  }
}
// assets/localStorage.js
let ls

if (typeof window === 'object' && typeof window.localStorage === 'object') {
  ls = window.localStorage
} else {
  ls = {
    length: 0,
    key () {},
    getItem () {},
    setItem () {},
    removeItem () {},
    clear () {}
  }
}

export default ls

This is an effective way of writing code that runs on only one side without causing any issues.

Built-In Components

We’ve already taken note of the nuxt component, but there are other components built into Nuxt that give us special functionality.

First of all, there is nuxt-child, which is used when you use nested routes—which we did not cover in this article—to put a child page within the parent page. This is similar, once again, to router-view from vue-router.

Next is nuxt-link, which is a way of creating links to pages in your web app, and works in the same way as router-link from vue-router.

Finally, there is no-ssr, which will not render the children of that component on the server. This is another way of using browser APIs without them breaking on the server, but this isn’t recommended, especially if it will prevent important content from being rendered by the server. The more you use this, the less benefit you get from server-side rendering. It certainly has its use cases or they would not have included it, but I would think hard about it before choosing to use this.

Finishing Up

That’s all for this one. There is plenty here to mull over, but I hope it’s not too daunting as this was—at least to an extent—intended to help relieve any fears about getting started with server-side rendering. There’s nothing to be afraid of, and it isn’t as daunting as it may seem at first, so feel free to explore this new world of server-side rendered web apps.