The Aging Developer

The Aging Developer

Richard Klein

Houston, We Have Liftoff

The seemingly annual process of reworking this site in another framework.

SpaceX
Image by SpaceX on Unsplash

A few years ago, I started eyeing the framework Astro, and it immediately caught my attention. When I refreshed the site last year by updating to a new version of Gatsby, I considered making the switch to Astro at that time. However, I decided against it, as it would require learning a new toolset, and I believed that Gatsby would offer an easier upgrade path and simpler long-term maintenance to keep the site running smoothly.

Gatsby Concerns

I believe it’s important to carefully manage any changes that could disrupt existing systems. However, I’ve become increasingly concerned about the future of the Gatsby framework. According to their long term support policy, each version of Gatsby is only supported for one year. The latest major version was released on November 8th, 2022, and since then, there hasn’t been any significant feature development. In fact, there’s an ongoing discussion within the Gatsby community questioning whether the project has been discontinued. Jared Scott, a member of the community, provided a detailed analysis of recent commits, showing that no substantial updates have been made.

Additionally, Gatsby was acquired by Netlify in February 2023. Shortly after, Netlify experienced a round of layoffs in July 2023, which adds to the uncertainty surrounding Gatsby’s future.

Astro Exploration

Given my concerns about Gatsby, I began exploring Astro as an alternative about three weeks ago. Since then, I’ve made significant changes behind the scenes: transitioning from JavaScript to TypeScript, replacing React components with Astro components, and switching from Material UI to Tailwind for styling. I’ll dive deeper into these changes throughout this article. Today, I’m excited to celebrate the launch of The Aging Developer, now completely rewritten using the Astro framework.

Typescript

TypeScript is a version of JavaScript that includes extra features to help developers write more reliable code. One of its key benefits is adding “types” to variables, which means the code can check for certain errors before it even runs. I’ve attempted to switch my site to TypeScript a few times in the past, but it required a lot of setup, and I could never get it quite right. What’s great about Astro is that it comes with built-in TypeScript support, making the transition much smoother. The documentation provided clear instructions, and I was able to get everything set up without much hassle. Previously, with Gatsby, I used a package called prop-types to define the types for my components. Now, with TypeScript, I can easily do this using an interface, which is both simpler and more powerful.

interface Props {
  image: ImageMetadata
  imageAlt?: string
  site: CollectionEntry<'site'>
}

Astro includes a handy check function that automatically scans your code for errors, such as type mismatches, ensuring everything works smoothly before building the site.

Components

Astro components are made up of two main parts. The first part, known as the “frontmatter,” contains the component’s script, which runs during the website’s build process. The second part is the HTML template, which uses a mix of standard HTML and JSX-like syntax to structure the content. If you need to add interactivity on the client side (like responding to user clicks), you can do this with script tags within the component. Since I only need a small amount of client-side interaction, I opted to use web components for those tasks. For example, I created a component that displays a floating action button (FAB) that lets users quickly scroll back to the top of the page.

<scroll-top class:list={['fixed bottom-8 right-8']}>
  <button
    class:list={[
      'rounded-full bg-secondary-main p-2 text-secondary-contrast opacity-0 shadow-xl',
      'hover:bg-secondary-dark focus:bg-secondary-dark'
    ]}
  >
    <Icon title="Scroll to Top" name="mdi:keyboard-arrow-up" class:list={['h-6 w-6 ']} />
  </button>
</scroll-top>

<script>
  // Define the behaviour for our new type of HTML element.
  class ScrollTop extends HTMLElement {
    constructor() {
      super()

      let timer: number
      const button = this.querySelector('button')
      button?.addEventListener('click', () => {
        window.clearTimeout(timer)
        window.scrollTo({ top: 0, left: 0, behavior: 'smooth' })
      })

      const showButton = () => {
        window.clearTimeout(timer)
        timer = window.setTimeout(() => {
          if (document.body.scrollTop > 100 || document.documentElement.scrollTop > 100) {
            button?.classList.remove('fade-out')
            button?.classList.add('fade-in')
          } else {
            button?.classList.remove('fade-in')
            button?.classList.add('fade-out')
          }
        }, 2.5)
      }

      window.onscroll = () => {
        showButton()
      }
    }
  }

  // Tell the browser to use our ScrollTop class for <scroll-top> elements.
  customElements.define('scroll-top', ScrollTop)
</script>

Astro is compatible with various component libraries, including React, Vue, Svelte, and more. This site initially used React components along with the Material UI component library.

However, as I transitioned to Astro components, I decided to replace the React components entirely. Since Material UI is specifically designed for React, I needed a new approach to styling. I opted for Tailwind CSS, a utility-first CSS framework. Tailwind doesn’t provide pre-made components like Material UI, but it is great in building custom designs. I used TailwindFlex, as a valuable guide for creating the components for the site. The ScrollTop component above demonstrates how Tailwind CSS can be used to achieve the desired look.

  <button
    class:list={[
      'rounded-full bg-secondary-main p-2 text-secondary-contrast opacity-0 shadow-xl',
      'hover:bg-secondary-dark focus:bg-secondary-dark'
    ]}
  >

Content

While Gatsby uses GraphQL to provide data for generating pages, Astro takes a different approach. In Astro, content collections are defined in a configuration file, with Zod used to define the schema for each collection.

const site = defineCollection({
  type: 'data',
  schema: ({ image }) =>
    z.object({
      title: z.string(),
      tagline: z.string(),
      category: z.string(),
      repository: z.string(),
      avatar: image(),
      icon: image(),
      background: z.string(),
      theme: z.string(),
      displayLimit: z.number(),
    }),
})

JSON files in the src/content/site directory are then converted into entries in the collection, which can be retrieved by the filename as their ID:

const site = await getEntry('site', 'agingdeveloper')

Alternatively, a collection can be iterated over as a whole:

export const getArticles = async (limit?: number) => {
  const articles = await getCollection('article')
  return articles
    .sort((a, b) => b.data.published.valueOf() - a.data.published.valueOf())
    .slice(0, limit)
}

Issues

I’ve encountered a few challenges and limitations. Manipulating image sizes has been difficult when working with endpoint APIs compared to standard pages. Additionally, the site has lost the blurred image placeholders that were easily implemented in Gatsby. Since Astro uses Sharp as its default image service, I believe these issues can be resolved, but they do require some additional effort.

Astro components currently fall short in terms of documentation support, though this is on the roadmap for future updates. Another challenge has been the need to rely on an experimental feature to write unit tests for my components, which isn’t ideal for stability or long-term maintenance.

Impressions

Overall, I have a positive impression of the framework. The speed at which the development server runs and builds is impressive. I can build the entire site with image optimizations from a cold start in under 30 seconds—a significant improvement over the previous setup, where just starting the dev server took longer. Astro delivers the static HTML and CSS generation I had been hoping for, making it a strong choice for content focused projects.