<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Max Dev</title>
    <description>The latest articles on Forem by Max Dev (@maxr96).</description>
    <link>https://forem.com/maxr96</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F486442%2F61e85c1f-f236-4285-b47b-3c7e0e7f2362.jpg</url>
      <title>Forem: Max Dev</title>
      <link>https://forem.com/maxr96</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/maxr96"/>
    <language>en</language>
    <item>
      <title>Moving My Blog from Next.js to Astro</title>
      <dc:creator>Max Dev</dc:creator>
      <pubDate>Tue, 27 Dec 2022 22:29:12 +0000</pubDate>
      <link>https://forem.com/maxr96/moving-blog-from-nextjs-to-astro-4aa1</link>
      <guid>https://forem.com/maxr96/moving-blog-from-nextjs-to-astro-4aa1</guid>
      <description>&lt;p&gt;A few years ago I tried to set up my blog with &lt;strong&gt;Gatsby&lt;/strong&gt;, but I soon got lost in its complexity. Having to define my GraphQL API was a bit of an overkill and it was difficult to navigate all the extensions that were provided by it. A few months ago I created my blog site with the &lt;strong&gt;Next.JS&lt;/strong&gt; framework.&lt;/p&gt;

&lt;p&gt;This time around I decided to give &lt;strong&gt;Astro&lt;/strong&gt; a go. I heard a lot of praise about it and since it was already more than 3 months since its initial release I was convinced it should be ready for prime time.&lt;/p&gt;

&lt;p&gt;In this blog post, I would like to go through some of the unique features that I liked in &lt;strong&gt;Astro&lt;/strong&gt; and some of the pitfalls that I stumbled upon. I will be comparing it mostly with &lt;strong&gt;Next.JS&lt;/strong&gt; since it is the framework I had the most experience with and I will do a side-by-side comparison of the &lt;strong&gt;Next.JS&lt;/strong&gt; and &lt;strong&gt;Astro&lt;/strong&gt; versions of my blog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial Setup and Getting Started
&lt;/h2&gt;

&lt;p&gt;I enjoyed the simplicity of Astro right from the get-go. To start building my blog with Astro all I have to do is to run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create astro@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Astro provides a great CLI that explicitly shows what is being created and later on when you want to add any integrations all the changes are always highlighted. That puts you in the driver's seat.&lt;/p&gt;

&lt;p&gt;Starting Astro locally is easy as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to build a blog on your own with Astro I highly recommend following &lt;a href="https://docs.astro.build/en/tutorial/0-introduction/" rel="noopener noreferrer"&gt;this guide&lt;/a&gt; from their docs page. Overall, whenever you struggle with something just look for it in the &lt;a href="https://docs.astro.build/en/getting-started/" rel="noopener noreferrer"&gt;docs&lt;/a&gt;, and 99% of the time the answer is there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zero JS by default 🚆
&lt;/h2&gt;

&lt;p&gt;The main and biggest difference between &lt;strong&gt;Astro&lt;/strong&gt; and &lt;strong&gt;Next.JS&lt;/strong&gt; is the way the pages are served. By default &lt;strong&gt;Next.JS&lt;/strong&gt; will load the basic HTML on the first load of the website and for each route in your app will only load JS needed to hydrate the initial page. In contrast, &lt;strong&gt;Astro&lt;/strong&gt; follows a more classical &lt;strong&gt;MPA&lt;/strong&gt; approach. Each route is a completely separate HTML page. When you navigate the route by default you only load the HTML page with the stylesheet and any assets defined on the page. Most importantly, you get Zero JS loaded by default.&lt;/p&gt;

&lt;p&gt;Now if we look at it from the user perspective, when transitioning from Next.JS to Astro when you are navigating from one page to another you need to load a new page. This means you are losing the SPA feeling that Next.JS gives you as you can see the navigation bar and footer flickering when loading the new page. What you gain though is an extra small bundle. My &lt;strong&gt;Next.JS&lt;/strong&gt; blog loaded 327 kB (153kB gzipped) of data, and &lt;strong&gt;Astro&lt;/strong&gt; fits in just 86kB (74kB gzipped). Here I must also mention that I used a trick to load Preact Compat instead of React to squeeze an even smaller bundle size. Still, the numbers speak for themselves, Astro's initial bundle is 2x smaller when comparing the transferred size over the wire.&lt;/p&gt;

&lt;h2&gt;
  
  
  100 Score in Performance 🚀
&lt;/h2&gt;

&lt;p&gt;One of the probably most exciting outcomes that you get from using Astro is how easy it is to get a perfect 100 Performance score even on mobile. One piece of the puzzle is of course smaller bundle, but another is no JS so no logic to run on the client. Even an underpowered phone can run your page easily in the browser with Astro.&lt;/p&gt;

&lt;p&gt;Now let's dive into some actual results for 2 pages. I will be running side by side comparison using &lt;a href="https://pagespeed.web.dev" rel="noopener noreferrer"&gt;PageSpeed Insights&lt;/a&gt; and concentrating on the mobile part as it is the one that reflects more challenging conditions. In fact, on the Desktop both frameworks perform well, so there is no point to compare that one.&lt;/p&gt;

&lt;p&gt;If you want to do your own comparison Next.JS version of my blog is available at &lt;a href="https://maxdev.blog" rel="noopener noreferrer"&gt;https://maxdev.blog&lt;/a&gt; and Astro at &lt;a href="https://my-astro-site-flame.vercel.app" rel="noopener noreferrer"&gt;https://my-astro-site-flame.vercel.app&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, let's look at the &lt;strong&gt;Main Page&lt;/strong&gt;. It is a pretty simple page with a navigation header, footer, theme toggle (which we will talk a bit later about), and 2 images - bigger for the post and smaller for the logo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7008ze3gk251vqd5kjs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7008ze3gk251vqd5kjs.png" title="Main Page with Next.js" alt="Main Page with Next.js" width="800" height="592"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjqqyv33dr013is9x051t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjqqyv33dr013is9x051t.png" title="Main Page with Astro" alt="Main Page with Astro" width="800" height="596"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Based on the results you can see that Astro was able to perform slightly better in terms of an overall performance score. If we look at the Performance details the biggest difference is in the Largest Contentful Paint (LCP) metric. This metric is one of the key metrics as it marks the point in the page timeline when the main content has loaded. LCP is 2 times faster with Astro than with Next.JS. Please, ignore the Accessibility score, I did a few changes in that regard to the Astro application, but that doesn't affect Performance.&lt;/p&gt;

&lt;p&gt;The second page is an &lt;strong&gt;About Me&lt;/strong&gt; page. Here we are using a different font that is being retrieved from the server, we also load quite a few SVG images and an image of myself. This page is a bit heavier than the previous one, but still, we can get a perfect 100 with Astro.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4dqlnn8j96sqpn3fcs45.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4dqlnn8j96sqpn3fcs45.png" title="About Me Page with Next.js" alt="About Me Page with Next.js" width="800" height="587"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhih6jxvf87m9lj984s78.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhih6jxvf87m9lj984s78.png" title="About Me Page with Astro" alt="About Me Page with Astro" width="800" height="596"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The performance difference is even more obvious in this one. The score with Next.JS is now down to the yellow category, while with Astro we still get a perfect 100. The difference in LCP is even bigger now, the site becomes interactive in only 1.1s and there is no blocking time with Astro.&lt;/p&gt;

&lt;p&gt;I believe that gives you a pretty good overview of why you may want to adopt Astro. But again performance is not everything and as I mentioned before this is more of an extreme case with a slower smartphone on a slower internet, with a normal internet on a desktop both web pages load very fast.&lt;/p&gt;
&lt;h2&gt;
  
  
  Closer to WEB standards
&lt;/h2&gt;

&lt;p&gt;Next.JS tries to hide away and abstract most of the details about how it is building your pages. For example, your classical &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; tag is defined inside the Document component provided by Next.JS and if you want to do any kind of modifications you will need to extend it. Unfortunately, it is only possible to extend it once for the whole web app. On the contrary, Astro lets you build a separate header for each page if you want. This is again related to the differences between MPA and SPA.&lt;/p&gt;

&lt;p&gt;When writing Astro components you will be using a JSX template, but still having conventional HTML tags so you can copy any HTML part from the web and it will be a valid JSX for Astro. When compared to Next.JS since it uses React it provides abstractions that are a bit different, like the infamous &lt;code&gt;className&lt;/code&gt; instead of the conventional &lt;code&gt;class&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Another interesting thing is how each Astro component is defined. You have your front matter first - the code that will be executed on the server, &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; part, a &lt;code&gt;&amp;lt;styles&amp;gt;&lt;/code&gt; part, and finally your actual content. This idea was borrowed from Vue. It has its drawbacks and benefits. Personally, since I use Tailwind I don't even need to specify the &lt;code&gt;&amp;lt;styles&amp;gt;&lt;/code&gt; part, but if you want you to have the option.&lt;/p&gt;
&lt;h2&gt;
  
  
  Easy to write code executed on the server
&lt;/h2&gt;

&lt;p&gt;Astro provides a very simple mental model on how to make code run on the server instead of on the client. All you have to do is write it in the frontmatter of your component. That's it! For example, this is how I can retrieve the list of my blog posts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MarkdownInstance&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;astro&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Post&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../types/post&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// Use Astro.glob() to fetch all posts, and then sort them by date.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Astro&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
  &lt;span class="nx"&gt;MarkdownInstance&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
  &lt;span class="nx"&gt;Post&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./posts/*.{md,mdx}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;valueOf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;valueOf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see I am using &lt;code&gt;Astro.glob&lt;/code&gt; to retrieve the list of my posts. All I have to do is specify where it is located and what file types I am expecting.&lt;/p&gt;

&lt;p&gt;In Next.JS I can do the same with the following code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;join&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;postsDirectory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getPostSlugs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readdirSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;postsDirectory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAllPosts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;slugs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getPostSlugs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;slugs&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getPostBySlug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="c1"&gt;// sort posts by date in descending order&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;post1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;post2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;post2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getStaticProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allPosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getAllPosts&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;date&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slug&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;coverImage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;excerpt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;])&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;allPosts&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While in both cases it is not rocket science to understand what is being executed. In the first example, it is much easier as &lt;code&gt;Astro.glob&lt;/code&gt; abstracts all the complexity of going through the file system and reading the necessary directory. Plus you don't have to put the result into a &lt;code&gt;getStaticProps&lt;/code&gt; method, all you need to do is just write the content inside the frontmatter of your component and it will be served statically by default. Of course, the way Next.JS does it gives you a bit more flexibility, especially if writing a more complex web app, but for a blog post you anyway should serve statically generated pages to ensure the best user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Markdown, RSS, Tailwind out of the box
&lt;/h2&gt;

&lt;p&gt;Astro provides plenty of different official integrations. The ones I already used in my blog are RSS, Sitemap, and TailwindCSS. It allows one to quickly start using it and is impressive how big the ecosystem got in so little time. Astro even provides SSR adapters for different platforms like Vercel and Deno. However, if you would like to use SSR you would probably be better with Next.JS as the support of it is much better right now. What I am most excited to try next is the Integrations for UI frameworks available. This brings me to my next point.&lt;/p&gt;

&lt;h2&gt;
  
  
  No Vendor Lock-In 🏝️
&lt;/h2&gt;

&lt;p&gt;Astro provides you with plenty of options using pure Astro components. But if this is not enough for you, you can bring any UI framework/library. So far Astro supports React, Preact, Svelte, Vue, Solid, AlpineJS, and Lit. That is plenty of great options, if you are fancy you can even combine different frameworks on the same page (or if you like your user to struggle and load all this JS 😆). Comparing to Next.JS or Gatsby, where you are locked to React this provides a set of options to develop your website. You have the flexibility to use different frameworks on different pages. Or you can use a lighter framework than React to make your website bundle smaller and loading times faster.&lt;/p&gt;

&lt;p&gt;This is all possible due to Astro's island architecture. As mentioned before Astro ships with Zero JS by default, but if you need interactivity you can opt-in and only load the necessary JS when the user scrolls into the island. All the islands are isolated, which is why you can have your React component in the header, and your Solid component in the footer. And all of them will load in parallel.&lt;/p&gt;

&lt;p&gt;Moreover, you can specify when exactly your interactive component should be loaded. This is done by &lt;a href="https://docs.astro.build/en/reference/directives-reference/#client-directives" rel="noopener noreferrer"&gt;Client Direcives&lt;/a&gt;. You can force the component to load immediately, load on idle, load when the component is visible, or even load based on the media query.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pitfalls when using Astro
&lt;/h2&gt;

&lt;p&gt;One of the issues I stumbled upon during setting up Astro is that the official &lt;code&gt;@astrojs/image&lt;/code&gt; integration was not able to work with JPG and WEBP images. I had to use a plain &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag and specify the width and height manually. It is a bummer since the &lt;code&gt;&amp;lt;Image&amp;gt;&lt;/code&gt; component provided by Next.JS works great and I don't need to compress and figure out the width and height of the images on my own.&lt;/p&gt;

&lt;p&gt;Some other issue I noticed during the development is that hot reload doesn't always work. Sometimes during hot reload I got a broken UI that I couldn't figure out how it was built. I spent a good hour trying to understand what is wrong until I just accidentally restarted the Astro script and voila everything worked. I never had this issue with Next.JS.&lt;/p&gt;

&lt;p&gt;A few smaller issues were how to load the fonts and icons. For both of these, I found an easy solution. For fonts, I used fontsource, which works by simply installing your font as an NPM package and importing it on the page you need. For the icons I used astro-icon integration, the setup was very simple - install the NPM package and start using it by importing an Icon component.&lt;/p&gt;

&lt;p&gt;Another thing I had to deal with was the implementation of the dark mode toggle. This is something that you got out of the box with Tailwind, but you want to provide the user with a way to turn it on/off. For the website that uses Next.JS, I first used a short code that I wrote with React, but then I had an issue with the theme flickering before the rendering and I decided to use a library that solves it called &lt;code&gt;next-themes&lt;/code&gt;. For the Astro however, I didn't want to load a whole UI library, being React or Preact just to have a simple logic for a dark mode toggle. So I had to build it with plain JS. By the way, it was the perfect use case for using the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag in the component as the logic is closely tied to the toggle itself. Here is what I came up with.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;themeToggleDarkIcon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme-toggle-dark-icon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;themeToggleLightIcon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme-toggle-light-icon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Change the icons inside the button based on previous settings&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(prefers-color-scheme: dark)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;themeToggleLightIcon&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hidden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;themeToggleDarkIcon&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hidden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;themeToggleBtn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme-toggle-button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;themeToggleBtn&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// toggle icons inside button&lt;/span&gt;
  &lt;span class="nx"&gt;themeToggleDarkIcon&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hidden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;themeToggleLightIcon&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hidden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// if set via local storage previously&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// if NOT set via local storage previously&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I must say it is not the code that I am most proud of, but it does the job for me, even if it looks uglier than what I could have achieved with a UI library like &lt;strong&gt;React&lt;/strong&gt;. It loads initially either a dark or light theme based on the theme set in the local storage or color scheme of the user. By default, it will use the light theme. Next, we add an event listener to the button that changes the SVG icon if it was clicked and applies the theme to the website by adding dark to the class list. Since this JS snippet loads immediately I don't have any flickering when initially loading the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Words
&lt;/h2&gt;

&lt;p&gt;In my opinion, &lt;strong&gt;Astro&lt;/strong&gt; combines the pure simplicity of tools like &lt;strong&gt;Hugo&lt;/strong&gt; or &lt;strong&gt;Jekyll&lt;/strong&gt;, while providing the power of building well-structured component-based frameworks like &lt;strong&gt;Next.JS&lt;/strong&gt; with the additional benefit of not being tied to a specific UI library like &lt;strong&gt;React&lt;/strong&gt;. That is why when I decide to bring something more interactive to my website, I will for sure give Solid a try. I believe Astro has a bright future and most importantly they know their target audience and focus their efforts on it.&lt;/p&gt;

&lt;p&gt;On the other hand, I am still not fully decided on what technology to continue using for my blog. On one side faster first load of the web page, but on the other re-renders are a bit less user-friendly as you have the header and footer reloading and the Next.JS ecosystem is definitely more mature so some things might be lacking.&lt;/p&gt;

&lt;p&gt;Overall, I am happy that I rebuilt my blog in another framework. It taught me a lot about how web pages are being served under the hood and how can I achieve the best performance for my website.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>ml</category>
      <category>mlops</category>
      <category>quantum</category>
    </item>
    <item>
      <title>7 Things to Consider When Working On a Personal Project</title>
      <dc:creator>Max Dev</dc:creator>
      <pubDate>Mon, 26 Sep 2022 22:00:56 +0000</pubDate>
      <link>https://forem.com/maxr96/7-things-to-consider-when-working-on-a-personal-project-78d</link>
      <guid>https://forem.com/maxr96/7-things-to-consider-when-working-on-a-personal-project-78d</guid>
      <description>&lt;p&gt;Starting a personal project is a cool thing to do, but there are a lot of factors that can distract you from finishing your project. I collected &lt;strong&gt;7 tips&lt;/strong&gt; from my personal experience and I hope you can learn 📘 from my mistakes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose a project idea that you are passionate about
&lt;/h2&gt;

&lt;p&gt;No matter what you like more 🐈 or 🐕, reading 📚 or watching 🎥 - you should choose a project based on your personal preferences. For example, I like to 🧑‍🍳 in my free time for myself and my wife. So I decided to build a recipe app, where I can upload different recipes, search for them and view a very detailed process on how to prepare them. Your tastes might be different. You may want to build your &lt;em&gt;To-Do&lt;/em&gt; app or a &lt;em&gt;URL Shortener&lt;/em&gt;. But usually, the best ideas will come if you find yourself in a position where you are missing particular functionality from existing tools. In this case, you could end up building a project that many people will find usable and potentially you can build your own company out of it (you never know!). &lt;/p&gt;

&lt;h2&gt;
  
  
  Don't follow blindly tutorials on the Internet
&lt;/h2&gt;

&lt;p&gt;The worst thing you can do when working on a personal project is to copy things blindly from the 🌐. There are tons of different tutorials for all different kinds of apps that you can build. While I am not saying they are useless, not at all you can learn a lot from how people approach different projects and use different strategies to build them. You can borrow ideas from those projects and I don't find a bit of copy/pasting evil, but you should never just write down the same code as the project author and call it a day. Instead, you should take your time and architect your project. In this process, you can pick a few pieces from different sources, but you should always understand what you are doing. In this way, you learn the most as you will tackle different challenging issues, that you will have to find the solution for. And overcoming those challenges is exactly what programming is about🚀&lt;/p&gt;

&lt;h2&gt;
  
  
  Set small reachable goals
&lt;/h2&gt;

&lt;p&gt;Setting small goals is the best way to let you enjoy the process constantly. When you set goals for yourself and can reach them in a couple of hours ⏲️, you have a very rewarding experience. For example, you want to build a blog. You start by doing your research and deciding what set of tools you will use and that is your very first goal accomplished. Next, you look into how other people design their websites and you sketch a draft either on paper or a tool like &lt;a href="https://www.figma.com/"&gt;Figma&lt;/a&gt; and this is already your second deliverable. When you work on the page each separate page and functionality like a button is a separate deliverable. In this way, you always have these small things that you can add and improve your website. Ideally, you should set up a light tracking tool like &lt;a href="https://trello.com"&gt;Trello&lt;/a&gt; or even a simple notebook. Just assigning a task to yourself and moving it to done feels very satisfying (trust me on that!). And if you want to get to the project after some time you can rewind the decisions you did and understand your motivation at that time. You will forget all the details about your project in just a few weeks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best practices are good, but you probably won't need them
&lt;/h2&gt;

&lt;p&gt;It is always nice to learn and follow the best practices from the community like proper folder structure for your project, following all the naming conventions and extracting the interface for the class. But most of them are useful only, when you are working on a big team or you have a huge project. So instead of immediately going to a popular repository and copying their structure, you should aim for starting things simple and improving/changing the structure naturally as your project grows.&lt;/p&gt;

&lt;p&gt;In my experience, in my recipe book &lt;em&gt;Angular&lt;/em&gt; app I used a Redux-like state management library called &lt;em&gt;NgRX&lt;/em&gt;. While it is a cool library and brings benefits for bigger projects, in my small project it only complicated things as after just a few weeks I couldn't remember how exactly everything was wired up and what I have to do to add some extra data to the state. If I would go with a simpler approach first (like using built-in services in my Angular app) I wouldn't have had those issues. And if I would later observe that this doesn't fit my use case anymore, it would be easier for me to switch to a more sophisticated solution than doing it in a reverse direction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stick to the tools you chose in the beginning
&lt;/h2&gt;

&lt;p&gt;This is true for any programming language, but especially, if you are a &lt;em&gt;JS&lt;/em&gt; developer as you can see a new framework/library being born 👶 every day. But before you jump on the hype train, ask yourself some of the following questions: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why did you start doing your project with the current tools? &lt;/li&gt;
&lt;li&gt;Were you trying to get some experience in them before you can apply for work 💼? If the answer is yes, then will this new shiny tool help you to achieve that? The answer here is usually no. As ✨ new tools are rarely used in the industry, they need to pass the test of time and be polished so they don't have any surprises, which you might also encounter and get stuck.&lt;/li&gt;
&lt;li&gt;Is there any real functionality that you are missing with the current tools and there is no way to easily replicate it or is it just the new tool is looking nicer or advertised as faster?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After you answered all the above-listed questions and you still feel the need to switch then fair enough - just do it! But, likely, that this won't be the case for you. Anyways, it is a cool idea to later re-write your project with a new tech stack or new programming language, but only after you have achieved at least your Minimal Viable Product state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decide what would be your Minimal Viable Product
&lt;/h2&gt;

&lt;p&gt;Put the most necessary things and deliver your project as an MVP 🚀. MVP will ensure you stay laser focused and have a small deliverable that is usable and that you can even showcase. It should contain only a minimal set of functionality that you need to implement. Say you want to build a To-Do app. Functionality that can go in your To-Do app is creating new notes 📓, editing/deleting current notes and maybe moving the notes around. But then things like including a fully featured rich text editor, marking favourite notes or even creating folders are all extra things that go on top. They are not necessary for the MVP. Creating an MVP is all about building this solid foundation, making quickly something that is not very sophisticated, but supports the very core functionality that users would expect from your app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Share your project with others
&lt;/h2&gt;

&lt;p&gt;Once you build an MVP, it is time to share 📤 your project with the world. Nowadays it is super easy to do. First of all, make your project repository open on platforms like &lt;a href="https://github.com/"&gt;GitHub&lt;/a&gt; or &lt;a href="https://gitlab.com/users/sign_in"&gt;GitLab&lt;/a&gt;. Next, you can create a small blog post about your project and post it on one of the awesome websites like &lt;a href="https://medium.com/"&gt;Medium&lt;/a&gt;, &lt;a href="https://hashnode.com"&gt;Hashnode&lt;/a&gt;&lt;br&gt;
 or &lt;a href="https://dev.to/"&gt;Dev.to&lt;/a&gt;. Last, but not least share it on any social media like &lt;a href="https://twitter.com/"&gt;Twitter&lt;/a&gt;, &lt;a href="https://www.linkedin.com/"&gt;LinkedIn&lt;/a&gt; or even &lt;a href="https://www.youtube.com/"&gt;YouTube&lt;/a&gt; with your friends and followers. &lt;/p&gt;

&lt;p&gt;You should not fear sharing your work, nobody will punish you for not providing the best solution possible, instead sharing your project helps you to get feedback from other developers like yourself. They can give you 💎 advices on your project structure, provide fresh ideas to work on or even find a bug that you didn't notice.&lt;/p&gt;

&lt;p&gt;Thanks for reading my very first blog post! I hope you learned something new 😊 Feel free to drop links to your projects under this post. I will happy to take a look and give my feedback.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>programming</category>
      <category>career</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
