First Adventures with Nuxt

Anya Pavlova
9 min readJan 17, 2019

For the last few months I was working with Nuxt to rebuild the web application written with Express and Vue. The idea was to have both server and client-side rendering handled in the one place by the same framework. And since the frontend was already written in Vue, naturally Nuxt was a framework that I chose (for applications written in React I suggest to take a look at Next).

Nuxt is a relatively new library, so sometimes the lacking of documentation and resources might be really frustrating. That’s why I will try to share as much as possible about the things that I’ve learned along the way.

Note: This article covers Nuxt 2.x. It requires some basic knowledge of Vue and Vuex.

About Nuxt and Server-Side Rendering

At the beginning of the web all pages were server-side rendered. Later when the frontend started to become more and more complicated server-side rendering was mixed with client-side code for dynamic parts of the application. Some applications remained that way — with html rendered on server-side and few parts rendered on client-side, some moved even further.

In order to make user interactions with the web as smooth as possible Singe Page Applications (SPAs) appeared. With Single Page Applications server is rendering the initial page (often empty) and later everything else is rendered by client-side scripts. SPAs allowed to create complicated web applications where all user interactions, including page navigation, were handled on the client-side, while all server-side interactions happened via API calls.

While SPA’s enabled smooth user experience — rendering of the initial page became a significant performance concern because of necessity for client-side scripts to be loaded and executed. To solve this we can render html on a server-side and serve it on a first page load, so that user will see the page right away. In order to do that both server-side html and client-side html should look exactly the same, so that when one will be replaced with another, visibly user won’t see any difference. That meant that the server and client should share the same logic for page rendering, which can be achieved efficiently only by sharing the same code base. This is how server-side rendering frameworks for SPA’s appeared.

Nuxt is one of them — it is a framework based on Vue library, that creates a Universal Application, where both backend and frontend use the same codebase. Instead of a SPA written on Vue, you get a full server setup, that creates a SPA that utilizes server-side rendering (SSR).

SSR gives you the best from both worlds — smooth page navigation with SPA and fast initial page loading. It also makes your web application more friendly for search engines crawlers. If you want to know more on a topic, I encourage you to read intro from the Vue SSR Guide.

Quick start and Webpack out of the box

Nuxt is a framework which means that it has a lot of things figured out for you from the start. It comes preset with Webpack, Vuex, Vue Router and vue-meta. It’s not only that you don’t have to deal with Webpack setup at all (vue-cli, for example, has Webpack setup as well), but with Nuxt you have both backend and frontend out of the box running together smoothly. Moreover things like developer environment (including hot reload) and images display work without any additional effort.

The installation process is very simple and quick, all you have to do is follow official documentation and choose one of the two approaches: either use create-nuxt-app — a tool for a quick start or start from scratch and create a project setup yourself. In the end, all you have to do is launch your project with npm run dev.

Nuxt generates a page layout for you (all the best practices included) so you don’t have to care/know where is the right place to put scripts and css in order to get the best site performance. For example, inspecting the html output you will see that scripts are added to the head with rel="preload”:

Also, they are added at the end of the body with defer option:

That’s why Nuxt is perfect for beginners — you don’t have to do much in order to get a web application with a setup for both backend and fronted, all best performance practices included.

Challenge 1: Adding meta-tags and favicon

My first challenge with Nuxt was pretty trivial, but it was a nice first dive-in into the project infrastructure. After I added my first Nuxt page, which was as easy as creating a usual Vue component, I had to figure out a proper way to add meta tags and favicon.

In Nuxt meta tags are handled the same way as in Vue, with a help of vue-meta — a library that allows you to manage your app’s meta information in form of an object. That means that you don’t have direct access to <head> tag, instead, you should edit head property in nuxt.config.js:

This approach works for the meta tags if they are the same on each page, but if your meta tags are different for each page or rely on the store data, it is possible to add them inside each separate page with help of the head property inside component:

hid attribute is crucial here and has the same role as vmid attribute in vue-meta library. Based on hid property Nuxt understands that parent meta tag value should be replaced by child’s meta-tag value, instead of just being concatenated.

To add the favicon I tried to use the same approach by following the example for link rendering:

That’s very close to what we are looking for: we can change rel property to icon and then just try to figure out where to put favicon image itself.

In Nuxt there are two places where one might put images: assets and public folders. And while assets is for files that will be later transformed through Webpack, public is for any files that are used outside Webpack transformations. That’s where our favicon should go.

In the end, you should have something like that in your nuxt.config.js:

Unfortunately, along with meta-tags and links a lot of other things go to nuxt.config.js as well: environment variables, external scripts and even webpack configuration. The idea behind one-for-everything config was probably to make Nuxt configuration as easy and dummy as possible, but in reality nuxt.config.js has inside all the imaginable setup possible and is quite long and hard to read.

Challenge 2: Making the store work

As any framework, Nuxt is very opinionated. Luckily, some of that opinions come with a benefit. I like how Nuxt defines a folder structure, solving for you where to put what. It follows so-called convention over configuration approach where if you put modules in specific folders the app will work with a help of some Nuxt magic in the background.

For example, Nuxt comes with already integrated Vuex, but please keep in mind, that in order to make the store work you should follow a few conventions, which might not be very obvious from the start. First, you have to create astore folder and choose one of two store modesClassic Mode and Modules Mode based on the fact if you wanna your modules to be with a namespace or not.

Modules Mode is strongly based on convention, which means that you have only to create files with modules inside store folder and Nuxt will import them for you. In this mode in store/index.js you should export the root state as a function, and root mutations and actions as an object:

Based on that Nuxt will create the store for you, where modules will have namespace by default.

If you want more control over the store you can pick Classic Mode. In that case you will have to import store modules yourself and register them with the help of modules property (as you will usually do it with Vuex). For Classic Mode store/index.js should follow the convention as well and export the method that returns a Vuex instance:

In both modes module files structure is the same and should export state, actions and mutations:

Important 1: please pay attention that state here is a function. I started with declaring state as an object and eventually run into behaviour that state was not cleared between page reloads. As it is stated here:

If we use a plain object to declare the state of the module, then that state object will be shared by reference and cause cross store/module state pollution when it’s mutated.

Important 2: If you are moving the application from Vue to Nuxt, don’t forget to remove store import inside components. Some of my pages were importing store directly and passing it as a property further (as you usually do it in Vue application). I ended up having a few instances of the store. The problem was following— Nuxt imports the store for you, so you don’t have to do it explicitly (Attention: the following code is not correct):

Challenge 3: Fixing full page reload

The default mode in Nuxt application is universal meaning that server side will render SPA for a front-end. So I was surprised to see the whole page reload when navigating through the app. The reason turned out to be simple(though I spent some time trying to figure it out) — since I was migrating an already existing application I had a lot of usual <a href="link"> links all over the project.

Apparently in Nuxt if you don’t wanna end up with a full page reload while navigating between pages you should only use <nuxt-link> component. That way you will have all the benefits of SPA rendered by your server side.

Challenge 4: Adding multiple languages support

Let’s look further into the app folder structure and see what is the difference between plugins and middleware folders. Both of these folders have special names and follow convention over configuration approach. Adding multiple languages support to my web app was a good starting point to understand the difference between the two. Here is an example that I almost exactly followed in my implementation.

Plugins run before initialization of the Vue application, therefore they are a perfect place for code to be executed ahead of everything else. Be aware that plugins will execute twice: once on a server-side and once on a client-side. If you know that library that you are including in plugins will be used on cliet-side only, there is an option to explicitly disable execution on server-side with ssr:false option.

In this example first we initialise vue-i18n library inside plugins/i18n.js file:

As I’ve already mentioned — plugins code runs before root application initialization, therefore, they will only see initial store state values:

So in the beginning locale and fallbackLocale will have the same default value.

After the plugins — code inside middleware folder is executed. On server side middleware is called once after plugins and before rendering of a page. And on client side middleware is called each time page URL is changed. That means that middleware is suitable for any logic based on route change.

In our example locale is a part of the URL, so whenever it will be changed on client-side the code inside middleware will be executed, which will commit mutation to change locale value in store and change the value of app.i18n.locale:

Conclusion

These were my first adventures inside Nuxt framework. I tried to tell things in the same order that I ran upon. Hopefully, some of those will be useful for people who started with Nuxt the same way as I did. Even though some things were not obvious from the start, I still believe that the pros coming from having your application server side rendered are worth all the efforts spent on getting to know Nuxt.

TL;DR

  • Nuxt comes with a full Webpack preset, developer environment and best practices for performance.
  • nuxt.config.js contains all project setup including head property, Webpack extensions and environment variables
  • Nuxt uses convention over configuration approach and integrates Vuex out of the box
  • <nuxt-link> is the only way to navigate without a full page reload
  • In Nuxt plugins are being run before middleware both on the client- and server-side, unless you configure them not to run on a server with ssr: false config option

Thanks for reading this article. Please leave claps 👏 and share on twitter if you find it useful! And I will be happy to hear any feedback.

📝 Read this story later in Journal.

🗞 Wake up every Sunday morning to the week’s most noteworthy Tech stories, opinions, and news waiting in your inbox: Get the noteworthy newsletter >

--

--