Recently I was using Hexo
for my website, and it was my 4th platform and 2nd static-site generator after Pelican. Hexo was fast and extensible and it allowed me to create custom pages with custom data (I was using YAML files for that). And that was pretty much it. I was using Hexo for two years, and during that time, I barely touched any customizations except for the theme and minor visual changes to the custom pages I created. Creating a visually appealing and unique design was kind of a pain, so switching to something more progressive and easier to use was a matter of time.
Why Astro?
I came across Astro by accident, and that was love at first sight. After reading the documentation, I was impressed by the core features, integration capabilities, and how easily Astro can import your content from different sources. The most exciting parts for me were:
-
Astro Components that are reusable building blocks for your website that you can combine however you want to create the functionality you need for your website. For example, a simple page with components may look like this:
--- import Head from './components/Head.astro'; import Footer from './components/Footer.astro'; --- <!DOCTYPE html> <html lang="en"> <Head /> <body> <slot /> <Footer /> </body> </html>
There are two self-descriptive components that contain parts of a page layout. The
<slot />
element is used to inject content from other files. You can structure your page however you want and use as many components as you need. -
Astro Islands that are basically isolated interactive UI components within a static page (by default, Astro wipes all the JavaScript out of the generated website). In this way, you can create a website that will remain fully static, and JavaScript will be loaded only for the components that require it.
Another benefit is that Astro is platform-agnostic, so you can continue working with the tools of your choice without sacrificing your workflow and experience. Astro officially supports many popular tools and frameworks that it can set up and configure for you automatically, taking out the hassle of starting from scratch. And if you canāt find the integration you need, you can create your own. In my setup, I use Tailwind integration for styling. I will talk more about it later.
Migration
Theme layouts
I spent some time searching for a suitable theme for my website, but I couldnāt find anything that I liked. There were some interesting templates, but they were rather complex, so organizing and customizing them would take a lot of time. So I decided to follow a samuraiās path and create my own theme from scratch. Taking into account my painful experience with customizing styles for Hexo (there were 10 separate CSS files), I decided to proceed with a more modern approach and use Tailwind because the existing frameworks, like Bootstrap, Materialize, Bulma, etc., are too bloated and canāt give you the flexibility you need.
The best thing about Tailwind is that you no longer have to think up the proper naming of yet another div
to make your CSS slightly more readable. Instead, you just add styles right to the HTML. Yes, when your website grows, you will have a lot of classes (this may seem scary at first), but these classes are self-explanatory and easy to use, and once tried, you will never go back to the old way of styling via separate CSS files. I can confirm it. I was really happy to give Tailwind a try.
For my website, I created two layouts: one for pages and another for blog posts.
Page layout
The page layout is simple and consists of several basic components:
---
import Head from '@components/Head.astro';
import Footer from '@components/Footer.astro';
export interface Props {
title?: string;
}
const defaultTitle = 'Personal website of Anton Zolotukhin';
const { title = defaultTitle } = Astro.props as Props;
---
<!DOCTYPE html>
<html lang="en">
<Head
title={title}
defaultTitle={defaultTitle}
/>
<body class="flex flex-col h-screen bg-mg-primary text-slate-400">
<slot />
<Footer />
</body>
</html>
<style is:global>
@font-face {
font-family: 'Quicksand';
src: url(/fonts/quicksand.ttf) format('truetype-variations');
}
</style>
I use components for the page head (where I dynamically pass the page title via props) and footer. Multiple components make parts of the layout easier to maintain and reuse. It is completely up to you to decide how to structure your pages and how many components to use.
The <slot />
element injects content from other pages, whereas the <style>
tag defines the styles for the current component. The is:global
attribute explicitly tells Astro that the styles should be applied globally. In my case, I define my custom font-family
globally. And this is the only place where I use explicit styles.
Blog post layout
The blog post layout is a bit more lengthy because of styling for posts and additional utilities to work with types and date formatting:
---
import type { MarkdownLayoutProps } from 'astro';
import type { BlogPostFrontmatter } from '@types';
import BaseLayout from './BaseLayout.astro';
import GoHomeArrow from '@components/GoHomeArrow.astro';
import { formatDate } from '@utils';
type Props = MarkdownLayoutProps<BlogPostFrontmatter>;
const { frontmatter } = Astro.props;
const postfolder = frontmatter.url!.split('/')[2];
---
<BaseLayout title={frontmatter.title}>
<div class="text-center">
<GoHomeArrow getBackTo="/blog" title={frontmatter.title} />
<small class="text-slate-400"
>Written by Antonio ā¢ {formatDate(frontmatter.date)}</small
>
<hr class="my-6 mx-auto w-20 border-t-2 border-mg-accent" />
</div>
<article
class="prose
prose-invert
mx-auto
px-6
mb-12
prose-headings:text-mg-accent
prose-a:text-mg-blue
prose-a:no-underline
hover:prose-a:underline
prose-code:text-mg-red
prose-blockquote:border-sky-700
prose-img:rounded-lg
prose-img:mx-auto
max-w-xs
sm:max-w-sm
md:max-w-md
lg:max-w-lg
xl:max-w-xl
"
>
{frontmatter.img && (
<img
class="rounded-lg"
src={`/images/${postfolder}/${frontmatter.img}`}
alt="image"
/>
)}
<slot />
<div class="text-center mt-14"><a href="/blog">ā Back to blog</a></div>
</article>
</BaseLayout>
The only interesting part of the layout is the <article>
tag that contains the <slot />
element to inject the content from the blog post. As blog posts are retrieved dynamically using the Astro.glob()
function, the content of the posts is passed to the <slot />
element as plain text, and you cannot directly control the formatting.
Styling all the Markdown elements on your own sounds like a huge overhead and the last thing in this world you want to do. But Tailwind has an elegant solution called Typography. It is a plugin that provides basic formatting for Markdown content along with a set of utilities to further customize it, if necessary. To apply the basic styling, you just need to add prose
class to the <article>
tag that acts as a wrapper for the content. As you can see, I also used additional classes starting with prose-*
to customize some of the elements.
Pages
Previously, there were two custom pages on the website: my resume and my projects. On the new website, these pages are still there but with some updates. I decided to keep the resume page āas isā so I only recreated the layout and design using Tailwind.
In turn, the projects page is no longer static (well, almost š). I created a component that makes an API call to GitHub, retrieves the list of my projects, and renders it. If I need to show a specific project, I can pass its name as the componentās prop. This way, I donāt have to update the list manually when something changes, and I can also refer to the projects or a specific project from other pages.
This is how the Projects component is rendered on the page:
antmarky
Static-site generator for Asciidoctor based on Node.js and EJS
bandantonio.github.io
My personal website created with Astro, Tailwind CSS, and GitHub Pages
Blog
I have only a few blog posts, so no rocket science was involved when migrating them. I moved all the static files, updated paths and links, and slightly changed the frontmatter. Thatās it. The URL structure remains the same.
Conclusion
The migration took approximately 1.5 weeks, and guess what? I really enjoyed the process, and Iām happy with the result. Astroās unique features are powerful enough to give you freedom and flexibility to create a truly unique experience. I feel like the decision to switch to Astro was totally worth it, as I havenāt had this much fun in a long time working on my website.
Thanks for reading! I hope you enjoyed this article.