cross platform development
Aleksander Furgal Published: 13 Apr 2023 10 min to read

SEO in Vue.js With Vue-Meta, Vue Router, and Other Useful Tools

Vue.js provides a significant edge over other frameworks when it comes to rendering web components and efficiently refreshing the view. It achieves this by updating only specified portions of the page, rather than repeatedly rendering all of it. Which, of course, has positive impact on performance.

That said, Vue presents considerable challenges for Search Engine Optimization (SEO). It is not SEO-friendly by default and requires substantial fine-tuning.

Vue.js is a JavaScript framework focused on building user interfaces. It offers a declarative and component-based programming model designed for efficiently creating complex single-page applications (SPAs).

In case of SPAs, typically most of their content loads dynamically by way of JavaScript. Because of that, a significant portion of a Vue-coded page can remain hidden to search engine crawlers.

Understandably, this results in poor rankings and low visibility in search engine result pages (SERPs).

To overcome this issue and ensure that Vue js applications are properly configured, developers must account for SEO from the start. Fortunately, there are multiple solutions available to address SEO-related concerns and optimize Vue apps for search engines.

Rendering options in Vue.js

With each passing year, web applications become increasingly complex. Although sophisticated functionalities enhance the users’ experience and provide them with more value, they also generate newfound concerns about their performance.

One way to tackle this issue is to delegate some of the rendering tasks away from the browser. When it comes to Vue, there are two main approaches to rendering: server-side rendering (SSR) and static site generation (SSG) powered by JAMstack.

JAMStack architectue

An illustration of JAMStack architecture.

#1 Server-side rendering

SSR is a technique that allows for rendering a website’s content and saving it as an HTML file. This is later stored on the server and sent to the browser, which then downloads the necessary JavaScript and takes over client-side rendering.

With server-side rendering, pages load faster, significantly improving user experience. And because page content is pre-generated, it is available for crawlers from the get-go and can be indexed as soon as server-side rendering is finished.

If you want to implement server-side rendering in your Vue app, you can read this tutorial or move your project to Nuxt.js.


#2 Static Site Generation with JAMstack

Another option for rendering Vue.js applications is static site generation with JAMstack.

This approach involves generating static HTML files at build time and serving those directly to the client. SSG can help improve the speed and performance of a web application and make crawling and indexing your content easier.

SSG provides time-to-content performance comparable to that of SSR applications, while being more cost-effective and easier to deploy due to static HTML and static assets.

However, SSG is limited to static data, which means that it can only be used for pages that use data fixed at build time and do not change between deployments. As a result, every time data changes, a new deployment is required.

Currently the most popular JAMStack solutions for Vue.js include Nuxt, Astro, Docsify, VuePress, and VitePress.


In today’s Internet, user experience is everything. Applications are expected to provide a smooth ride that combines great aesthetics with high functionality. SPAs are perfect for this, but they also tend to present significant challenges for SEO. To overcome this problem, SEO needs to be accounted for from the very beginning. Mike Jackowski COO, ASPER BROTHERS Let's Talk


Useful tools for Vue SEO

There are several tools available to optimize Vue.js applications for search engines crawlers.

These tools can help you identify and fix most common issues that prevent Vue.js applications from being crawlable. Some of them can also help you automate SEO processes by taking over the most time-consuming tasks.


#1 Pre-rendering solutions

To generate fully rendered HTML pages for dynamic content, you can use pre-rendering solutions, such as vue-prerender or ssr-vuejs-nodejs.

These middleware solutions intercept requests from search engine crawlers and forward them to a prerendering engine. The prerendering engine visits the site, executes all the JavaScript, fills templates with content, and returns it to a crawler.


#2 Vue-meta

Because Vue JS is an SPA framework, it’s contained inside a single HTML file. Every other template is stored within .vue files. Meta tags can’t be saved in .vue files, since adding titles, meta descriptions, and canonical URLs generates code-breaking issues.

Meta tags play an important role in Google rankings. Adding meta tags within the Vue JS framework is simple – you can use the vue-meta npm package.

Vue-meta is a Vue.js plugin that helps you manage your app’s metadata using Vue’s built-in reactivity. This includes meta tags, meta descriptions, and other important SEO elements.

Below is a small example of coding meta tags with vue-meta:

// Component.vue


metaInfo: {

meta: [

{ charset: 'utf-8' },

{ name: 'viewport', content: 'width=device-width, initial-scale=1' }




<!-- Rendered HTML tags in your page -->

<meta charset="utf-8">

<meta name="viewport" content="width=device-width, initial-scale=1">


#3 SEO-friendly links with Vue Router

In order to create SEO-friendly URLs, you can employ Vue Router. To do so in a way that facilitates search engine crawling and indexing, you should follow established best practices. These include:

  • Using history mode. You might have noticed hashes (#) in your URLs. Getting Google to index pages using this format is difficult and inconsistent. To remove hashes, you can set Vue Router to History mode. When you create a new project, it will ask you to choose between hash and history modes. However, if you’d like to modify your URL format in an existing project, you can switch to history mode using the following code: const router = new VueRouter ({ mode: ‘history’, router: […] })
  • Including keywords. Always use meaningful and descriptive keywords in your URL slugs, rather than generic names like “page-1” or “post-123”. This will help crawlers understand what your page is about.
  • But not too many. Yes, include target keywords in your URLs – but don’t overdo it. Avoid stuffing your URLs with too many keywords, as this can be seen as spammy and harm your rankings.
  • Sticking with hyphens. To separate words in your URLs, you should choose hyphens instead of underscores or any other characters. Hyphens are what crawl spiders generally prefer.
  • Managing your canonical tags. Use canonical tags to indicate the preferred version of a page when there are multiple URLs that refer to the same content. This helps avoid content duplication, which can negatively impact your SEO.

By following these steps you can make sure that your URLs are SEO-friendly.


#4 Lazy hydration in Vue vs. Delayed hydration with Nuxt

Vue app hydration can be costly. It can significantly increase your rendering time, especially for big sites with deeply nested HTML. This is why you want to minimize the amount of hydration that occurs. While most apps cannot avoid hydration costs, some SPAs allow for isolating  JavaScript hydration to specific segments.

This approach is called “partial hydration”. In general, with partial hydration only the interactive components of an application get hydrated, like the toolbar and comment section, while the content itself stays static. This allows you to conserve resources and significantly improve site performance, user experience, and, consequently, your SEO.

Lazy hydration, on the other hand, is a type of partial hydration that delays JavaScript hydration in time.

A prime use for lazy hydration is to delay loading JavaScript for components located outside the viewport. Because the user won’t be able to notice anyway, they can remain unhydrated until they come into view.

There are two ways of delaying hydration in Vue. You can find dedicated libraries on GitHub, such as vue3-lazy-hydration or vue-lazy-hydration if you’re still using Vue 2.

However, those can be complex, break HMR, and require significant tinkering to get them to work properly. Thankfully, lazy hydration can also be done in Nuxt. Following the release of Nuxt 3, the go-to package is nuxt-delay-hydration by @harlanzw. Nuxt Delay Hydration aims to provide optimized hydration with minimal setup.


#5 Nuxt SEO kit

If you’re using Nuxt, you might also be interested in Harlan’ latest GitHub contribution, nuxt-seo-kit.

It’s an all-in-one SEO module for Nuxt 3, allowing for configuring your robots.txt file, HTTP headers, meta tags, as well as your sitemap and structured data, among others. It also facilitates setting up automatic canonical URLs.


Vue SEO: best practices

When optimizing Vue.js applications for search engines, there are several practices worth remembering. To make your life easier, we’ve collected those we find most important. By following these guidelines, you can ensure that your Vue.js application can be easily found through search.


#1 Optimizing bundle size

Optimizing bundle size is essential for improving page load times and search engine visibility. This can be achieved by removing unnecessary dependencies, optimizing images, applying lazy loading, and minifying code.

In Vue, this can be taken a step further by eliminating unused code through tree-shaking.

Below you can find some useful materials dedicated to optimizing bundle size in Vue:


#2 Code splitting

Code splitting involves breaking up your code into smaller chunks and loading them only when needed, as reducing the amount of code results in shorter download and parsing times. The primary code bundle should only include essential dependencies and the initial view.

The classic example of code splitting involves dynamically importing components:

// Statically loaded

import Article from './components/Article.vue'


// Asynchronously loaded

const Article = () => import('./components/Article.vue')

There is a more advanced Async Component Factory syntax with options:

const AsyncComponent = () => ({

component: () => import('./components/Article.vue'),

loading: Loader,

error: Error,

delay: 300,

timeout: 4000


Vue 3 Code Splitting Syntax:

import { defineAsyncComponent } from 'vue'

const Article = defineAsyncComponent(() => import('./components/Article.vue'))


const AsyncComponent = defineAsyncComponent({

loader: () => import('./components/Article.vue'),

loadingComponent: Loader,

errorComponent: Error,

delay: 300,

timeout: 4000,

suspensible: false,

onError(error, retry, fail, attempts) {

if (error.message.match(/fetch/) && attempts <= 2) {


} else {





You can read more on code splitting under the following links:


#3 Lazy hydration

Lazy hydration involves rendering only those elements which the user currently needs, which can improve initial page load times and search engine visibility. For more information on Lazy Hydration in Vue.js and Nuxt see the above paragraph.


#4 Vue Composition API

Vue Options API Vs. Composition API

A comparison between Vue Options API and Composition API.

Vue Composition API can be used to create reusable logic and simplify component code, which can improve search engine visibility and make your code easier to maintain.

Using the Vue Composition API, we can create reusable functions that can be implemented across all components. This reduces code duplication and improves overall performance. It also helps the SEO by reducing bundle size.

Here’s a basic example of component code in Composition API:

<script setup>

import { ref, onMounted } from 'vue'


// reactive state

const count = ref(0)


// functions that mutate state and trigger updates

function increment() {




// lifecycle hooks

onMounted(() => {

console.log(`The initial count is ${count.value}.`)





<button @click="increment">Count is: {{ count }}</button>


You can dive into Composition APi using official docs and also this article.


#5 Async Components

Async components can be used to load components asynchronously, which can improve initial page load times.

Below you can find official guides on implementing Async Components:


#6 Splitting API requests

Splitting API requests involves breaking up API requests into smaller chunks and only loading the necessary data when it is needed.

API requests can be a major bottleneck for website performance, especially when loading large amounts of data. By splitting them up, developers can improve website performance and reduce loading times.

Additionally, by conditionally loading components based on user interactions, it’s possible to increase website performance even further.

Conditionally loading components

Conditional loading is a technique that allows for loading only those components that are currently needed for a particular user interaction.

This is yet another way of reducing overall bundle size and improving SEO and website performance.

Vue provides two directives for conditional loading:

  • The v-if directive to conditionally render components based on certain conditions.
  • The v-lazy directive, which allows you to lazy load components based on current user viewport.

Refining the loading state

Refining the loading state is commonly used for improving the user experience when the server is fetching API requests.

By displaying a loading icon while API requests are being processed, developers can give users a visual indication that parts of the page are still loading.

You can read more about refining the loading state here.


#7 Aborting API Requests

By aborting API requests that are no longer needed, you can free up network resources and reduce loading times.

Setting up API request cancellation also provides a way to prevent the application from freezing if the server fails to respond.

To cancel a request in Vue JS, you can use Axios. To learn more on how to set it up, refer to this article.


#8 Importing Dependencies

By limiting the number of imported dependencies to only those necessary, you can reduce the overall bundle size.

Using Bundlephobia, you can examine the cost of adding an npm package to your bundle. Most certainly, a lot of the modules and functions you have previously imported remain unused.

To optimize your dependencies further you should look into dependency injection.


#9 Inlining CSS

Inlining critical CSS involves embedding the CSS needed to render the above-the-fold content of a web page directly into the HTML file.

This approach eliminates the need for the browser to download and parse an external CSS file, reducing the number of HTTP requests and resulting in quicker rendering.

In Vue.js, inline styling can be done either by using object syntax or array syntax. You can read more on this topic here.


#10 Stale while revalidate

Stale-while-revalidate is a caching strategy that allows for displaying cached HTML while updated content is still being fetched in the background.

This technique helps reduce loading times and improve website performance. It is useful mostly for API requests that are less important in terms of content structure.

To implement stale-while-revalidate in your app, you can use the swrv package.



The described approaches to Vue SEO are not a silver bullet – many of them depend on the type of project, its architecture, or budget.

Nevertheless, the above information should provide you with everything you need to create a SEO-friendly Vue web app. Ensuring that your application is optimized for  search engine crawling and indexing is crucial for increasing your visitor numbers.


Call to action
Do you still have some questions about Vue SEO? Leave us a message and we’ll help you figure it out.

Aleksander Furgal

Content Specialist



Are you interested in news from the world of software development? Subscribe to our newsletter and receive a list of the most interesting information.


    RELATED articles