In this post, I want to show JS developers how easy it is to make Vue SEO-friendly.
I'll go through:
General SEO tips you should always apply.
Specific Vue.js SPA SEO issues.
Tools to fix them with server-rendering & prerendering.
A technical Vue.js SEO example using prerender-spa-plugin.
This is a fully updated version of our Vue.js SEO resource. The original one was followed by two other pieces about JS frameworks SEO issues, for React, and Angular. There's also a video version of this content you can find at the end of this post.
General SEO tips
Before we explore SEO issues specific to JavaScript SPAs, let's cover the best practices developers should adopt when building search engine optimized sites.
This shortlist is inspired by some of the biggest SEO players such as Moz, Backlinko, and Ahrefs. I've added links to their resources should you want to dig deeper.
→ Meta tags 🏷️
Meta tags are low-hanging fruits. They let you show search engines precisely what your content is about.
However, not all tags are born equal. You should focus your energy on only a few of them, namely:
Meta content type—Has to be present on every page, declares your character set for the page.
Title—Choose a unique title that catches searchers' attention while accurately describing the content of the page. Keep it concise!
Meta description—It should convince SERP (Search Engine Results Page) visitors to click on your link to find answers to their specific research intent.
vue-meta is a solid tool to manage page meta info in Vue 2.0 components.
Social tags can also have a significant impact on SEO as they help your content to spread on social platforms, thus increasing your SEO social signals. There are specific tags for all the big platforms (Facebook, Twitter, Pinterest, Google+).
If you're not aware of Google's mobile-first indexing, well, consider this a wake-up call! Google is now giving more love to smooth mobile experiences than desktop ones, and so should you.
NativeScript powers cross-platform Vue.js mobile apps.
→ HTTPS 🛡️
A missing HTTPS certification or a broken config could penalize your website. Isn't it understandable that search engines are inclined to push sites that are trusted and certified, after all?
Even if it wasn't for SEO, you probably want to offer a platform as secure as possible for your users. So no reason not to meet this criterion!
People's attention span is short—searchers are quick to abandon slow-loading pages. Google knows that and, to offer the best UX to searchers, will penalize slow sites.
Googlebots themselves are pretty impatient and won't wait for a script longer than 5 seconds. If you don't meet this timeout, you risk not getting your content rendered adequately. Speed it up!
A sitemap act as a map of your site's architecture for search bots. Included in it should be the pages you consider to be good-quality landing & navigation pages, worthy of indexation.
Sitemaps might not be that useful for smaller websites, but it's still a valuable SEO tool to consider.
Building your domain authority remains a key SEO tactic. How does Google know that you've become an authoritative resource? By having other relevant domains linking to yours.
There's no secret formula here, to accomplish this you need to work hard at crafting great content. Content that others want to share and use as a resource on their own site!
The more links you get from relevant sources, the more your authority will rise, the more you'll earn Google favors when it comes to rankings.
For the following tutorial, I decided to use the blog demo we made in an earlier post because, honestly, it looks awesome. If you're interested in knowing how it was built, see this post.
Although it's a great piece of work visually, it's not set up ideally SEO-wise. It's a complete SPA that search engines might have a hard time crawling & indexing.
Why is that?
A single-page application adds content to pages dynamically, which has its benefits, but brings two significant issues:
We don't really know to which extent Google is able to crawl and correctly render JS. They've been saying for a few years now that Googlebot is capable of doing so. However, these claims are always followed by a "but" or two.
Google isn't the only search engine out there. If things are still shaky on its side, we know for a fact that the other players out there don't crawl JS yet, thus not encountering the actual content of the page.
So there's no way around it. If you want to make sure your Vue.js website/app ranks on SERPs, you have to act on it.
Your options?
Server-side rendering
With an SSR setup, you have the rendering logic done directly in the backend, in a Node.js environment. HTML views are then returned to the client, ready to be served to search bots.
It's excellent for time-sensitive apps where you want to offload as much logic as possible on the server but comes with a cost. You're going to need a robust infrastructure to handle the stress added to the server, asking for more development time. You might also slow down your system in the process.
But if you can handle it, it's definitely the way to go for bigger apps. Nuxt.js is without a doubt the tool to use for your server-rendered Vue.js SPA.
Prerendering
Sometimes though, server-side rendering might feel overkill, like in my demo's case. For a small SPA with only a few pages, prerendering will do the trick just fine. Even more so if your only concern is SEO.
This way, there's no need to attach your Vue.js app to any server. Rendering is done client-side with the use of third-party plugins such as:
Prerender.io - Compatible with all the most popular JS frameworks, including Vue.js.
prerender-spa-plugin - A Webpack plugin that will compile your pages into static pages. Makes all the content available in the source, and indexing is a breeze.
The latter is very easy to use and was built by a Vue.js core team member, so it suits my use case perfectly here.
Vue.js SEO technical example using prerendering
Time to fix the Vue.js blog shown earlier. Using prerender-spa-plugin, I'll have a Vue.js SPA that's in Google's good graces in no time.
Prerequisites
Basic knowledge of Vue.js
Node.js & npm installations
1. Installing prerender-spa-plugin
Configuring the plugin is very easy. In this setup, I'll run the plugin only when building for production. You don't need prerendering when actively developing components.
Start by installing the plugin from npm.
npm install prerender-spa-plugin
Then, open the build/build.js file. There, you'll find custom things for production build. You'll need to import the plugin and add these lines at the top of the file:
Why PrerenderSPAPlugin.PuppeteerRenderer? Because you'll need to customize the renderer. You need to inject some data to let the application know when it's being prerendered. There are things like Disqus comments that you won't be able to prerender.
In a 100% SEO-friendly app it would be preferable to benefit from these comments as rendered content on your pages. If your working with a static site generator, you should consider Staticman for static user-generated content.
You'll need to fetch all your blog entries and generate a list of routes that will be prerendered. In a real production scenario, you'd probably want to call your headless CMS API or an external service that provides your content. In this case, the data lives in simple JSON files.
You'll have access to the window.__PRERENDER_INJECTED.prerendered variable when the site is prerendering. This will be useful to exclude content you don't want to prerender.
You also need to specify the wait for the app.rendered event.
In this demo, I'll use Pace, which is an awesome library to add a progress bar on a site quickly. However, you need to make sure the loading is complete before prerendering the site if not, you'll end up getting a random progress bar on multiple pages.
2. Configuring Vue.js for prerender
You'll then need to apply some minor changes in some components. To do so, I changed the way Pace was started. I got rid of the startup.cs file and put everything in main.js.
Open the main.js file and change how your App components are included:
Once your main component is mounted, start Pace and dispatch the app.rendered event when initial loading is completed.
The last update you have to do is to make sure Disqus is not loaded when the site is being prerendered.
Open BlogPost.vue file where you'll include the Disqus component.
Because of the way the prerender-spa-plugin was configured at first, you'll have access to a variable that indicates you that the site is being prerendered.
Update the showComments method this way:
// /BlogPost.vue
showComments() {
// This is injected by prerender-spa-plugin on build time, we don't prerender disqus comments.
if (window.__PRERENDER_INJECTED &&
window.__PRERENDER_INJECTED.prerendered) {
return;
}
setTimeout(() => {
this.commentsReady = true
}, 1000)
}
3. Building your SEO-friendly Vue.js site
You're now ready to build your site. In your terminal use this command:
npm run build
Then, if you look at the dist folder, you should see all the posts in the read folder. A static HTML file has been generated for each post:
prerender-spa-plugin is a neat plugin. It's effortless to configure and flexible enough to handle async use cases like the one we had with Pace.
I spent about 2 hours to figure out how to wire all these things together and update our initial application to support prerendering.
As I mentioned earlier, the plugin is straightforward and best suited for simple applications like a static blog. If you want to build something more complicated, I'd suggest you look at Nuxt instead!
If you enjoyed this post, please take a second to share it on Twitter. Got comments, questions? Hit the section below!
About the author
Charles Ouellet Co-Founder & Product Owner
Charles has been coding for over 16 years (if we count his glory days with HTML/PHP). He's the founder and lead developer behind Snipcart and has spoken at several web development events including the WAQ, VueToronto and WinnipegJS. Formerly a backend programmer, he's now fluent in JavaScript, TypeScript, and Vue.js. Charles is also a big believer in the Jamstack.