Migration from Gatsby to Astro
I took a month of my free time to migrate this website from Gatsby to Astro. Why? After five years in the React bubble, I wanted a breath of fresh air in the chaotic frontend world.
The design philosophies of Gatsby and Astro diverge in notable ways:
-
Gatsby uses the GraphQL to query collections and build a single page application (SPA), leveraging modern frontend techniques such as preloading and resource chunking to enhance performance.
-
Astro, by contrast, defines collections using its type system and constructs a multi-page application. Yet, the development experience remains refreshingly similar to React.
The migration works started from individual pages. Transforming JSX to Astro pages proved
fairly straightforward. The primary adjustment was to convert React-specific attribute names,
like className
to standard HTML attributes, class
in this case. The markdown page
in Astro was compiled by the remark and rehype pipelines, allowing direct
use of their plugins without relying on Gatsby-specific gatsby-remark-transform-*
plugins.
In the remainder of this post, let’s discuss the key tasks involved in the migration.
Define Collections
Gatsby loads ALL content and exposes the GraphQL API, giving developers significant flexibility
in querying data sources and defining collections. For instance, I could store
both blog
and tldr
entries in a single directory, then filter them using the kind
attribute.
Astro, on the other hand, defines the collection though type matching, which implicitly enforces that each collection MUST have distinct types. I was unable to implement a multi-collection setup, but it was OK, as I wanted to fix the tech debt anyway.
Rework JavaScript
Given the content-driven nature of this site with minimal user interactivity,
I opted to strip javascript from most pages, except for /blog/tags
. Previously,
the page was rendered with react-d3-cloud with tags
directly passed as props.
In Astro, however, data MUST be passed via web components with custom elements:
<Layout>
<d3-cloud id="cloud" data-tags=`${ JSON.stringify(tags) }`></d3-cloud>
</Layout>
<script>
import cloud from "d3-cloud";
class D3Cloud extends HTMLElement {
connectedCallback() {
const tags: cloud.Word[] = JSON.parse(this.dataset.tags!);
// ... ...
}
}
customElements.define("d3-cloud", D3Cloud);
</script>
In the above code snippet, we define a web component <d3-cloud>
, and bind it
to D3Cloud
element. The data-tags
attribute is then passed
as this.dataset.tags
in the connectedCallback
to pass from server-side
to the client-side javascript namespace.
Since Astro builds a multi-page application, third-party stylesheets and JavaScript can be selectively included on specific pages, reducing the overall page payload and optimizing load times.
Image Optimization
The official Gatsby plugin, gatsby-transformer-sharp
automatically downsizes images
linked in Markdown files and generate responsive srcset attributes.
Astro’s image optimization however,
only applies to the .astro
file when images are imported at compile time.
Fortunately, Chirstian Ohanaja encountered the same issue,
and developed a solution with
astro-remark-eleventy-image
to close the gap.
I also submitted a pull request to add relative path support, allowing Markdown files to reference images within the same folder.
However, the cover image used in the /tldr
page are referenced in the astro file, but can
not be imported in the build time. Since the images are referenced outside of Markdown
processing, there isn’t an easy solution for this in Astro.
Eventually, I followed a suggestion from the documentation,
adding a check in src/content/config.ts
and downsizing the images manually:
convert -resize 282px foo.jpg foo.jpg
It’s worth noting that ImageMagick’s convert
command is not stable;
rerunning it can resample the image inconsistently.
Syntax Highlighting
The syntax highlighting engine was upgraded to Shiki due
to stalled development on Prism. A bonus point
is Shiki supports TextMate syntax in JSON format, which is compatible to a popular
library, iro. We can export the iro syntax highlight definition to TextMate XML format,
then convert it to JSON format via TextMate Languages
VSCode extensions to be
consumed in Shiki.
I added minizinc, and apacheconf via this approach.
Jupyter notebook migration
The Machine Learning notes are crafted in Jupyter Notebook.
In Gatsby, the notebook can be dynamically converted to Markdown format using the
onCreateNode
API.
Astro, however, lacks a similar mechanism to inject generated content directly into
Markdown collections, — or at least none that I’m aware of.
The closest workaround is to generate content using the astro:config:setup
hook,
though the generated Markdown files may clutter the source directory.
It is worthy noting that the one-month migration is not typical: I also take advantage of the migration to pay off the tech debt accumulated over last seven years, notably:
- Migrate the hosting from netlify to cloudflare page for better observability.
- Fix various A11y bugs reported by accessiBe.
- Various UI fix.
- Various build warnings, such as dependency deprecations.