If you’re using NuxtJS to build out a web application or advanced website, blogging might come as an after thought.
Maybe your customer just wanted a basic website with some special features fitted just for their business, like a custom form or cost calculator, and you spun up a Nuxt instance to get it done quickly and reliably. You did a great job, so they’ve come back for more. They want to start publishing posts on their website to provide their customers with a feed of written content (and boost their SEO).
One option would be to create a subdomain, fresh install a WordPress instance, then point to it with links on their main website. This isn’t ideal because they now have two websites, you’d like to keep everything on their primary domain, and there’s no need to send website visitors back and forth between two domains just to have a content feed.
Introducing the WordPress REST API
With WordPress’s REST API functionality, you can host the WordPress instance anywhere and simply pull content data into your Nuxt website on its own time. You can pull all posts, individual posts, categories, tags, and even customized data sets using Advanced Custom Fields and Custom Post Types.
To be clear, you won’t be able to use everything that the WordPress ecosystem has to offer. Some plugins provide a REST API for your front end to interact with, some do not. Given that the WordPress instance is now headless, no plugin with visible components will be useful here, but there’s still plenty to be gained from the Content Management aspect of the world’s most popular CMS.
Table of Contents
- Setting up a headless WordPress instance
- Plugging NuxtJS into the WordPress REST API
- Styling the WordPress REST API in NuxtJS
- Setting web page metadata with WordPress REST API post data
- Use the WordPress REST API for more than a blog roll
Setting up a headless WordPress instance
To use the WordPress REST API with NuxtJS or VueJS or any other front end, it first must exist. If it does exist, you’re good to go. If not yet, all you have to do is spin up a WordPress instance and a few changes in the settings.
Over 40% of websites are running on WordPress, at this point there are many website hosting companies who offer automated installs. You can pick a website host and walk through their setup guides.
I prefer to host WordPress instances on the same server as the Nuxt app, under a subdomain of the primary domain. I like this option because it keeps everything in one place, but there’s a bit more setup to do.
You’ll need to install PHP, because that’s what WordPress is written in. You’ll also need a MySQL database ready to go, and a server like Apache or NGINX to configure which domains listen to which ports or directories. My preference is NGINX, it’s already running Nuxt, so I opt to tap into that for WordPress site. You’ll also need to set up an SSL certificate for the WordPress site’s domain.
Linode and Digital Ocean both have how-to setup guides so you can set up WordPress on their Ubuntu instances. If you’re running another linux distro, you’ve probably got the guides already.
Once you’ve got it up, and you’re in the wp-admin dashboard there are a few things to do.
One thing you want to change in the settings is the permalink format. Go to Settings > Permalink Settings and change the format to Post Name.
The Second thing you’ll want to do is disable WordPress’s customer facing views. This way, if someone somehow gets to that subdomain all they see is a barrier or nothing at all. I accomplish this by using an ‘under construction’ plugin that’s customizable enough to be made to not look like an page under construction but an error or blank page.
Third, you’ll want to install and activate the Jetpack plugin to make grabbing the url for featured images a little bit easier.
Plugging your NuxtJS app into the WordPress REST API
Now you have a WordPress instance running and routing REST access to your content data. To test it out, swap out the filler with your domain and either visit or send a get request to https://your.domain.com/wp-json/wp/v2/posts
Implementing this get request in NuxtJS to load this content into a page can be done in the async asyncData life cycle with Axios. To access your posts and categories, it looks a bit like this:
async asyncData({ params }) {
const posts = await axios.get(
`https://your.domain.com/wp-json/wp/v2/posts?per_page=88`
);
const categories = await axios.get(
`https://your.domain.com/wp-json/wp/v2/categories`
);
return { posts: posts.data, categories: categories.data };
},
Notice we’re also passing parameters. Here you have to decide how your urls are structured. As you can see above, I prefer: https://inlandapp.com/content/_slug but you could make it /blog/_slug or /aritcles/_slug using the paging structure provided by Nuxt.
Ah, paging structure, this reminds me that WordPress does not want to send you all the posts at one time. It wants to paginate them. If you want to pull in more than ten pages at a time for your front-end to work with, you’ll have to add a per page parameter. You can set it of a value of your choice, you’ll see I implemented this one above: ?per_page=88
Getting all posts for a category from your methods looks like this:
methods: {
allPosts() {
axios.get(
`https://your.domain.com/wp-json/wp/v2/posts`
).then(response => {
this.posts = response.data
});
},
sortPosts(id) {
axios.get(
`https://your.domain.com/wp-json/wp/v2/posts?categories=` + id
).then(response => {
this.posts = response.data
});
},
searchPosts(query) {
query = query.replace(/^\s+|\s+$/g, '');
// Make the string lowercase
query = query.toLowerCase();
// Remove accents, swap ñ for n, etc
var from = "ÁÄÂÀÃÅČÇĆĎÉĚËÈÊẼĔȆÍÌÎÏŇÑÓÖÒÔÕØŘŔŠŤÚŮÜÙÛÝŸŽáäâàãåčçćďéěëèêẽĕȇíìîïňñóöòôõøðřŕšťúůüùûýÿžþÞĐđßÆa·/_,:;";
var to = "AAAAAACCCDEEEEEEEEIIIINNOOOOOORRSTUUUUUYYZaaaaaacccdeeeeeeeeiiiinnooooooorrstuuuuuyyzbBDdBAa------";
for (var i=0, l=from.length ; i<l ; i++) {
query = query.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i));
}
// Remove invalid chars
query = query.replace(/[^a-z0-9 -]/g, '')
// Collapse whitespace and replace by -
.replace(/\s+/g, '-')
// Collapse dashes
.replace(/-+/g, '-');
axios.get(
`https://your.domain.com/wp-json/wp/v2/posts?search=` + query
).then(response => {
this.posts = response.data
});
}
},
Grabbing an individual post looks like this:
async asyncData({ params }) {
const post = await axios.get(
`https://your.domain.com/wp-json/wp/v2/posts?slug=${params.slug}`
);
let title = post.data[0].title.rendered
.replace(/–/g, '-')
.replace(/“/g, '"')
.replace(/”/g, '"')
.replace(/’/g, "'");
function removeTags(str) {
if ((str===null) || (str===''))
return false;
else
str = str.toString();
return str.replace( /(<([^>]+)>)/ig, '').replace( /\r?\n|\r/ig, '');
}
let excerpt = removeTags(post.data[0].excerpt.rendered);
let date = new Date(post.data[0].date);
var months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
date = months[date.getMonth()] + ' ' + date.getDate() + ', ' + date.getFullYear()
return { post: post.data[0], date: date, title: title, content: post.data[0].content.rendered, excerpt: excerpt, slug: params.slug };
},
Notice the replace functions below the api call, those are there because otherwise the title is received a little wonky with ASCII codes instead of characters like hyphens and quotations.
You’ll also notice I’ve stripped html tags from the excerpt since they aren’t needed when you’re trying to pull the excerpt as a string and not html markup.
In your template, make sure to inject your title and content as v-html=”title” and v-html=”content” otherwise the tags will show.
Styling the WordPress REST API in NuxtJS
To style the classes tagged onto the content pulled in from your WordPress REST API, you’ll need to make sure you do not have ‘scoped’ in your style tag. Here’s a quick cheat sheet for the element classes I’ve targeted as a good starting point.
<style>
.article-content {
max-width: 880px;
margin: 24px auto 36px auto;
padding: 0 24px;
}
.article-content .wp-block-image img {
width: 100% !important;
height: 100% !important;
margin: 0 0 16px 0
}
.wp-block-embed__wrapper {
position: relative;
width: 100%;
height: 0;
padding-bottom: 56.25%;
}
iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.wp-block-quote {
border-left: 3px solid #888;
margin: 18px auto;
padding: 18px;
}
.wp-block-quote p::before, .wp-block-quote p::after {
content: '"';
font-weight: 900;
}
.article-content ul {
padding: 18px 0 24px 0;
}
.article-content ul li {
padding: 4px 0;
font-size: 0.9em;
}
.article-content ol {
padding: 8px 0 18px 0;
}
.article-content ol li {
padding: 4px 0;
font-size: 0.9em;
}
.article-content p {
margin: 24px 0;
}
.wp-block-code {
width: 100% !important;
overflow: scroll;
overflow-y: hidden;
scrollbar-width: thin;
scrollbar-color: #444 #000;
}
.wp-block-code::-webkit-scrollbar {
background: #000
}
.wp-block-code::-webkit-scrollbar-track:horizontal {
background: #000;
}
.wp-block-code::-webkit-scrollbar-thumb:horizontal {
background-color: #444;
border-radius: 10px;
border: 3px solid #000;
border-left: 5px solid #000;
border-top: 5px solid #000;
}
p > a {
word-wrap: break-word;
}
.share-link-text {
color: #eee;
border-bottom: 1px solid #eee
}
.share-link-text:hover {
color: #fff
}
</style>
Notice this array of style classes only covers a few of WordPress’s block types. My priority here was to provide for the article’s content wrapper to expand into the area I’m styling in view, make images and iframe consistently 100% width, handle block quotes, ordered and unordered lists, and anchor link colors.
Setting web page meta tags with WordPress REST API post data
For SEO optimization, you want to make sure that all your meta tags for the page that’s generated contain information from your post. Featured image for social media cards, titles, excerpts, slug for the canonical and permalink, and more. You can place the variables you’ve assigned to your post data in the head space of your script export.
head() {
return {
title: `${this.title} - InlandApp.com`,
meta: [
{
hid: "description",
name: "description",
content: `${this.excerpt}`,
},
{
hid: "og:description",
property: "og:description",
content: `${this.excerpt}`,
},
{
hid: "og:title",
property: "og:title",
content: `${this.title} - InlandApp.com`,
},
{
hid: "og:image",
property: "og:image",
content: `${this.post.jetpack_featured_media_url}`,
},
{
hid: "og:image:alt",
property: "og:image:alt",
content: `${this.title} - InlandApp.com`,
},
{
hid: "og:url",
property: "og:url",
content: `https://inlandapp.com/content/${this.slug}`,
},
{
hid: "twitter:image:alt",
name: "twitter:image:alt",
content: "Inland Applications",
},
{
hid: "twitter:image",
name: "twitter:image",
content: `${this.post.jetpack_featured_media_url}`,
},
{
hid: "twitter:title",
name: "twitter:title",
content: `${this.title} - InlandApp.com`,
},
{
hid: "twitter:description",
name: "twitter:description",
content:
`${this.excerpt}`,
},
],
link: [
{ rel: "canonical", href: `https://inlandapp.com/content/${this.slug}` },
{ rel: "shortlink", href: `https://inlandapp.com/content/${this.slug}` },
],
};
}
Confirm that these values are being generated as meta tags by using the developer tools (ctrl + shift + c) or viewing source (ctrl + u).
Use the WordPress REST API for more than a blog roll
With the WordPress REST API, you can pull not just posts but pages and custom post types.
WordPress pages are useful in this context because you can either hard code pages, or pull all pages and list them dynamically in a menu, on your front-end and your customer will be able to modify content there as easily as any other blog post on WordPress.
Custom Post Types are useful to separate out different steams of posts. You can also add in the Advanced Custom Fields plugin to modify what kind of information those posts will be able to store. Both of these WordPress plugins create endpoints that your front-end can interface with via the REST API.