DateJul 27, 2021

NuxtJS state management with Vuex

In this article, let's set up a NuxtJS project with PWA(Progressive Web Applications) abilities. I'll explain the folder structure, various settings (which you will most likely use) and propose some libraries that you can use to make your life easier.

NuxtJS - A bit of Introduction

Coz you got to start from some basics. Skip if you already know

What's NuxtJS?

NuxtJS is a JavaScript framework built on top of VueJS. Vue is a framework similar to Angular, built by Evan You, an ex-googler. Nuxt abstracts most of the complex configuration involved in managing things like asynchronous data, middleware, and routing, which helps you focus more on your Business features.

Why NuxtJS?

Nuxt solves various shortcomings of VueJS. The first and foremost is - server-side rendering (SSR). By default, in a typical Vue or similar framework, you receive a barebone HTML with the necessary scripts defined. The client then downloads those JavaScript files which are then responsible for rendering your webpage.

Search Engine Crawlers don't like this..?

They want the whole webpage while parsing it. Since your barebone? Html does not have much for the crawler to parse, you get a pretty low SEO score. Hence, it makes sense for a dynamic web application to give the complete HTML in the first place.

Other than SSR, Nuxt brings about host other features such as File System automatic route generation, static site generation, inbuilt router, vuex store, etc.

Setting up the Base Project

Before we can customize our Nuxt project, let's first set up the essential files using the command line utility provided by yarn or npm.

Prerequisites

As suggested by Nuxt's official website, you should have:

  1. node - at least v10.13
  2. A text editor, I recommend VS Code with the Vetur extension
  3. A terminal for which VS Code's integrated terminal is recommended.

Commands

To initialize the base project, in your terminal execute

yarn create nuxt-app <project-name>

if you have yarn installed. If you have npm, you can execute:

npm init nuxt-app <project-name>

You can build from the source as well by following instructions in the official docs. But that's not for usual folks.

you'll get the following menu on executing the above command
you'll get the following menu on executing the above command

Let's go through the menu and options that you need to select:

  1. Project Name: If you change your mind from the one you entered in the previous command, you can change it here.
  2. Programming language: Choose between JavaScript and TypeScript. It's preferred to use TypeScript as it offers type checking. However, for a small project, it is not significant.
  3. Package manager: Choose anyone between yarn and npm. Whatever you prefer.
  4. UI Framework: Nuxt offers support for a host of UI frameworks. I tried Bootstrap and Vuetify but finally settled with Tailwind CSS as it offers a lot of customizability. It has a comparatively steep learning curve but is fun to use. ?
  5. Nuxt.js modules: You can supercharge your application by adding capabilities such as making REST calls (Axios), making a mobile downloadable web app (PWA), and having a Git-based CMS (Content). I only used Axios and PWA.
  6. Linting tool: EsLint preferred hands-down. However, make sure to integrate it properly with VS Code or the text editor of your choice.
  7. Testing Framework: Not necessary for a small-scale project.
  8. Rendering Mode: Decides if you want server-side rendering or not. My suggestion is to go for Universal mode unless you are creating a fully static website.
  9. Deployment Target: Decide whether you want to create a static hosting(then server it using an external server like Nginx or Apache) or have an npm/express server running to serve your content over a specific localhost port.
  10. Development tools: Not much useful. You can add the jsconfig.json file for VS Code though if you are using JavaScript and not TypeScript.
  11. Continuous Integration: If you want to have a CI pipeline setup using your preferred CI tool. I have a CI pipeline setup using GitHub actions since it has enough free usage and integrates well with GCP.
  12. Version Control: Use Git..duh!

Understanding the default Directory Structure

Critical Ones

# Layouts

You can define various layouts for your website. You can import the mostly static components such as navbar and footer in the Layout. This prevents reloading of such components on route change.

Know more about the Layout directory >>

# Pages

The pages directory contains your application's views and routes. Every .vue file in the page's directory is automatically converted into a route. This even supports dynamic routing by using folders and _slug.vue naming conventions. So if you have a folder named posts with slug.vue file, Nuxt will generate /posts/_slugroute.

Know more about the Pages directory >>

💡
You can prevent a .vue from becoming a route by defining a .nuxtignore file in your project's home directory and then adding the path of the page.

# package.json

This holds information about all the packages and scripts for your project. All the keys defined here are also accessible inside the app. You can maintain your application version here.

# nuxt.config.ts or nuxt.config.js

The nuxt.config.js file is the single point of configuration for Nuxt.js. If you want to add modules or override default settings, this is the place to apply the changes. In case you have a lot of settings, you can break your config file into individual .ts and .js files and then import them into the main nuxt.config file.

Know more about nuxt.config 

Not so Critical Ones

# Components

The components directory is where you put all your Vue.js components which are then imported into your pages.Nuxt supports Autoimporting your components but this slows down the Hot Reload. IMO it's not worth it in a small project.

Know more about the components directory >>

# Middlewares

The middleware directory contains your application middleware. Middleware lets you define custom functions that run before rendering either a page or a group of pages (layout).

Know more about Middlewares >>

# Assets

This is where you can store your external javascript, Images, and external styles. You can even store fonts and webpack assets.

# Static

Here you can store your static content like your website's favicon.ico, images, robots.txt, etc. The static directory is directly mapped to the server root and contains files that likely won't be changed. All included files will be automatically served by Nuxt and are accessible through your project root URL.

To know more about Static directory >>

# Plugins

The plugins directory contains JavaScript plugins that you want to run before instantiating the root Vue.js Application. This is the place to add Vue plugins and to inject functions or constants. Every time you need to use Vue.use(), you should create a file in plugins/ and add its path to plugins in nuxt.config.js.

To know more about Plugins >>

# Store

The store directory contains your Vuex Store files. The Vuex store comes with Nuxt.js out of the box but is disabled by default. Creating an  index.js  file in this directory enables the store. This is available only if you haven't disabled store in your nuxt.config.js file.

To know more about NuxtJs store >>

tsconfig.json or jsconfig.json

It's not necessary to create this file, however, it will help your Text Editor during linting and error checking. I'll suggest you create this file. An example file:

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "Node",
    "lib": [
      "ESNext",
      "ESNext.AsyncIterable",
      "DOM"
    ],
    "esModuleInterop": true,
    "allowJs": true,
    "sourceMap": true,
    "strict": true,
    "noImplicitAny": false,
    "noEmit": true,
    "experimentalDecorators": true,
    "baseUrl": ".",
    "paths": {
      "~/*": ["./*"],
      "@/*": ["./*"],
      "Images/*":["./assets/images/*"],
      "Styles/*":["./assets/styles/*"]
    },
    "types": [
      "@nuxt/types",
      "@nuxtjs/axios",
      "@types/node"
    ]
  },
  "exclude": [
    "node_modules",
    ".nuxt",
    "dist"
  ]
}

Configuring NuxtJS

Let's now move on to adding configurations to nuxt.config.*s file to customize the Nuxt set up according to your project needs.

Since the nuxt.config.js file is the point of most Nuxt configurations. It can become huge and cumbersome to maintain. So I suggest dividing the major settings such as headmetabuild into their files. I have several sub-configuration files -

You can then import them into your main config file.

💡
I'd like to point out that since I use a typescript version of nuxt.config file. My code will have minor differences from someone who is using nuxt.config.js file.

Here's the Nuxt configuration file of this website at the time of writing this article-

import { resolve } from 'path'
import type { NuxtConfig } from '@nuxt/types'
import { head, meta, manifest, build, utils } from "./config";

const config: NuxtConfig = {
  modern: !utils.isDev && 'client',
  // Watch config subfiles
  watch: ['~/config/*', '~/plugins/*'],
  head,
  meta,

  env: {
    baseUrl: process.env.BASE_URL || 'https://limosyn.com',
    GitHubUrl: "https://github.com/limosin",
    LinkedInUrl: "https://www.linkedin.com/in/ss1804",
    TwitterUrl: "https://twitter.com/limosyn_com",
  },

  alias: {
    Images: resolve(__dirname, './assets/images'),
    Styles: resolve(__dirname, './assets/styles'),
  },

  router: {
    trailingSlash: false
  },

  generate: {
    fallback: true
  },

  loading: {
    color: '#ed64a6',
    height: '5px'
  },

  sitemap: {
    hostname: process.env.BASE_URL || 'https://limosyn.com',
    trailingSlash: true,
    exclude: [
      '/privacy',
      '/legal'
    ],
    defaults: {
      changefreq: 'daily',
      priority: 1,
      lastmodrealtime: true
    }
  },

  css: [],

  plugins: [{ src: '~/plugins/prism', mode: 'client' }, { src: '~/plugins/vue-gtag' }],

  // Auto import components (https://go.nuxtjs.dev/config-components)
  components: false,

  buildModules: [
    "@nuxt/typescript-build",
    "@nuxtjs/composition-api/module",
    "@nuxtjs/tailwindcss",
    '@nuxtjs/pwa',
  ],

  modules: ["@nuxtjs/axios", "nuxt-svg-loader"],

  axios: {},

  tailwindcss: {
    configPath: "~/config/tailwind.config.js",
    cssPath: "~/assets/styles/tailwind.css"
  },

  pwa: {
    icon: {
      source: 'static/imgs/logo.png'
    }
  },

  manifest,
  build
};

export default config

Let's look at each option one by one -

  1. watch: Add the files which on making changes to, you wish to hot reload your application. So if I make changes to my configuration files, the app automatically reloads with the newer version of the files.
  2. env: You can add your environment variables here.
  3. alias: Set smaller and concise names for various directories where you store files such as styles, images, scripts, etc.
  4. router: There are more options available to configure, but I have just set trailingSlash to false. This prevents Nuxt from automatically adding a / at the end of every URL.
  5. loading: Nuxt has built-in loading animation which you can show while changing routes. So for example, this website shows a red line when you are changing routes.
  6. plugins: You can define plugins that you wish to use in your application. I have used prism.js to add styling to code components. Basically whichever features need Vue.use(feature) can be added here.
  7. buildModules and modules: Certain libraries need to be defined under buildModules or modules for you to use in your code. This is dependent on the library itself. Check the library's documentation to know the exact configurations for using the library.
  8. headmetamanifest and build: These have been defined externally and then imported. There is no need to write key name as it's implied. this means that -
{
	..
    head: head,
    meta: meta,
	..
}
// is same as

{
	..
    head,
	meta
    ..
}

For your reference, here are the external configs that I'm using -

# head.ts

export default {
  meta: [
    { name: 'theme-color', content: '#9f7aea' },
  ],
  link: [
    {
      rel: 'stylesheet preload',
      as: 'style',
      type: 'text/css',
      href: 'https://fonts.googleapis.com/css2?family=Raleway:wght@600;700;800&family=Quicksand:wght@300;400;500;700&display=swap',
      crossorigin: 'anonymous'
    },
  ],

  __dangerouslyDisableSanitizers: ['script'],
  titleTemplate: c => c ? `${c} - Limosyn.com` : 'Limosyn.com - Somil Singhai',
  script: [
    {
      type: 'application/ld+json',
      innerHTML: JSON.stringify(
        {
          '@context': 'http://schema.org',
          '@type': 'Person',
          address: {
            ..
          },
          name: 'Somil Singhai',
          image: '',
          jobTitle: 'Software Developer',
          url: 'https://limosyn.com',
          sameAs: [
            'https://twitter.com/limosyn_com',
            'https://github.com/limosin',
            'https://linkedin.com/in/ss1804'
          ]
        })
    }
  ]
}

# build.ts

import path from "path";
import utils from './utils'

export default {
  parallel: utils.isDev as boolean,
  cache: utils.isDev as boolean,
  publicPath: "/assets/" as string,
  transpile: [/^vue-if-bot($|\/)/, /^vue-cookieconsent-component($|\/)/] as any,

  postcss: {
    plugins: {
      tailwindcss: path.resolve(__dirname, "tailwind.config.js"),
      "postcss-nesting": {},
      "postcss-import": {}
    }
  } as any,
  extend(config, ctx) {
    // Run ESLint on save
    if (ctx.isClient && ctx.isDev) {
      config.module.rules.push({
        enforce: "pre",
        test: /\.(js|vue)$/,
        loader: "eslint-loader",
        exclude: /(node_modules)|(\.svg$)/
      });
    }
  }
};

# manifest.ts

export default {
  name: 'limosyn.com',
  lang: 'en',
  short_name: 'limosyn.com',
  scope: "/",
  start_url: '/?source=pwa',
  display: 'standalone',
  background_color: '#edf2f7',
  theme_color: '#9f7aea' // Allows the purple color on Address bar - mobile
}

Finally...

In this article, I have tried to present a pretty comprehensive way of setting up a starter project for your personal website. You can copy my settings and even better - test out your own. A few takeaways that I believe you can take are -

  1. Steps to set up a Nuxt starter project.
  2. Significance of various directories in a Nuxt starter project.
  3. nuxt.config.ts/js is the single point of most Nuxt configurations.
  4. If you have a lot of settings, you can split you nuxt.config file into subfiles.

See you in the next article. Until then, Adios!??‍