Out with the old
I remember first grasping the fundamentals of HTML and CSS back in 2014. “I’m a coder now!”, I thought to myself as I continued to apply position: absolute
to all the elements on the page as if it were a drag-and-drop editor.
Clearly I didn’t quite understand the intended document flow at that stage, but with a little time (and many StackOverflow visits), I eventually found my footing.
The catalyst for this journey was a desire to have my own blog. At the time, I would frequent the blogs of my favourite creators to catch a glimpse behind the scenes. Their choice of design and layout complimenting the work it housed.
After some research, WordPress emerged as the obvious solution. Too stubborn to use a pre-built theme, I began building a theme from scratch, guided by an abundance of tutorials on YouTube.
I became familiar with PHP not long after, with loops and conditions beginning to make regular appearances in my code. I would install Advanced Custom Fields with each new theme for added flexibility and customisation.
This blog went through many iterations over the years, often being saturated with unnecessary design features. I really enjoyed the process though, and that was all that mattered.
My knowledge plateaued for a while. I was happy with my abilities, and WordPress catered for all my needs.
Over time though, I started to become interested in web apps. I knew I would need more granular control over databases and other aspects of the infrastucture.
It wasn’t until a little pandemic hit in 2020 that I would use some new-found spare time to dive into React. Of course, in doing so, I would unknowingly expose myself to the ever-expanding world of JavaScript frameworks.
In with the new
Astro piqued my interest for a number of reasons. Firstly, there was a lot of talk online about it outperforming other, more established frameworks of similar capabilities. Who doesn’t love an underdog?
Secondly, Astro was placing Static Site Generation (SSG) at the forefront. I knew this blog was going to remain relatively small, with minimal amounts of interactivity. From my point of view, there was no need to serve anything other than static HTML.
But the biggest draw for me came with Astro’s 2.0 release. A release that introduced Astro Content Collections with type-safe markdown.
At this point, I was convinced this was the correct decision. No need for for an external headless CMS, SSG-centric, and what appeared to be a familiar approach to components and pages.
I was sold, time to install and explore.
Routing
Routing here is determined by the structure of your files in the pages directory. This is fairly standard, however I often find that as a site grows in complexity, I end up with files named similarly throughout the project, making it a little difficult to see what’s what at a glance.
📂 src
📂 pages
📂 blog
📂 category
- [slug].astro
- index.astro
- [slug].astro
- index.astro
- index.astro
Components & Pages
On the other hand, Astro components and pages are a joy to work with. There’s a fenced area at the top for your JS code. Below that, regular HTML for layout and styling. Need to reference the data above? Just whip out your curly braces and channel your inner PHP.
---
const title = “Hello, World”
const content = “Astro is awesome!”
---
<div class=“wrapper”>
<h1>{title}</h1>
<p>{content}</p>
</div>
<style>
.wrapper {
display: flex;
}
</style>
You’ll also notice the <style></style>
tags at the bottom of the above example. Styles defined here will be scoped locally to the component (unless otherwise specified). I really like this - it provides the modularity of CSS Modules, with the convenience of having everything visually accessible within the same file.
Content Collections
Content Collections help us organise our entries.
In this case, we have a ‘blog’ content collection folder. Inside this, we have our blog entries written and saved in markdown.
It may look something like this:
📂 src
📂 content
📂 blog
- first-post.md
- second-post.md
- third-post.md
Markdown files have an indentical layout to Astro components and pages. At the top, a fenced area for our frontmatter (consider this our post metadata). Underneath, we have the body of our post.
---
title: Rebuilding a WordPress blog with Astro
date: 2023-05-02
category: dev
---
## Out with the old
I remember first grasping the fundamentals of...
Once we have a few completed entries, we can nip back over to one of our Astro pages and import the getCollection
function.
This function allows us to retireve a collection of entries that meet given conditions using the data we provided in the markdown frontmatter.
In WordPress terms, this is the equivalent of WP_Query
.
---
import { getCollection } from 'astro:content';
// grab all content from ‘blog’ collection
const posts = await getCollection(“blog”, ({data}) => {
// second argument takes a function for filtering
// return posts marked as published, and not future dated
return data.published && data.date < new Date();
});
// sort result by date, descending
posts.sort((a, b) => b.data.date - a.data.date);
---
<section>
{posts.map((post) => (
<h1>{post.data.title}</h1>
))}
</section>
The resulting array can then be manipulated however you see fit. In the above example, I’m sorting the array to show posts in reverse chronological order, then mapping the title of each post to HTML.
Dynamic Routes
Dynamic routes represent multiple routes of related content.
/blog/first-post
/blog/second-post
/blog/third-post
We define dynamic routes in the pages directory, using square brackets in the file name.
📂 src
📂 pages
📂 blog
- [slug].astro
- index.astro
Dynamic routes must export the getStaticPaths
function. Inside this function we provide Astro with an array of all the paths we want to generate.
---
export function getStaticPaths(){
return [
{ params: { slug: first-post } },
{ params: { slug: second-post } },
{ params: { slug: third-post } }
]
}
---
Updating that array by hand with each new entry would be tedious. We can use getCollection
in tandum with getStaticPaths
to update it dynamically with each new entry we add to the collection.
---
import { getCollection } from "astro:content";
export async function getStaticPaths(){
// grab our "blog" entries from the content folder
const posts = await getCollection("blog", ({data}) => {
// exclude unpublished and future dated posts
return data.published && data.date < new Date();
});
// map our entries to an array
return posts.map((post) => ({
params: { slug: post.slug },
props: { post }
}));
};
---
Using getCollection
to create this array ensures that our content folder remains our single source of truth for our dynamic routes.
We then pass the props to our HTML and build out the template for our entries.
Conclusion
Building with Astro is very intuitive - this likely due to my experience with React and NextJS. Astro components and routing are very similar to their counterparts in the aforementioned frameworks. Getting up to speed won’t take too long for anyone familiar with this space.
Obviously there’s plenty more to Astro than the items outlined above. Layouts, and Type-Safety are both important concepts to become familiar with if you’re building a blog with Astro. Definitely check out the Astro documentation for information on these topics.
For me, this framework has been a treat. I’ve found it to be a simple and elegant solution to rebuilding this blog, and I’ll be continuing to build with Astro going forward.
So here we are. Nothing of WordPress remains, save a collection of cherished memories. The result? An extremely performant, statically-generated website, with a near frictionless deployment process.