You Don't Need A Framework - Build A Dynamic Blog with HTML & Vanilla JavaScript

Months ago, when I was reworking parts of my website, I began questioning every part of the tech stack. During that process, I thought about how I would build a dynamic blog with vanilla JavaScript. Eventually, I realized I was still going to end up using a framework. So, I worked through the main concepts in my head to scratch the itch, then shelved the idea...until, I couldn't ignore the fact that the itch was still there.

This article is simply about the JavaScript side of building a dynamic blog. It is not a claim on the best approach for such a system, nor an argument for or against using vanilla JS or a framework. We're just looking at how this could get done.

Let's get into it

First things first, we need a few HTML and JS files, so let's get a new project going in your favorite editor (feel free to code along).

Create a new folder, name it whatever you want, and open it in your editor. Create the following files and folders:

  • index.html

  • blog.html

  • blog/post.html

  • js/blogCards.js

  • js/blogPost.js

  • js/blogPostsData.js

No Webpack, no Vite, no package.json... and no Tailwind ๐Ÿคฏ. Ahhh, how refreshing.

Boilerplate code

Here is the relevant HTML

blog.html:

  • Include the script tag for blogCards.js in the head
    <script type="module" src="/js/blogCards.js" async></script>

    screenshot of code in blog.html

blog-post.html:

  • Include the script tag for blogPost.js in the head
    <script type="module" src="/js/blogPost.js" async></script>

    screenshot of code in blog-post.html

Also, the blog post data

blogPosts.js:

  • Add as many blog post objects as you want. I had ChatGPT generate some dummy data here.

    screenshot of code in blogPosts.js

Start and preview

This is a good time to start the project to see what we have so far. If you're using something like VS Code, view the project using Live Server.

Dynamically populate the blog listing page with blog-post cards

Let's start by importing the data at the top of the blogCards.js file:

  • import { blogPosts } from "./blogsPosts.js"

We're going to have to place each blog card inside of a the container element we created in the blog listing page, so let's declare a blogCardsContainer variable and store a reference to the container in it:

  • const blogCardsContainer = document.querySelector("#blog-cards-container");

Now, we can loop over each blog post in our data, create the cards, and then finally append them to blogCardsContainer:

screenshot of code in blogCards.js

The first three lines inside of the forEach loop:

  • Creates a div element to be styled as the card

  • Adds a "blog-card" class to the div

  • Sets a "data-id" attribute on the div with the value of current post id to uniquely identify each blog post card (in case you want control for individual cards).

Here's one of the most important parts of the blogCardHTML content we created above: We set the href attribute to the individual post.html page, but, and here it is, we add in a slug parameter which is the value of the blog post slug. This is how the individual post.html page knows what blog post it's supposed to be loading when navigated to.

Now that we know which post is supposed to be showing, all we have to do is create the HTML structure for our blog post and load in the post data, similar to how we did for the blogCards - except we're not doing a loop this time. Let's apply the logic...

Dynamically building the individual blog post page

When our post.html file loads, we already know that we're going to have a post slug as a parameter. This is how we know which post to work with, so let's set it up.

  • At the top of blogPost.js, import blogPosts

You may know about the window.location object. But, to isolate the query parameters from the URL, we can access the search property on window.location, and we'll pass that into the URLSearchParams interface:

  • const params = new URLSearchParams(window.location.search);

The URLSearchParams interface exposes a 'get' utility method allowing us to retrieve the exact parameter we're looking for:

  • const slug = params.get("slug");

Perfect. Now, let's find the exact post:

  • const blogPost = blogPosts.find((post) => post.slug === slug);

Like with the blogCards, let's grab the blog post container on our post.html file so that we know where to inject the post after we build it:

  • const blogPostContainer = document.querySelector("#blogPost");

Okay, now let's build the blog post HTML structure, and inject into the blog post container, but only on the condition that the post was actually found. For now, we handle the situation where the post isn't found by displaying some simple text.

screenshot of code in blogPost.js

Try it out

Go back to the blog listing page. Navigate to different posts, and watch them be displayed onto the page.

๐ŸŽ‰ Congrats! You now know some essential concepts needed to build a dynamic blog system in vanilla JavaScript ๐ŸŽ‰

If all this has made you curious to find out how much more it would take to get this blog to a complete, production-ready level, I encourage you to scratch that itch - It'll be worth it.

Here's a hint to some modifications and optimizations that would make this project better:

  • SPA experience

    • load all content and pages using a single div in the index.html file, just like frameworks do

    • this will offer better URLs and faster load times since you won't be loading full pages

  • Store blog posts in an external db

  • Use a mouseover event listener on blog post cards to prefetch html content

Conclusion

Although there are many changes you could make to this project, the point is you don't always need every framework feature. Often, all you need is simplicity.

If this article helped you learn something, I'm so glad. But there are three important takeaways I hope youโ€™ll leave with:

  1. Remember to question things from time to time.

  2. Go beyond the question, and let your curiosity scratch the itch (think about the solution)

  3. You don't always need a framework ๐Ÿ˜๏ธ

0
Subscribe to my newsletter

Read articles from Patricio Salazar directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Patricio Salazar
Patricio Salazar

I'm a Software Developer with a full-time "9-5" working on Side Projects and sharing about it.