Migrating to Astro: The End
- Migrating to Astro (3 Part Series)
My adventures converting my old Next.js site to use Astro.
- 3 The End
Howdy again! We’re in the final mile now. We got the individual blog post pages working last time. In this final installment, we’ll add a few more pages, look at RSS feeds and social sharing images, and add a dark/light theme toggle. Let’s get started!
#Add About Me page fff60ed
As I mentioned in the first series post, Astro can automatically convert Markdown files in src/pages/
to HTML. For the About Me page, I did just that. I created src/pages/about-me.md
and copy/pasted the contents from my old site. The text was present, but neither the structure nor the styling were correct. I fixed the first issue by creating src/layouts/MarkdownPageLayout.astro
just for Markdown pages to pull in the site layout.
Astro supports layouts specific to Markdown files. For these cases, we have the MarkdownLayoutProps
type helper that sets the type of Astro.props
for us. We’re passing in a generic here to define the structure of our frontmatter. Everything in the template is straightforward and matches fairly closely the HTML we added to the individual blog posts page.
To fix the styling issue, I added Tailwind’s typography plugin via pnpm add @tailwindcss/typography
, copying over my theme overrides from my old site and applying the necessary Tailwind classes in a shared Markdown
component. With these changes, I can add as many .md
pages as I wish and they will all automatically render correctly.
#Add category list page cdf1c2a
For the category index pages, I didn’t need to update any content collections since we defined the category collection for the main index page nor did I need to add any new components as everything I needed had already been built. I created src/pages/category/[slug].astro
and added logic to get the current category based on the slug as well as any posts that referenced the current category. As with the individual blog posts page, we are using getStaticPaths
to tell Astro about all of our categories it needs to render.
The template renders the list much like the main index page. One difference is that we’re adding an additional page header and subheader here. Another is that the ArticleBlock
doesn’t display the category link since we’re already on the category listing page.
#Add theme toggler 7341d58
This step was interesting because I got to embed client-side JS in an Astro component. My old site had a button in the footer that allowed the user to change which theme to use. The old site used a Next.js-specific library though so I was going to need to use a more generic solution this time around. Conveniently, Astro had a tutorial for how to add a theme toggle, so I copied that while making a few small tweaks for my use case.
The cool feature here is the custom is:inline
directive on the <script>
tag. This tells Astro to not bundle the JS with the main bundle, but to leave the <script>
tag exactly where it is when rendering the page. Notice too how we’re adding an old school event listener to our theme toggler button. Because Astro components are rendered to HTML without any sort of client-side templating engine or framework, handlers can’t be passed as attributed to tags e.g. onClick={handleToggleClick}
in React. The // ...
line contains boilerplate code for getting/setting the theme using a combination of media queries and window.localStorage
1. I’ll refer you to the Astro tutorial if you’d like to explore that code further.
#Add social images 7daa74f
With my old site, I used a clever library called resoc to generate social images for each of my blog posts. Resoc allows me to write HTML which it loads up in a browser via Playwright and takes screenshots. This gives me immense control over what my social images look like. I won’t go into the whole setup; that’s another blog post in and of itself. I will say that getting it integrated into Astro was fairly effortless. I first copied over the generateSocialImages
function from my old site into utils.ts
.
This function generates two different images, one optimized for Twitter and the other a more generic OpenGraph size and shape2. Next, I installed the necessary dependencies, copied over the resoc configuration files, and added meta tags for the images to the SEO.astro
component. Most everything worked. One issue I didn’t realize until I generated the site for the first time on Netlify was that PNPM’s strict hoisting (or non-hoisting, really) meant I had to add sharp as a direct depencency. There was also a strange CommonJS/ES Modules bug that I patched via pnpm patch
. With those two fixes in place, the social images generated smoothly.
#Add RSS feed 915776a
Thankfully, Astro has a first-party package for generating RSS feeds. This was not the case with my old site and I was happy to see how easy it was get an RSS feed added. I ran pnpm install @astrojs/rss
and created src/pages/rss.xml.ts
with the following contents:
Most of it is boilerplate, but I do want to point out two things. First, this page has a .ts
suffix which means we have to be more instructive in telling Astro what to render for this page. This is why we’re using the GET
named export here to tell Astro how to generate the contents of this file3. Second, we’re doing a lot of async
/await
here but that’s not a problem since this code will only be run during the build and most of the data we’re waiting on is already going to be held in memory by the Astro build process. According to the logs on my site’s most recent build, the RSS page took 6ms to generate.
#Final touches
There were just a few last things I needed to get the site finished up. I copied over a few files for Netlify from my previous site. I also parallelized the social image generation code. That cut the genration time for those pages in half. Lastly, I added Plausible analytics back into my site. With that, I created a PR for posterity and merged it. 🚀
#Conclusion
What a ride! I’m glad I took a chance on Astro. Considering my last site rewrite took me the better part of six months on and off, I was hestitant to do another, but I got all this done in 24 hours! While raising tiny humans! I really am impressed with what Astro provides out-of-the-box. The developer experience is top-notch and I haven’t even explored all of it’s features. It has first-class support for view transitions, enables island architectures, allows link prefetching, has many different integrations, and much more I’m going to have fun with. Any content-heavy sites I’m building, Astro will be the first framework I reach for.
#Footnotes
-
I’ve since updated the theme toggle to support dark mode, light mode, and system mode. The logic is broken up between two components:
ThemeToggle.astro
andThemeToggleScript.astro
. ↩ -
This image generation process was ripe for optimization. I also found that including the
{{ hash }}
in the file path caused issues because the hash wasn’t deterministic enough. The solution I went with was to move the final image processing to an Astro plugin. The end result turned out nicely. ↩ -
Other verbs are available, e.g.
POST
, when using SSR and running Astro via edge/serverless functions. When doing traditional static site generation, only theGET
verb is supported and it determines the resulting output for a given page. ↩