I’ve been the front-end developer for the U-M Library website for the last 4 years and for most projects our goal is to make something new. And, while I like creating new things, this work required us to make something that looked exactly the same.
In 2025, we rewrote the library website from Gatsby to Astro. The website had been stable for about 5 years, so this wasn’t us trying to fix something that was broken. The problem was maintainability. The site uses Drupal as its content management system, with a decoupled front end hosted on Netlify. The original architecture was:
Drupal → GraphQL → Gatsby → React
Netlify acquired Gatsby in 2023 and, since then, Gatsby’s development has been fairly stagnant. Updating dependencies was becoming increasingly difficult, and we weren’t confident about building the next several years of development on top of it.
Also, I really despise React, especially for a static site, and Gatsby is a React-first framework.
My initial goal was simply to replace the Gatsby layer with a modern, well supported framework that didn’t use React. During the evaluation phase, however, it became clear that we couldn’t cut Gatsby and drop in something else. Is anything ever that easy?
Choosing an approach
We could have taken a number of paths to replace Gatsby. We evaluated several options, including Next.js, Svelte, Remix, Astro, Eleventy, and approaches based more directly on HTML, CSS, JavaScript, and Web Components. I was really looking for something that wasn’t another large JavaScript framework. React is useful for applications with complex client-side state logic, but our static site didn’t need React to generate it.
After meeting with developers from around the library to get their thoughts and input, we ultimately decided to give Astro a shot. I created a new branch called ‘astro-trial’ and that ended up being my working branch for the entire project.
Astro offers key benefits: developers can write components primarily as HTML, fetch data directly in component frontmatter, and limit JavaScript to functionality that actually requires it.
Rewriting in Astro
I started by rebuilding the homepage because it exercised nearly every major part of the site: Drupal content, navigation, images, hours, news, events, and shared components. Once that worked, I mapped out the rest of the site’s templates and components and proceeded with converting them from Gatsby to Astro. Throughout the project, Heidi Burkhardt (the website’s product manager and my project manager for this project) provided structure and guidance, plus expertise on the site’s architecture and content.
For the most part, I was able to reuse a good portion of the architecture from Gatsby. Some front-end architectural decisions are framework-agnostic, like file organization, component scope, etc., and I was able to keep a lot of that structure with Astro.
The final architecture contains no React and no GraphQL 🎉. The result is primarily Astro-generated HTML, with JavaScript used only where we need it.
That substantially reduced the amount of code sent to the client’s browser. The Gatsby homepage, for example, produced approximately 382 KB of JavaScript, compared with the 3.8 KB for the Astro version, a roughly 99% reduction. We also dropped the homepage image payload from approximately 192 KB to 48 KB.
Replacing things Gatsby provided
The most difficult part of the migration was identifying functionality that Gatsby and its plugins provided…and recreating it ourselves.
The image pipeline is a good example. Gatsby’s Drupal integration could download remote images, process them with Sharp, and provide metadata needed for responsive image placeholders all with one simple call. Astro’s image tooling provided a foundation, but I needed to build additional tooling for our Drupal-specific workflow.
Caching is another (ongoing) challenge. Gatsby’s persistent caching made repeated builds, local development, and data persistence for a single build relatively straightforward. Because the Astro site fetches Drupal data directly, I had to develop a different caching strategy for each of these problems. In hindsight, Astro Content Collections may have been a better fit than a custom caching solution, but I was so focused on “no out-of-the-box tooling” that I made this far more complicated than it needed to be, and, at some point, moving to Content Collections would have added considerable scope to the project.
We encountered similar issues with Drupal relationships, navigation hierarchy, breadcrumbs, and functionality that Gatsby plugins, GraphQL, and third-party utilities had previously supplied indirectly. These small and intrusive bugs really made things like navigation, breadcrumbs, node ID management, hours data and relationship handling quite difficult.
The broader lesson was that replacing a framework doesn’t mean removing the problems the framework solved, it means deciding which of those problems you want in your code versus pulling in as a package.
A rewrite, not a redesign
I was the sole developer, and we intentionally kept the project focused on the front end and its build/deployment infrastructure. When we set out we explicitly made it a goal not to touch the Drupal layer, and we were successful in that. We kept the existing URL structure, visual design, and functionality rather than turning the migration into an opportunity for a redesign or massive architectural uplift.
The final Astro project contains:
- 53 primary components
- 7 reusable components
- 18 page templates
- 19 Drupal panel types
Across the repository the rewrite resulted in 306 files changed, 259 commits to my single ‘astro-trial’ branch, 32,729 additions, and 37,739 deletions.
Instead of creating a new repository, I simply used the existing repository, updated the packages to remove Gatsby and Astro, then went file by file replacing Gatsby with Astro. For me, this made the conversion easier to have it all running in the existing repository and environment and slowly migrate things over versus rebuilding all the established architecture and business logic in a new repository.
The rewrite also gave me the opportunity to consolidate a lot of duplicated code that had accumulated on the original site. Rebuilding the site template by template, component by component, allowed me to address some of those inconsistencies rather than reproduce them in a new architecture.
Accessibility and performance
Accessibility, as with all of our projects, remained front and center throughout the development process. We continued screen-reader testing, manual audits, and automated testing, plus we were able to integrate Siteimprove automated checks right into our development environment. Siteimprove continues to report a 100% compliance rate.
Performance measurements also improved, although Lighthouse results should be treated as indicators rather than controlled benchmarks. These tests were run in the same local environment, but Lighthouse scores vary between runs even when the code hasn’t changed.
For the homepage, Lighthouse reported:
Overall, the Astro site performs better than the Gatsby site, but the difference is marginal and the end-user will hardly notice it. The biggest difference that people may notice is elimination of Gatsby’s post-hydration layout shift.
In short, the Gatsby site would load with static content, then hydrate with refined styles and logic. This hydration would visually refresh the page and cause the content to jump. It was very noticeable on some of the more complex pages. In Astro, we have none of that hydration and no layout shifts.
An intentionally uneventful launch
On August 19, 2026, the new Astro site went live after roughly 9 months of active development, which included extensive testing for technical functionality, layout, and styles. We also completed task-based quality control testing across a variety of devices, operating systems, and browsers prior to launch to try and catch any issues or inconsistencies.
We monitored the site closely afterward and found no significant problems. We had the opportunity to roll back to the Gatsby site at any given moment in case something went wrong, but everything went smoothly!
The most significant outcome of the rewrite was reducing the number of layers between Drupal and the HTML delivered to users. We removed React and GraphQL, substantially reduced our JavaScript and image payloads, shrank our dependency footprint, and gained more direct control over the build process.
The resulting site is not at all different to the end-user, which was intentional, but the architecture underneath that I work with everyday is simpler, more transparent, and less dependent on the long-term direction of a particular framework.
Thank you to everyone who helped in this project, including Heidi, Eliot, the Web Content Coordinator Group, and our fellow Library development team members.
🚀