

Building Your Own Blog with Nuxt Content and Tailwind
source link: https://blog.openreplay.com/building-your-own-blog-with-nuxt-content-and-tailwind
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Hey there, and welcome to this tutorial. This will be an in-depth guide on creating your own personal blog using Nuxt, a meta-framework built on top of VueJS. Nuxt is designed for developer productivity, and includes lots of great features out of the box to help us create awesome websites. We’re also gonna be using TailwindCSS to style our blog and make it look 🔥
What we’ll be building
We’re gonna be building a static blog website with NuxtJS and TailwindCSS. We’re gonna use the Nuxt Content module to allow us to easily write and display blog posts using Markdown. By using Nuxt Content, we can supercharge our posts by including HTML or even Vue components inside the markdown files, providing us a seamless and flexible authoring experience to create awesome content - without needing a Content Management System (CMS).
We’re gonna add tags and images to our posts and present them in a nice grid on our homepage. Additionally, we’ll add a Table of Content to each individual blog post so users can easily navigate our content. And using the Tailwind Typography plugin, we’ll style our blog posts nicely including syntax highlighting for code snippets. Finally, we’ll make sure our blog posts has the proper meta information so Google and social platforms can index our content.
This is what the homepage of our blog will look like:
And then our blog posts will show their image, tags, ToC and content like so:
Let’s get started!
Setup with create-nuxt-app
To setup the project, make sure you have NodeJS installed on your machine, as well as access to a terminal (any terminal will do). You’ll also need either Yarn or npm (comes with NodeJS)
Start by running:
This will ask you a bunch of questions to help you setup the project. Feel free to pick any options you like, but for simplicity, this is what I went with:
The key thing is that you add the “Content - Git-based headless CMS” module, as that’s the module that will be driving our blog. You can also pick another styling framework if you prefer, but I’m gonna use Tailwind CSS as it provides awesome results and a really nice workflow. It’ll also be helpful when we get to styling our blog post content.
When it’s done, you can cd
into the project. Open it with your favourite editor (I use VS Code) and run yarn dev
(or npm run dev
). Open your browser and head to localhost:3000
to get greeted by the Nuxt welcome screen:
Now go back to your editor, where Nuxt has set up some files and folders for us to use:
- The
pages
folder will include any routes/pages in your application (more on that later). - The
components
is what we will use to store our smaller components that pages can use. - The
static
folder is where we’ll be putting all the images needed for our blog posts. - The
content
folder is because we’re specifically using the Nuxt Content module, and we’ll use it to contain all our individual blog posts. Nuxt has even made an examplehello.md
file we can get started with.
Last piece of the setup, if this is your first time working with Vue, you’ll also want to install the Vetur
extension in VsCode. Just search for it in the Extensions Panel and install it, as it’ll give you a bunch of awesome features and quality of life improvements when writing Vue code.
Creating and displaying blog posts with Nuxt Content
Now we’re ready to start building! Let’s start by adding a few more posts. We’ll use something called frontmatter
(which comes automatically with Nuxt Content) to describe some basic data about each of our posts. If you open up hello.md
, the frontmatter is the information between the dashes at the top:
We’re gonna be adding some fields to our frontmatter, so rename hello.md
to first-post.md
and make it look like the following:
Now, copy paste this post in the same directory to create a couple more. Update their filename, title, image path and tags to something else to make them unique:

The image
key refers to an image path, and can be any URL on the web. In this case, I’ve added 3 random images from Unsplash in my static
folder. These images will then be available at localhost:3000/first-post.jpg
for example. Just make sure the name of the file matches the path provided exactly, and that the images are inside the static
folder.
Display post info on homepage
Next up, we’re gonna show links to our posts on the homepage, which we’ll do in the index.vue
file. We’re gonna use an built-in Nuxt function called asyncData
to fetch the post data and make them available to our homepage.
We’ll add this function to our <script>
tag:
Here we’re using the $content
feature from Nuxt to load in some post data (the title, image, tags and slug of each post). We then sort them in descending order (newest first), and call fetch()
to execute the function. Finally we’re returning posts
, which exposes the post data to our template as an array of posts. Check the console to see all the data we just grabbed:

Let’s update our template to show the title of each post (still in index.vue
):
If all went well, you should see your 3 post titles on the screen - and remember that this title is coming directly from the frontmatter of each individual post, so feel free to update the titles to something else. We’re also fetching the slug, image and tags as we’ll need those in a bit.
Creating post routes
Of course we want each post to have it’s own page to display the body content, tags, image etc. We’ll do that by creating a dynamic route in Nuxt.
Inside your pages folder, create a new folder called blog
. Then, create a single file called _slug.vue
inside the blog
folder. This will be our dynamic route that will display an individual post. Add the following code:
Now what’s going on here? Because we named the file _slug.vue
and put it inside a blog
folder, Nuxt will render this page component whenever it hits a URL at /blog/something. And when it does, the params
object in asyncData
will have access to the slug (the part of the URL after blog/
, in this case “something”). We then use that slug to fetch a single post using the $content
feature again. By returning post
, we expose this data to the template, just like we did on the homepage when fetching all the posts.
To verify that it works, visit localhost:3000/blog/first-post
and you should see the post title “My first blog post” on the screen. Because first-post
is the slug, that slug gets matched up with our markdown file first-post.md
.
Creating proper post previews
Now that we know our blog post routes are working, let’s go back to index.vue
and make our post previews more appealing and actually link them to the individual posts. Let’s update the post loop to use a new PostPreview
component that we’ll create in a second, and pass each post
object to this component. We’ll also wrap the loop in a list with some grid classes to make them align nicely. With these changes, our template in index.vue
should look like this:
Let’s then create the PostPreview.vue
component inside the components
folder. This component will accept a post
object as a prop, and then display the post image and title, and act as a link to the post itself. We’ll even use another cool new Nuxt feature to get optimized images called Nuxt Image.
Paste the following in your PostPreview.vue
file:
Now when you look at the homepage, you’ll see the title of each post along with its tags. But now they work as links so you can click each one and go to that post’s page.
To get the images working, we’ll need to install the @nuxt/image
module. Do that by running:
yarn add @nuxt/image
Then, head into your nuxt.config.js
and add it to your buildModules
array (alongside the Tailwind module that was automatically installed in setup):
Finally, add the following to nuxt.config.js
as well. This will tell @nuxt/image
to optimize for different screen sizes and add a preset we’ll use in our PostPreview
:
Reload the homepage, and you should now see our images show up:

Adding a layout
Great! But the page itself could use some max width to make it look better. There’s many ways we could do this, but let’s use another Nuxt feature: Layouts.
Create a new folder in the root of your project called layouts
. Inside that, create a file called default.vue
and paste the following in there:
Now all our pages will follow this layout and won’t span the entire page. The <Nuxt />
component is a special component that will render the content of the page we’re currently on (either the homepage or a blog page in our case). With this new layout, our homepage looks a bit tighter:

Displaying post content
Now that we got a nice looking homepage, let’s move on to the actual blog posts in our _slug.vue
file. There’s gonna be a lot going on here, so start by setting your template up like this, and we’ll break it down bit by bit:
Firstly, we’re including the post image on top of the page to catch the attention of our reader. Then we add a Table of Content using a special key on our post
object that Nuxt Content has added for us called toc
. This key is an array with ID’s of every headline that’s part of the body content. We use this to add <NuxtLinks>
linking to each headline by their ID:
Next up, we have some code to display a link back to the homepage, the tags of the post, and the title:
And finally, a single (but very important!) line of code:
This is where we tell Nuxt Content that we want to render the entire body of each blog post. To test it out, let’s head over to first-post.md
and fill in some example content, right below the frontmatter:
This is just some example markdown to test how everything looks when we write it as part of a post.
When you save the file and head to your post page, you should see all the newly added content. You’ll also notice that it’s basically unstyled and doesn’t look great. That’s because Tailwind removes most default HTML styling to make it easier to work with and avoid browser inconsistencies.
But luckily, we can install a plugin to provide even better typography defaults. Run:
yarn add @tailwindcss/typography
To use this plugin, create a tailwind.config.js
file in the root of your project and add the following:
All this code is Tailwind specific, so don’t mind if it doesn’t make sense to you. But the key is in the plugins array, where we require our newly installed typography plugin. Restart your dev server and head back to the blog post - you should see the content looking MUCH better with great typographic defaults 🔥
Open Source Session Replay
Debugging a web application in production may be challenging and time-consuming. OpenReplay is an Open-source alternative to FullStory, LogRocket and Hotjar. It allows you to monitor and replay everything your users do and shows how your app behaves for every issue. It’s like having your browser’s inspector open while looking over your user’s shoulder. OpenReplay is the only open-source alternative currently available.

Happy debugging, for modern frontend teams - Start monitoring your web app for free.
Additional content features
Now we got a basic blog up and running, and can start writing real posts using markdown - but like I mentioned earlier, Nuxt Content includes even more cool features.
Live Editing
One of those is called Live Editing, and you can use it by simply double-clicking on the rendered nuxt-content
component in the browser. This will open a textarea directly on the page where you can update your content. When you click outside this area, the changes are saved to the actual file. Check out the link to see it in action or try it yourself!
Including HTML and Vue components in posts
Because of Nuxt Content, the markdown files in our content
folder basically gets super powers. We can include any random HTML in here if we’d like. In your second-post.md
, add this HTML code:
Now you’ve got a blue box, directly in your blog post. And we can even take it a step further and include entire Vue components! Say you have a Newsletter.vue
component that’s a form for readers to sign up to your newsletter. You can easily include that too by just adding:
<newsletter></newsletter>
anywhere in your markdown. NB: The component name has to be kebab-cased
and you must include both the opening and closing tag (unlike in .vue
files where you could write <Newsletter />
)
All put together, you could have a post with this content:
And it would get rendered in the browser as:

Sweet! These features allow us to create more engaging blog posts, since we can basically include anything we’re able to do with Vue right inside our posts 🚀
Meta tags, SEO and social links
I’d say we’ve got a pretty solid basic blog setup here! You can write content using markdown, HTML and Vue components, and everything looks nice and smooth. But one thing I often feel is overlooked when building your own blog, is thinking about how you’re actually gonna acquire readers.
Now that your blog is up and running and you start writing content, you wanna make it easy for interested people to find it - and one of the best ways for this is through SEO (Search Engine Optimization).
SEO is basically how Google and other search engines can index your content and present it to people searching for certain keywords or phrases. It’s an entire science, but there’s a few things you can do to boost your SEO. At the same time, we can add some information to make links to our posts look good when sharing it on social media.
Search engines and social media platforms look for this information by parsing the <head>
tag of any given website. They look for specific meta tags that we’re gonna add using another feature of Nuxt, the head()
function. The head()
function works by returning an object that will be injected into the page’s actual <head>
tag.
So to do that, we’re gonna add the head()
function below asyncData
in our _slug.vue
file. Make it look like this:
This will set the page title to be equivalent to the title of the post. It’ll also use the post title to set proper meta tags for Open Graph (Facebook) and Twitter. And for both of these, it’ll also set the meta image to be the image from the post.
NB: Remember to update https://my-site.com/
to the actual root domain of your own website.
With these in places, Google will have an easier time parsing our content and we’ll have good looking preview cards when sharing links to our posts on social media.
Additional features
Whew, you made it to the end! I’d say we got a pretty solid foundation for our blog going here - but there’s still a bunch of stuff that would make it even better. That’s why I encourage you to play around with some of these additional features and see if you can add them on your own:
- Add a description to each blog post. This could be used to show 1-2 lines of the post on the preview card. You can also use it to add additional meta tags to make Google even more likely to index your content properly.
- Add a tag filtering system. Right now we only got 3 posts, but in the future you might have way more. It would be nice if the homepage had a list of all available tags, that the user could use to filter the posts down to their interests.
- Make it responsive. Our layout currently is only optimized for large screens, so you could add additional styling to make it look great on mobile too.
- Page transitions. The blog works fine as it is, but it would be cool to have some animation when entering a blog post or going back to the homepage to achieve a better “look & feel”.
- Build a real site around your new blog. The blog you just built is good, but it would be even better if you could integrate it as a part of your portfolio website with proper navigation, other content etc.
- Deploy it to Netlify. A blog is no good if nobody can read it - so I strongly encourage you to push the code to GitHub and deploy it live to Netlify for the world to see!
Conclusion
Thank you for reading! I hope you enjoyed the tutorial and learned a thing or two about Nuxt and creating a blog using Nuxt Content and the cool features it has to offer.
To learn more about Nuxt, checkout the official website at NuxtJS.org.
And finally, I’d love to know your thoughts on this article and Nuxt / Nuxt Content in general. So feel free to hit me up on Twitter at @madsbrodt with any feedback you might have.
Until next time!
Recommend
-
197
两个多月的空余时间里,博客差不多搭完。 感谢surmon.me,此 blog 才会诞生。 感谢七牛云提供资源...
-
6
A Nuxt PWA to Improve Québec Citizens' Lives July 16, 2020 ...
-
25
If you have worked with Vue or Nuxt application, I am sure you have had a hard time managing the sessions, storing cookies, and working with localStorage. Luckily we have a
-
11
Building a Strapi E-Commerce: Nuxt.js Tutorial & Live Demo September 09, 2021 ...
-
1
Building Universal Vue.js Apps with Nuxt.js Full Stack Radio
-
5
BackHow to build a CMS-powered blog with NuxtApril 11th, 2022 · 9 min read
-
4
How To Easily Create a Blog With Nuxt Content
-
9
May 24, 2022 - AnnouncementsAnnouncing Nuxt Content v22 years after the release of Content v1, we are proud to announce the second version of Nuxt Content built for Nuxt 3.2 years after...
-
7
5 Awesome Modules to use in your Nuxt Project Nuxt.js is an amazing framework that makes it easy to build Vue.js applicatons with static-site performance. It’s especially useful for making dynamic experiences (e.g. sites with user login...
-
5
Building a performant search bar in Nuxt with Algolia & Storefront UIOct 2nd 2023 engineering
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK