Webdevelopment heeft een lange weg afgelegd sinds de begindagen van de statische persoonlijke websites met maar één pagina. Inmiddels hebben we een overvloed aan verschillende talen, frameworks en content management systemen om uit te kiezen – ontworpen voor elke denkbare niche.

Dat is waar Astro om de hoek komt kijken, een van de nieuwkomers op het gebied van JavaScript.

Astro is inmiddels een favoriet geworden in de devcommunity en is gemaakt door Fred K. Schott en een groep andere medewerkers. Het is een alles-in-één framework dat veel lijkt op een statische sitegenerator.

In dit artikel leggen we uit waarom zoveel developers Astro zo waarderen en het verkiezen boven andere oplossingen. We laten je ook zien hoe je een op markdown gebaseerde blog kunt bouwen met het framework.

Wat is Astro?

Het Astro-logo in zwart, met
Astro

Astro, of Astro.js, is een populaire statische sitegenerator die is ontworpen voor diegenen die websites met veel inhoud willen maken die snel en soepel draaien. Het lichte gewicht, de intuïtieve structuur en de lichte leercurve maken het aantrekkelijk voor developers van alle ervaringsniveaus.

Ondanks de kleine afmetingen beschikt Astro over krachtige gereedschappen die de flexibiliteit van je site drastisch vergroten, waardoor je uren kunt besparen op het beheren van inhoud en thema’s. Daarnaast geeft het ontwikkelaars de optie om met hun favoriete frameworks te werken in combinatie met Astro – een aantrekkelijk vooruitzicht voor doorgewinterde programmeurs die al een groot aantal favorieten hebben.

Dit zijn slechts een paar van de manieren waarop Astro zich onderscheidt van de rest:

  • Eilandarchitectuur: Astro splitst je gebruikersinterface (UI) op in kleinere, geïsoleerde componenten die bekend staan als “Astro islands” en die op elke pagina kunnen worden gebruikt. Ongebruikte JavaScript wordt vervangen door lichtgewicht HTML.
  • Nul JavaScript (standaard): Hoewel je alle JavaScript kunt gebruiken die je wilt om je websites te maken, zal Astro proberen om nul JavaScript in te zetten voor productie door je code voor je te transcriberen. Dit is een perfecte aanpak als je je richt op de snelheid van je site.
  • SSG en SSR inbegrepen: Astro begon als een statische sitegenerator, maar gaandeweg is het een framework geworden dat zowel statische sitegeneratie (SSG) als server-side rendering (SSR) gebruikt. En je kunt zelf kiezen welke pagina’s welke aanpak gebruiken.
  • Framework-agnostisch: Als je Astro gebruikt, kun je elk JavaScript framework gebruiken dat je wilt – zelfs meerdere frameworks tegelijk. (We gaan hier later in dit artikel dieper op in)

Bovendien is Astro kant-en-klaar, wat betekent dat het overal en altijd gemakkelijk kan worden ingezet.

Klaar voor meer informatie? Laten we dan eens dieper ingaan op hoe Astro werkt.

De structuur van Astro

Voordat we verder gaan, is het belangrijk om te begrijpen hoe Astro is opgezet zodat je het effectief kunt gebruiken. Laten we eens kijken naar de basisbestandsstructuur van Astro:

├── dist/
├── src/
│   ├── components/
│   ├── layouts/
│   └── pages/
│       └── index.astro
├── public/
└── package.json

Zoals je kunt zien, is de structuur zelf vrij eenvoudig. Er zijn echter enkele belangrijke punten die je moet onthouden:

  • Het grootste deel van ons project is te vinden in de map src. Je kunt je componenten, layouts en pagina’s in submappen plaatsen. Ook kun je extra mappen toevoegen om je project overzichtelijker te maken.
  • De public map is voor alle bestanden die buiten het bouwproces leven, zoals lettertypen, afbeeldingen of een robots.txt bestand.
  • De dist map bevat alle inhoud die je wilt deployen op je productieserver.

Laten we nu dieper ingaan op de belangrijkste onderdelen van Astro: componenten, layouts en pagina’s.

Componenten

Componenten zijn herbruikbare stukjes code die je overal op je website kunt gebruiken, vergelijkbaar met shortcodes in WordPress. Standaard hebben ze de bestandsextensie .astro, maar je kunt ook niet-Astro componenten gebruiken die gebouwd zijn met Vue, React, Preact of Svelte.

Hieronder zie je een voorbeeld van hoe een eenvoudig component eruitziet – in dit geval een geclassificeerde div tag met een h2:


<div class="kinsta_component">
    <h2>Hello, Kinsta!</h2>
</div>

En hier zie je hoe we dat component in onze site kunnen opnemen:

---
import KinstaComponent from ../components/Kinsta.astro
---
<div>
    <KinstaComponent />
</div>

Zoals hierboven is aangetoond, moet je het onderdeel eerst importeren. Pas daarna kan het op de pagina worden opgenomen.

Nu is het tijd om enkele properties aan ons component toe te voegen. Laten we beginnen met een property {title}:

---
const { title = 'Hello' } = Astro.props
---

<div class="kinsta_component">
    <h2>{title}</h2>
</div>

En hier is hoe onze property zou worden geïmplementeerd:

---
import KinstaComponent from ../components/Kinsta.astro
---

<div>
    
    <KinstaComponent title="Good day"/>

    
    <KinstaComponent />
 </div>

Simpel, toch?

Zoals je je waarschijnlijk al hebt gerealiseerd, ligt de echte kracht van Astro’s componenten in hun globale en herbruikbare aard. Ze stellen je in staat om ingrijpende veranderingen in je hele site aan te brengen door slechts een paar regels code te bewerken, wat je ontelbare uren kan besparen die je anders zou besteden aan vervelende, moeizame tekstvervangingen.

Layouts

Laten we het nu eens hebben over layouts. Naast hun bekende thematische functie zijn layouts in Astro ook herbruikbare componenten, maar ze worden gebruikt als codewrappers.

Kijk eens naar dit voorbeeld:

---
// src/layouts/Base.astro
const { pageTitle = 'Hello world' } = Astro.props
---

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width">
    <title>{pageTitle}</title>
</head>
<body>
    <main>
        <slot />
    </main>
</body>
</html>

Let hier ook op de tag <slot /> . Het <slot /> element in Astro fungeert als een placeholder voor HTML tags en inhoud.

Laten we het eens in actie zien.

De code hieronder laat zien hoe onze tag <slot /> wordt vervangen door de gewenste code, die allemaal wordt ingepakt door onze Base.astro layout:

---
import Base from '../layouts/Base.astro';
---

<Base title="Hello world">
    <div>
        <p>Some example text.</p>
    </div>
</Base>

Zoals je kunt zien, is onze tag <slot /> vervangen door de HTML die hij vertegenwoordigt, namelijk:

<div>
    <p>Some example text.</p>
</div>

Zoals je kunt zien, kun je met layouts, net als met componenten, stukken code hergebruiken op je hele site, waardoor het eenvoudiger wordt om je algemene inhoud en ontwerp bij te werken.

Pagina’s

Pagina’s zijn een speciaal type component dat verantwoordelijk is voor routing, het laden van gegevens en templating.

Astro gebruikt bestandsgebaseerde routing om pagina’s te genereren, in plaats van dynamische routing. De bestandsgebaseerde methode verbruikt niet alleen minder bandbreedte, maar bespaart je ook het handmatig importeren van je componenten.

Hier is een voorbeeld van gedefinieerde routes:

src/pages/index.astro => yourdomain.com
src/pages/test.astro => domain.com/test
src/pages/test/subpage => domain.com/test/subpage

Met deze routes zou onze homepage er als volgt uitzien:


<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width">
    <title>Hello World</title>
</head>
<body>
    <h1>Hello, Kinsta</h1>
</body>
</html>

Maar we weten al hoe we layouts moeten gebruiken, dus laten we dit omzetten in iets dat globaal toegankelijk is:

---
import Base from '../layouts/Base.astro';
---

<Base>
    <h1>Hello, Kinsta</h1>
</Base>

Zo, dat is veel strakker.

We zullen later in dit artikel dieper ingaan op routing in Astro, maar nu gaan we verder met het leuke werk: het bouwen en aanpassen van sites.

Astro aanpassen en uitbreiden

Het is tijd om te leren hoe je je Astro site kunt aanpassen! We gaan Markdown collecties, routing, afbeeldingsverwerking en een integratie met React gebruiken om onze statische site op te bouwen en te personaliseren.

Markdown collecties

Met versie 2.0 introduceerde Astro een veel betere manier om Markdown content te onderhouden dan voorheen. Dankzij collecties kunnen we er zeker van zijn dat al onze frontmatter data is opgenomen en de juiste associatie heeft.

Onlangs, in versie 2.5, hebben ze een mogelijkheid toegevoegd om ook JSON en YAML bestanden te beheren als collecties.

Klaar om je handen vuil te maken?

Zet eerst al je Markdown artikelen in de map src/content/collection_name. We gaan voor dit project een blogcollectie maken, dus in onze demonstratie wordt de map src/content/blog.

Nu is het tijd om alle vereiste frontmatter velden te definiëren in ons src/content/config.ts bestand. Onze blog heeft het volgende nodig:

  • title (string)
  • tags (array)
  • publishDate (tijd)
  • image (string, optioneel)

Zo ziet het er allemaal samen uit:

import { z, defineCollection } from 'astro:content';

const blogCollection = defineCollection({ 
    schema: z.object({
        title: z.string(),
        tags: z.array(z.string()),
        image: z.string().optional(),
        publishDate: z.date(),
    }),
});

export const collections = {
    'blog': blogCollection,
};

En dit is wat ons article-about-astro.md Markdown bestand bevat:

---
title: Article about Astro
tags: [tag1, tag3]
publishDate: 2023-03-01
---
## Tamen risit

Lorem *markdownum flumina*, laceraret quodcumque Pachyne, **alter** enim
cadavera choro.

Het klopt dat er niets bijzonders is aan ons Markdown bestand. Maar er is wel wat verborgen magie die zichtbaar wordt als we een typefout maken.

Laten we bijvoorbeeld zeggen dat we in plaats van publishDate per ongeluk publishData hebben getypt. In het geval van zo’n spelfout zal Astro een foutmelding geven:

blog → article-about-astro.md frontmatter does not match collection schema.
  "publishDate" is required.

Verbazingwekkend, toch? Deze handige functie kan ons helpen om fouten met betrekking tot frontmatter in een paar seconden te vinden.

Het laatste dat we moeten toevoegen is een pagina met onze gegevens. Laten we een bestand maken op src/page/blog/[slug].astro met de volgende code:

---
import Base from '../../layouts/Base.astro';
import { getCollection } from 'astro:content';
export async function getStaticPaths() {
    const blogEntries = await getCollection('blog');
    return blogEntries.map(entry => ({
        params: { slug: entry.slug }, props: { entry },
  }));
}
const { entry } = Astro.props;
const { Content } = await entry.render();
---
<Base>
    <h1>{entry.data.title} </h1>
    <Content />
</Base>

Dankzij getStaticPaths maakt Astro alle statische pagina’s voor elk bericht in de blogcollectie.

Het enige wat we nu nog missen is een lijst van al onze artikelen:

---
import Base from '../../layouts/Base.astro';

import { getCollection } from 'astro:content';
const blogEntries = await getCollection('blog');
---
<Base>
<ul>
    {blogEntries.map(item => <li> <strong><a href={'/blog/' + item.slug}>{item.data.title}</a></strong></li>)}
</ul>
</Base>

Zoals je kunt zien, maakt het gebruik van collecties deze taak opmerkelijk eenvoudig.

Laten we nu een gegevenstypecollectie maken. Eerst moeten we het bestand src/content/config.ts opnieuw openen en een nieuwe gegevenscollectie toevoegen:

import { z, defineCollection, referenece } from 'astro:content';

const blogCollection = defineCollection({ 
	type: 'content',
    schema: z.object({
        title: z.string(),
        tags: z.array(z.string()),
        image: z.string().optional(),
        publishDate: z.date(),
	    author: reference('authors')
    }),
});

const authorsCollection = defineCollection({ 
	type: 'data',
    schema: z.object({
        fullName: z.string(),
        country: z.string()
    }),
});


export const collections = {
    'blog': blogCollection,
'authors': authorsCollection,
};

Naast het maken van een nieuwe collectie , hebben we ook de verwijzing naar de auteur toegevoegd in de blogCollection.

Tijd om een nieuwe auteur (author) aan te maken. Hiervoor moeten we een bestand maciek-palmowski.json maken in de content/authors.json:

{
    "fullName": "Maciek Palmowski",
    "country": "Poland"
}

Het laatste wat we nog moeten doen is deze gegevens ophalen in onze Post. Hiervoor moeten we getEntry gebruiken:

---
import Base from '../../layouts/Base.astro';
import { getCollection, getEntry } from 'astro:content';
export async function getStaticPaths() {
  const blogEntries = await getCollection('blog');
  return blogEntries.map(entry => ({
    params: { slug: entry.slug }, props: { entry },
  }));
}
const { entry } = Astro.props;
const author = await getEntry(entry.data.author);
const { Content } = await entry.render();
---
<Base>
<h1>{entry.data.title}</h1>
<h2>Author: {author.data.fullName}</h2>
<Content />
</Base>

Routing

Astro heeft twee verschillende routeringsmodi. We hebben al geleerd over de eerste – statische (bestandsgebaseerde) routing – toen we eerder pagina’s behandelden.

Nu gaan we onze aandacht verleggen naar dynamisch routen.

Met behulp van dynamische routeparameters kun je een Astro paginabestand opdracht geven om automatisch meerdere pagina’s met dezelfde structuur te maken. Dit is handig als je veel van één bepaald type pagina hebt (denk aan auteursbiografieën, gebruikersprofielen, documentatieartikelen, enzovoort).

In het volgende voorbeeld gaan we aan de slag met het genereren van biopagina’s voor onze auteurs.

In Astro’s standaard statische uitvoermodus worden deze pagina’s gegenereerd tijdens het bouwen, wat betekent dat je vooraf moet bepalen welke auteurs een corresponderend bestand krijgen. In de dynamische modus daarentegen, worden pagina’s op verzoek gegenereerd voor elke route die overeenkomt.

Als je een variabele wilt doorgeven als bestandsnaam, voeg er dan haakjes omheen:

pages/blog/[slug].astro -> blog/test, blog/about-me 

Laten we hier dieper op ingaan aan de hand van de code uit ons src/page/blog/[slug] bestand:

---
import Base from '../../layouts/Base.astro';
import { getCollection } from 'astro:content';
export async function getStaticPaths() {
    const blogEntries = await getCollection('blog');
    return blogEntries.map(entry => ({
        params: { slug: entry.slug }, props: { entry },
  }));
}
const { entry } = Astro.props;
const { Content } = await entry.render();
---
<Base>
    <h1>{entry.data.title}</h1>
    <Content />
</Base>

De getStaticPaths route is verantwoordelijk voor het genereren van alle statische pagina’s. Hij geeft twee objecten: terug

  • params: Gebruikt om de haakjes in onze URL’s te vullen
  • props: Alle waarden die we doorgeven aan de pagina

En daarmee is het genereren van je pagina geregeld.

Afbeeldingsverwerking

We kunnen het niet over krachtige websites hebben zonder het te hebben over moderne afbeeldingsformats, correcte methoden om de grootte aan te passen en lazyloaden.

Gelukkig helpt Astro ons hier ook. Dankzij het @astrojs/image pakket kunnen we al het bovenstaande binnen enkele minuten introduceren.

Na het installeren van het pakket krijgen we toegang tot twee componenten: Image en Picture.

De component Image wordt gebruikt om een geoptimaliseerde tag <img /> te maken. Hier is een voorbeeld:

---
import { Image } from '@astrojs/image/components';
import heroImage from '../assets/hero.png';
---

<Image src={heroImage} format="avif" alt="descriptive text" />
<Image src={heroImage} width={300} alt="descriptive text" />
<Image src={heroImage} width={300} height={600} alt="descriptive text" />

Op dezelfde manier creëert het Picture component een geoptimaliseerd <picture/> component:

---
import { Picture } from '@astrojs/image/components';
import hero from '../assets/hero.png';
---
<Picture src={hero} widths={[200, 400, 800]} sizes="(max-width: 800px) 100vw, 800px" alt="descriptive text" />

SSG vs SSR

Standaard draait Astro als een statische sitegenerator. Dit betekent dat alle inhoud wordt geconverteerd naar statische HTML pagina’s.

Hoewel dit vanuit veel oogpunten een perfecte benadering is (vooral met betrekking tot snelheid), geven we soms de voorkeur aan een meer dynamische benadering. Als je bijvoorbeeld voor elke gebruiker een aparte profielpagina wilt, of als je duizenden artikelen op je site hebt, zou het veel te tijdrovend zijn om alles elke keer opnieuw te renderen.

Gelukkig kan Astro ook werken als een volledig server-side gerenderd framework of in een hybride modus tussen de twee.

Om side-wide SSR in te schakelen, moeten we de volgende code toevoegen aan astro.config.mjs:

import { defineConfig } from 'astro/config';

export default defineConfig({
    output: 'server'
});

Dit is de standaard aanpak.

De hybride aanpak betekent dat standaard alles dynamisch wordt gegenereerd, behalve de pagina’s met export const prerender = true toegevoegd.

Met Astro 2.5 is er ook de mogelijkheid om statische rendering als standaard in te stellen en dynamische routes handmatig te selecteren.

Hierdoor kunnen we bijvoorbeeld een volledig statisch gegenereerde website maken met dynamische aanmeldings- en profielpagina’s. Leuk!

Je kunt hier meer over lezen in de officiële documentatie.

Andere JavaScript frameworks integreren

Een andere geweldige feature van Astro maakt het mogelijk om je favoriete framework mee te nemen en het te gebruiken in combinatie met Astro. Je kunt Astro mixen met React, Preact, Svelte, Vue, Solid of Alpine (voor alle integraties, zie Astro’s “Adding integrations” documentatie).

Laten we zeggen dat we React willen gebruiken. Eerst moeten we de integratie installeren door het volgende uit te voeren in npm:

npx astro add react

Nu React is geïntegreerd, kunnen we een React component maken. In ons geval wordt dat het counter component in src/components/ReactCounter.tsx:

import { useState } from 'react';

/** A counter written with React */
export function Counter({ children }) {
    const [count, setCount] = useState(0);
    const add = () => setCount((i) => i + 1);
    const subtract = () => setCount((i) => i - 1);

    return (
        <>
            <div className="counter">
                <button onClick={subtract}>-</button>
                <pre>{count}</pre>
                <button onClick={add}>+</button>
                </div>
            <div className="counter-message">{children}</div>
        </>
    );
}

Last but not least moeten we de teller op onze pagina plaatsen met de volgende code:

---
import * as react from '../components/ReactCounter';
---
<main>
    <react.Counter client:visible />
</main>

En voilà: je React component is naadloos geïntegreerd in je site.

Astro deployen met Kinsta

Nu is het tijd om onze Astro site op het web te zetten. Gelukkig is Kinsta de perfecte host voor snelle en probleemloze Statische Site Hosting.

Begin met het maken van een GitHub repository voor de bestanden van je site. Als je nog niet klaar bent om je eigen bestanden te gebruiken, kun je deze Astro startsitetemplate klonen die ons team heeft gemaakt.

Zodra je repo klaar is, volg je deze stappen om je statische site te deployen naar Kinsta:

  1. Login of maak een account aan om je MyKinsta’s dashboard te bekijken.
  2. Autoriseer Kinsta met je Git provider.
  3. Klik op Statische sites in de linker zijbalk, klik dan op Site toevoegen.
  4. Selecteer de repository en de branch waarvan je wilt deployen.
  5. Geef je site een unieke naam.
  6. Voeg de build instellingen toe in het volgende format:
    • Build commando: npm run build
    • Node versie: 18.16.0
    • Publish directory: dist
  7. Klik ten slotte op Site maken.

En dat is het! Je hebt nu een live, volledig functionerende statische site gemaakt met het Astro framework.

Een donkere pagina met het Kinsta logo in het wit in het midden boven de woorden
Onze live Astro homepage

Je vindt je live URL en andere details over het deployen op het tabblad Deployments.

Als alternatief voor Statische Site Hosting kun je ervoor kiezen om je statische site te deployen met Kinsta’s Applicatie Hosting, die meer flexibiliteit biedt bij het hosten, een breder scala aan voordelen en toegang tot robuustere features. Bijvoorbeeld schaalbaarheid, aangepaste implementatie met behulp van een Dockerfile en uitgebreide analytics met real-time en historische gegevens.

Samenvatting

Astro’s duidelijke structuur, eenvoudige syntaxis en globale componenten maken het bouwen en uitvoeren van een applicatie eenvoudig. de lichtgewicht aard en het dubbele gebruik van statische en dynamische routing verhogen de reactiesnelheid van de site aanzienlijk, terwijl de mogelijkheid om samen te werken met en naast andere JavaScript frameworks het des te aantrekkelijker maakt voor ervaren programmeurs.

Als het je doel is om een site met veel inhoud te maken die snel laadt, modulaire functionaliteit biedt en zowel statisch als dynamisch genereert, dan is Astro misschien wel de juiste keuze voor jou.

Je kunt je statische website gratis hosten met Kinsta’s Statische Site Hosting.

Wat vind jij van de Astro statische website generator? Heb je het gebruikt voor je eigen project? Laat het ons weten in het comments hieronder.

Maciek Palmowski

Maciek is een web developer en werkt bij Kinsta als Development Advocate Analyst. Na werktijd besteedt hij de meeste tijd aan coderen, interessant nieuws zoeken voor zijn nieuwsbrieven of koffie drinken.