Why I finally moved
I had already gone from Hexo to Jekyll, then to VuePress, and eventually back to Hexo. For a while, I really thought that was where I would stay for good. Then Astro came along and stirred something up again.
Lately, Astro has been everywhere in the blogging world. I watched a lot of people move from Hexo and other platforms to Astro, but I did not pay much attention at first. That changed when I clicked into Astro’s theme gallery out of curiosity and saw AstroPaper. It looked unbelievably smooth. After that, I opened Astro’s introduction page, and that was basically enough for me: okay, time to migrate.
The migration
The move itself went fairly smoothly, but I still had to put in some effort to fit everything to my own needs. I kept a few notes along the way.
Keeping old links working
Because Astro’s page paths are different from Hexo’s, I needed to handle compatibility so my old links would still work.
In Hexo, I used the permalink field to customize article URLs. Astro, however, builds post paths from the markdown filename. For example, one of my old posts, “2023 Year-End Summary,” had its permalink set to post/2023-summary.html in Hexo, but in Astro it became posts/2024-01-01-2023-summary.
Fortunately, this is not hard to deal with in Astro. I created a page called post/[slug]/index.astro and handled the routing there, mapping incoming URLs to the permalink field inside the markdown files.
First, I updated src/content/config.ts so it would read the permalink field from the markdown files:
const blog = defineCollection({
type: "content",
schema: ({ image }) =>
z.object({
...
permalink: z.string().optional(),
}),
});
Then I handled the permalink matching inside src/pages/post/[slug]/index.astro. The code is a bit long, so I will not paste it here, but if you are interested, you can take a look at src/pages/post/[slug]/index.astro.
Adding a table of contents
AstroPaper itself does not come with a TOC, so I added one for easier reading. I borrowed the idea from the Astro Cactus theme and built a TOC component. You can see the result on the right side of this article.
Adding Disqus comments
AstroPaper also does not include a comment system out of the box, so I added a Disqus component by following another article.
Switching code highlight themes
Astro uses Shiki for syntax highlighting and supports both light and dark themes natively. But after I finished setting things up, I found that the code theme did not actually change when I switched to dark mode.
After checking through it, I realized Astro writes the highlighting styles directly into the style attribute of the code tag. When dark mode switches, those styles do not change automatically. To make the code theme switch properly, I had to override the styles manually in base.css.
html[data-theme="dark"] .astro-code,
html[data-theme="dark"] .astro-code span {
color: var(--shiki-dark) !important;
background-color: var(--shiki-dark-bg) !important;
/* Optional, if you also want font styles */
font-style: var(--shiki-dark-font-style) !important;
font-weight: var(--shiki-dark-font-weight) !important;
text-decoration: var(--shiki-dark-text-decoration) !important;
}
Shiki also supports transformer-style highlighting, like the diff-style blocks above, but those need extra styling before they will render properly. If you are curious, src/styles/base.css is worth a look.
RSS support
Astro already has an RSS plugin, so that part was easy enough. By default, though, it does not output the full article text. If you want full content in the feed, you can use sanitize-html and markdown-it to parse and render it, following the official documentation.
AstroPaper only provides the /rss.xml path, while my previous blog supported both /rss.xml and /atom.xml. To avoid breaking subscriptions after updates—if there are any subscribers left out there—I simply copied rss.xml.ts and renamed the copy to atom.xml.ts. If you need other feed paths, the same approach works.
How it feels overall
I had also done quite a bit of customization on Hexo-based blogs before, but the process always felt a little awkward. Maybe that had something to do with the EJS or Swig engines. Astro, on the other hand, uses syntax that feels close to Vue and gives you routing and build behavior that feels a lot like Next.js, which makes development feel much more natural and smooth.
On the user side, the experience is even better. Thanks to the View Transitions API, page switching feels fluid and seamless.
In short, I really like it. If you enjoy tinkering too, give Astro a try.