This website uses cookies. We use cookies to personalise content and ads, to provide social media features and to analyse our traffic. We also share information about your use of our site with our social media, advertising and analytics partners who may combine it with other information that you’ve provided to them or that they’ve collected from your use of their services.

Cookies are small text files that can be used by websites to make a user's experience more efficient.

The law states that we can store cookies on your device if they are strictly necessary for the operation of this site. For all other types of cookies we need your permission.

This site uses different types of cookies. Some cookies are placed by third party services that appear on our pages.

You can at any time change or withdraw your consent from the Cookie Declaration on our website.

Learn more about who we are, how you can contact us and how we process personal data in our Privacy Policy.

Please state your consent ID and date when you contact us regarding your consent.

Your consent applies to the following domains: blog.angulion.dev

Your current state: Deny. 

Cookie declaration last updated on 6/28/23 by Cookiebot:

Necessary (4)

Necessary cookies help make a website usable by enabling basic functions like page navigation and access to secure areas of the website. The website cannot function properly without these cookies.

Name Provider Purpose Maximum Storage Duration Type
CookieConsent Cookiebot Stores the user's cookie consent state for the current domain 1 year HTTP Cookie
iconify0 blog.angulion.dev Used by the website's content management system (CMS) to determine how the website's menu-tabs should be displayed. Persistent HTML Local Storage
iconify-count blog.angulion.dev Used by the website's content management system (CMS) to determine how the website's menu-tabs should be displayed. Persistent HTML Local Storage
iconify-version blog.angulion.dev Used by the website's content management system (CMS) to determine how the website's menu-tabs should be displayed. Persistent HTML Local Storage

Marketing (1)

Marketing cookies are used to track visitors across websites. The intention is to display ads that are relevant and engaging for the individual user and thereby more valuable for publishers and third party advertisers.

Name Provider Purpose Maximum Storage Duration Type
i/jot/embeds Twitter Inc. Sets a unique ID for the visitor, that allows third party advertisers to target the visitor with relevant advertisement. This pairing service is provided by third party advertisement hubs, which facilitates real-time bidding for advertisers. Session Pixel Tracker

Unclassified (5)

Unclassified cookies are cookies that we are in the process of classifying, together with the providers of individual cookies.

Name Provider Purpose Maximum Storage Duration Type
__ph_opt_in_out_phc_mhkFeVpp5lxOsefr2qaJVPXQlpXkfgPgG5RTHTRKy4M blog.angulion.dev Pending Session HTTP Cookie
ph_#_posthog [x2] blog.angulion.dev Pending 1 year HTTP Cookie
ph_#_primary_window_exists blog.angulion.dev Pending Session HTML Local Storage
ph_#_window_id blog.angulion.dev Pending Session HTML Local Storage

Angular dev did a blog in Astro

Post cover

Hey! 👋


First of all: welcome to my blog 🎉

It’s a pleasure to share my knowledge, probably some jokes, tips & overall experience with you!

Disclaimer


Your overall thoughts on Astro can be different - this posts presents my own opinion 😇

Why exactly this topic?


Yeah.. I know it’s the most common topic & it seems trivial, but I HAD TO!


One, really solid Angular player I really like and who (I assume 🤫) likes me a bit, suggested to go with exactly this topic for the first post.


Thank you, your idea was just great! Huge shout out to Mihai Paraschivescu 💛

How it started? 🛫


I must admit I was really impressed of Astro when I started contributing to (btw. really great) initiative - Angular Snippets by Santosh Yadav


A few things I paid attention to:

  • Lightweight
  • Simplicity
  • Imo great docs
  • A lot of integrations


So I thought, yeah… let’s do it

Of course the first step was to install it, boom:

pnpm create astro@latest


Then I was asked about a template: yes, the blog one please...

I looked around 👀


Because as an Angular Dev I was really interested in:

  • How routing works
  • How to compose components
  • How to pass props to components
  • Other things…


Routing

The same game as with the Nuxt - cool

Maybe we’re getting old… 👴

The fresh approaches everywhere… 🍓


It means routes are automatically generated based on folders’ structure


For me it was really beneficial, why?

✅ I love to keep a structure meaningful

✅ This project must be simple

✅ No ...RoutingModule, const routes = [...] etc.


Example ⌛️ (time, heh)

Structure of routes generation

  • It scans pages folder to build the structure
  • There’s no nesting, so the first index.astro is just /
  • blog folder means now we have /blog
  • index.astro inside the blog folder represents a structure of /blog
  • [...slug] represents a concrete post, so now we have /blog/my-concrete-post


Components’ composition

Pretty cool! We can just…


Components' composition

…use it like in Angular

How to pass props

Let’s assume our HeaderLink takes position from the parent Header

<HeaderLink href="/" position={'top'}>Home</HeaderLink>


Then in our HeaderLink we just:

const { position } = Astro.props;

Integrations ⚙️



This is the part I really appreciate 🤝

How do you think - what else do I need for the blog?


Some styling 🔊: yes, welcome Tailwind!

Some content 🔊: yes, welcome MDX!


Started with simple astro-cli commands:

pnpm astro add mdx

pnpm astro add tailwind


And ended with an Astro’s warm welcome, with everything configured

export default defineConfig({
	site: "https://blog.angulion.dev",
	integrations: [mdx(), sitemap(), tailwind()],
});

I was also curious how the MDX fetching works. First thing - src/content is reserved for collections, second thing - these collections are accessible via astro:content. Let’s say we have a PostLayout component being a styled wrapper, then we have a [...slug].astro component loaded basing on nested structure + a slug of a post. The component uses getCollection() to get the whole collection, then it’s being filtered to find the exact post by a slug props.


And finally:


const post = Astro.props;
const { Content } = await post.render();
---
<PostLayout {...post}>
  <Content />
</PostLayout>

BTW the What you can expect header and the whole table are automatically generated by rehype-toc & rehype-autolink-headers. Tbh it required to be styled, nothing more 😛

Let’s build ⚒️


The biggest benefit of this project was that I wanted go ##YOLO 🤪

Fu** architecture, engineering, thinking about abstractions - it was supposed to be fun, so it was 😀

Landing on the first shot 🛬


By doing a landing I did some new Astro components, really nice & smooth

Browse all posts page 🔎

Here I just reused some components from the previous page - 😎


However, title of this page contains “Browse”, so I thought it’d be nice to have some search bar


Meme according to the problem

astro components === non-client code


💡 It means we’ve no access to window or document


Vue components to the rescue 🤝

Are u still here? So here we go again:


pnpm astro add vue


And we’re home, keeping in mind one quote from the greatest monk ever:

Vue components in Astro, no Astro components in Vue


EASY 🔥

Blog post page 🗾

For the first time it seemed like everything works


Meme according to the problem

Markdown doesn’t work properly for headers, lists, etc.

I still don’t know what’s wrong 🤪, but workaround was to use global styles assuming the only article is the blog post one

EASY 🔥

I also had a problem with non-working tailwind (vue)

This time I was just stupid, apologies

SEO 🧑‍💻


Yeah.. again - I was warmly welcomed by the built-in BaseHead component:


<!-- Global Metadata -->
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />

<!-- Canonical URL -->
<link rel="canonical" href={canonicalURL} />

<!-- Primary Meta Tags -->
<title>{title}</title>
<meta name="title" content={title} />
<meta name="description" content={description} />

<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content={Astro.url} />
<meta property="og:title" content={title} />
...
...


Then, as we already know the flexibility of Astro, it can just be used in other components


<!DOCTYPE html>
<html lang="en">
  <head>
    <BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />
  </head>


Small adjustments and we’re home again!

Deployment


Oh c'mon maaan, that's TOO EASY!

Meme according to the problem

I used Netlify, so:

  • Click to connect repo
  • Build command + dist
  • Click
  • Done


And in the meantime setting a domain with SSL


BTW. I don’t use SSR!



What’s more, astro-sitemap plugin takes care of creating a sitemap ‼ NICE

A11Y, Performance, Best practices


Let’s begin with the Lighthouse report 😀



At this point I just adjusted semantics, because of semi-#YOLO building

A11y

Unforunately, I haven’t found any good #accessibility tricks in the blog template, maybe I selected wrong one. Anyways - proper sectioning, aria-labels, alting… even skip to content did a trick!

Performance & Best practices

I had one problem with content compression - no problem, there’s always astro-compress easy to integrate as other tools. COOOOOL!

btw.. when you’ll see 272 console issues - that’s the problem of embedded tweets & twitter itself

Wrapping up 🔥


I really enjoyed working on this project

For me Astro was the best choice:

  • Wider perspective / get out of (Angular) comfort zone
  • Simplicity
  • Easy integrations
  • A lot of built-in components/approaches
  • Hugely customizable
  • Good docs
  • And the most important: it was a fun to work with it

Keeping in mind simplicity, no complex logic, no advanced data manipulation, no need to make it hugely scalable - that’s why I didn’t choose Angular (which I love btw)



- - Yours truly