Do you know about Hulu's JavaScript fail story?
Back in 2016, the online video streaming service experienced a 56% visibility drop due to bad JS handling.
A nightmare no business wants to go through.
To avoid such disasters, you have to know what you're doing when working with modern JavaScript frameworks.
Here at Snipcart, we love Vue.js, but are entirely aware of the SEO issues with a Vue.js singe-page application.
In this post, I want to show JS developers how easy it is to make Vue SEO-friendly.
I'll go through:
prerender-spa-plugin
.This post was followed by two other pieces about JS frameworks SEO issues, for React, and Angular.
Before we explore SEO issues specific to JavaScript SPAs, let's cover the best practices developers should adopt when building search engine optimized sites.
As someone already said: “The best place to hide a dead body is the second page of Google search.”
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 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:
Learn more: SEO Meta Tags, by Moz.
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+).
Learn more: Must-Have Social Meta Tags for Twitter, Google+, Facebook, and More, by Moz.
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.
Learn more: Mobile SEO: The Definitive Guide, by Backlinko.
NativeScript powers cross-platorm Vue.js mobile apps.
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!
Learn more: HTTP vs. HTTPS for SEO: What You Need to Know to Stay in Google’s Good Graces by ahrefs.
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!
Learn more: On-Site SEO: Page Speed, by Moz.
A sitemap act as a map of your site's architecture for searchbots. 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.
Learn more: XML Sitemaps: The Most Misunderstood Tool in the SEO's Toolbox, by Moz.
You can generate a sitemap.xml by vue-router configuration.
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.
Learn more: Link Building for SEO: The Definitive Guide, by Backlinko.
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:
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?
With a 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.
Nuxt.js rendering process [source]
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:
The latter is very easy to use and was build by a Vue.js core team member, so it suits my use case perfectly here.
Time to fix the Vue.js blog shown earlier. Using [prerender-spa-plugin](https://github.com/chrisvfritz/prerender-spa-plugin)
, I'll have a Vue.js SPA that's in Google's good graces in no time.
Prerequisites
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.
Read the feed.json
file and generate your routes:
Add the plugins to webpack configuration:
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](https://github.hubspot.com/pace/docs/welcome/)
, 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.
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:
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:
That's it! How easy was that, right?
See the live demo here
See GitHub repo here
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!
I originally published this post on the Snipcart blog and shared it in our newsletter.