Why Switch
I’d previously used NextJS to host my portfolio but the problem was that I was sending a lot of Javascript to the users’ web browser. This is because React (which NextJS uses) needs to be loaded onto the browser for “reactivity.” But for a portfolio, I didn’t need all that much interactivity so sending the React library to the client is heavy.
That’s where Astro comes into play. It lets me serve my website statically resulting in faster load times and smaller bundles. If I need interactivity, I can sprinkle that in where needed (whether it be React, Svelte, Vue, Solid, etc.).
Okay but why Solid instead of React?
- I’ve used React for years, time to learn something new
- Solid is fast. SolidJS doesn’t use a virtual DOM like React does where it goes node by node to see what’s changed between the DOM. SolidJS instead plugs itself directly into the parts of the DOM that can be “reactive.” It’s a substantially faster and simpler way to add reactivity to a website or app.
What’s in this Guide
This isn’t quite a full “follow along” type tutorial. I’m going to give you the gist to understand at a high level and if you want to see my source code, you can check it out here
The Tech Stack
- Astro for the main framework
- SolidJS to sprinkle some reactive components as needed
- TailwindCSS because it lets us quickly style our website with easy to remember utility classes
- Vercel to deploy
Let’s Get Started
Installation
Create a new Astro project
Add TailwindCSS
Add SolidJS
If you’re wondering why pnpm, it’s because its a whole heck faster.
Now run your project
Pages
Creating pages in Astro is simple. Within the pages
directory you can create a new route e.g. about.astro
which would yield portfolio.com/about
.
Since I also want to have a blog, I can create markdown files in the pages directory. Particularly subdirectories will yield nested routes e.g. portfolio.com/blog/post-1
if I create a file pages/blog/post-1.md
.
Layouts
Pages in my portfolio have a general layout and Astro allows you to create layouts that you can wrap your content within.
This layout file has two components Head
and Navigation
defined elsewhere that we’ll talk about later.
The slot
element is where my page content will go (a sort of placeholder).
I can use this layout as such:
Components
In the above example, you saw that I used some components. You can use Astro syntax to make components or you can also make them in another framework like Solid. Components can take in props like we’re used to and are reusable.
Since the navbar would be on each page, I had it as a component within my Layout file.
I know I want a list of my projects on my portfolio so I can make this a component.
I can create src/components/projects.tsx
to make a solid component.
Let’s store some example projects
You’ll notice here that I’m importing images within my src
folder. This is because Astro can bundle and optimize them versus if stored in the public/
directory.
Now I can display these projects in a Solid component which has similar syntax to React but with a few differences
A few things about this code
- We’re using TailwindCSS to style our elements
- These are actually HTML elements unlike React so you’ll see we’re using
class
instead ofclassName
- The
For
component allows us to render a list and is already keyed which is why we’re not using thekey
attribute like you would in React if you were mapping an array
Here’s the code for the FadeIn
component that I’m using
The main thing to note here is createSignal
. This is effectively like useState
. But remember how I said SolidJS plugs itself into the actual DOM whereas React uses a virtual DOM? Because of that, visible
isn’t a static variable like in React. It’s a getter method, meaning we have to call it as visible()
.
Another thing to note, createEffect
is like useEffect
but we don’t need to supply it a dependency array like in React because Solid is smart enough to detect the signals being used.
Finally, I can use my Solid component in my Astro page, pages/projects.astro
:
By default, Astro won’t send the component’s javascript to the client, so we have to specify the directive client:load
to tell Astro to do so! What’s awesome about this is I could essentially use Solid (or another UI framework) instead of a mix of Astro and Solid and Astro will only send the HTML by default.
Blog Posts
So now how do I generate 1) a page to display all my blogs posts and 2) a page to show the content of a single post?
On pages/blog.astro
for example we can write Javascript within the ---
snippet to retrieve all our markdown files.
Astro.glob
will import many files at once (in this case all our markdown files within the current folder). We use Typescript to define what the frontmatter will look like, e.g.
Here the frontmatter matches the typescript interface we defined, but what about MarkdownLayout
. I created another layout file to define the layout of a blog post. BlogCard
is a component I created to render a url to each blog post with some information about that post.
In the MarkdownLayout
we can pull in the props passeed and slot in our blog post
My Review
Overall, it was really fast to learn Astro especially if you’ve used other frameworks like NextJS before. Astro’s documentation is well written and I even recommend their tutorial.
The developer experience was great, I had a fun time building and very little issues.
But what about performance? So it would be unfair to directly compare my revamped portfolio to my old one because I changed quite a bit. But a few general things to note
- Network requests are down by almost 4x. In my NextJS website, I would have close to 40 requests on some pages. Astro will send 10
- Loading times were all mostly down, with some down by close to 100ms.
- Network resources substantially lowered (over a MB with NextJS, versus a few kB with Astro)
Also, adding <ViewTransition />
that Astro offers added a smooth app like feeling with just that one line!
What about the bad? I have to test this more, but some images (even using Astro’s image optimization) loads slow. This could be an issue with Vercel though. More investigation is needed.