Skip to content

The Great Migration: My Journey from WordPress to Astro—Pitfalls, Triumphs, and Deployment Diaries


JavaScript is what the devil uses to torture sinners - but I like Astro.

When Astro first launched, I didn’t think much about it. I roll my eyes at every new JavaScript framework. There’s one every two weeks. If we can fix or agree to abandon JavaScript, I think the world would be a better place - rant over.

For those unfamiliar, Astro is a web framework for content-driven websites. It’s blazingly fast and allows you to integrate with your existing UI framework. I won’t dive into how it works under the hood or its architecture; Instead, I’ll walk you through how I moved this blog away from WordPress, configured TinaCMS for content management, and OneSignal for subscription management and deployed it to Cloudflare pages.

Before we dive in let’s understand why I’m making this switch in the first place. The primary reason is that I don’t want to manage servers and all the security aspects that come with hosting a WordPress site.

I’ve migrated about 90% of client sites that were once powered by WordPress to Nuxt.js and Contentful because I do not wish to deal with that anymore. It’s more work upfront to build the front-end and does not offer the same design “flexibility” but the long-term benefits are worth the tradeoff.

The secondary reason is to improve performance and provide a better experience to you, my readers. Given that my bounce rate is now under 30% I’d say you all have been liking the change.

Let’s dive in

Astro setup

Because I’m lazy and this is a side project I opted to use a predefined theme.

npm create astro @latest-- --template satnaing / astro - paper

If you want to build your UI from scratch run the command without the template flag.

Tina setup

TinaCMS does not come pre-built with this template so the next step is to install it by running:

npx @tinacms/cli@latest init

There should now be a tina directory in the root of your project. It contains the configuration file needed to model the collections. A collection is basically a group of fields for a specific content type. It takes a name, label, path and list of fields.

If you’re trying this with the same template as I am then you’ll need to set the content path in tina/config.ts to path: “src/content/blog” since the template uses astro content.

After creating a collection update the build and dev commands in the package.json and push to github.

”build”: “tinacms build && astro build && jampack ./dist"

"dev”: “tinacms dev -c \“astro dev\""\

At this point, we need to configure Tina for use. Register for Tina Cloud at and create a new custom project by authenticating with Github and selecting your repo. Now grab your client ID and Content (Read Only) tokens and save them as environment variables in your project. Inside tina/config.ts set clientID and token.

The configure Tina to work with your domain.

Run npm run dev to start Astro and TinaCMS locally. To manage Tina go to http://localhost:{port}/admin/index.html

Content Migration & Management

Since I hadn’t done much writing, there were only a handful of posts to migrate, so it was easy to copy/paste into TinaCMS. For a larger site, I’d need to find a way to export the posts and pages to markdown. I’m sure there’s a plugin.

When it comes to creating a new post or page Tina, being git-based gives me the option of manually writing my markdown and pushing to github or using the admin panel with the WYSIWYG editor. Choosing the latter will have the markdown created automatically and pushed to Github when in production.

One Signal Configuration

What’s a blog without a subscribe CTA? I like OneSignal because it offers push notifications to many different channels, though for this setup I’m only concerned with web push and email.

OneSignal Web Configuration

<script client:only is:inline defer>
  window.OneSignalDeferred = window.OneSignalDeferred || [];
  OneSignalDeferred.push(function (OneSignal) {
      appId: "treat this like a password and keep it secret",
OneSignal Email Configuration

With this, users will see a popup asking them to subscribe by entering their email. Now that we’re capturing emails, we need to use OneSignal’s API to send notifications when a new post is published.

Since we’ll be deploying this as a static site via Cloudflare pages the best way to accomplish this is by creating a worker that makes a POST request to OneSignal.

Deploying to Cloudflare

Final Thoughts

The overall developer experience of Astro has been very good. The template I chose uses React and since I’ve been working with that a lot recently there was no break in mental model and it was easy to customize. I like the control I have over my content with TinaCMS. My only complaint is that the admin section breaks regularly due to cloudflare’s caching so it’s always best to upload content on the * domain rather than the custom domain.