Astro is a new approach to JavaScript’s current frenzy, extracting more performance from your reactive front end. Developed by the same team that created the Snowpack build tool.
There have been several attempts to improve performance by avoiding the expensive prefetching and bootstrapping that has plagued frameworks like React. This is the infamous moisture problem described here.
Astro takes an interesting and novel approach. It’s a build system that lets you use whatever framework you want (React, Svelte, Vue, etc.) and does the work of finding the best places to use lazy loading. You can think of this as a kind of smart code splitting applied to your app when it’s bundled.
So you can use the same familiar framework you use today, but with the potential for significant performance gains.
island architecture
The web architecture proposed by Astro is sometimes called island architecture. The core idea is that islands are interactive JavaScript dependent components surrounded by pure HTML/CSS markup.
By splitting up the app in this way, we can send all the HTML directly to the browser, so the user can do something with it, and the parts that rely on JavaScript can be loaded only when needed. You can also tell Astro to defer JavaScript until the component is visible to the user, as shown below.
Working with Astro
Familiarize yourself with Astro using our online sandbox. Click here to open.
This URL will display a simple page with a time stamp named Page.astro. Notice that the page (Listing 1) is divided into two sections. The first triple dash (---
) contains code that is executed on the server at build time, not at run time. His second section, denoted by the second three dashes, contains markup that is delivered at runtime.
Listing 1. A simple Astro sandbox
---
import {format} from 'date-fns';// Welcome to Astro!
// Write JavaScript & TypeScript here, in the "component script."
// This will run during the build, but never in the final output.
// Use these variables in the HTML template below.
//
// Full Syntax:
// https://docs.astro.build/core-concepts/astro-components/const builtAt: Date = new Date();
const builtAtFormatted = format(builtAt, 'MMMM dd, yyyy -- H:mm:ss.SSS');
---
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Astro Playground</title>
<style>
header {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
margin-top: 15vh;
font-family: Arial;
}
.note {
margin: 0;
padding: 1rem;
border-radius: 8px;
background: #E4E5E6;
border: 1px solid #BBB;
}
</style>
</head>
<body>
<header>
<img width="60" height="80" src="https://bestofjs.org/logos/astro.svg" alt="Astro logo">
<h1>Hello, Astro!</h1>
<p class="note">
<strong>RENDERED AT:</strong><br/>
{builtAtFormatted}
</p>
</header>
</body>
</html>
Note how {builtAtFormatter}
Used to reference build-time variables in markup.
Add components to Astro
Now let’s add a component. Click the plus icon in the top file bar as shown in image 1.
Image 1. Add components
The new component receives a default name (Component1.astro) and content, as shown in Listing 2.
Listing 2. Component1.astro
---
const name = "Component"
---<h1>Hello {name}</h1>
Again, there is simple variable assignment and display. Take advantage of main page components.
Return to Page.astro. Note that the system conveniently inserts imports in JavaScript segments.
import Component from '@/Component.astro';
You can make use of this component by inserting <Component />
to markup. When you do this, the output of the child component will be displayed in the preview window.
Using frameworks with Astro
A nice feature of Astro is that it supports various other frameworks. This is done by using a rendering engine during the build process and compiling them into “islands” of components. Let’s see how this works.
Open this link and you’ll see the Astro app running the Svelte component. (This is an example showing some rendering engines.)
The first thing you’ll notice in the Svelte demo linked above is the astro.config.mjs file. The contents of this file should look like Listing 3.
Listing 3. Enabling the Svelte renderer
export default /** @type {import('astro').AstroUserConfig} */ ({
// Enable the Svelte renderer to support Svelte components.
renderers: ['@astrojs/renderer-svelte'],
});
Listing 3 shows how to enable Svelte so that the engine understands Svelte components. Svelte files can now be imported directly into Astro files. For example, add the following lines to /pages/index.astro:
import Counter from '../components/Counter.svelte
Now you can use Svelte’s Counter in Astro, as shown in Listing 4.
Listing 4. Using Svelte components in Astro
<Counter client:visible>
<h1>Hello, Svelte!</h1>
</Counter>
Note that this is typical Svelte usage, but counters have Astro-specific properties. client:visible
This means that the component will not be loaded on the client unless the component is visible on the page. Therefore, it achieves fine-grained lazy loading with minimal effort.
At the time of writing, Astro supports Svelte, React, Vue, Solid, Preact, and Lit. Usage is the same as Suberto. In fact, you can enable multiple rendering engines and use them side by side in your Astro app.
In addition to integrations, Astro also offers several themes and starters.
Fine tune partial hydration with Astro
you saw client:visible
during directive execution. We have others, too. In either case, the directive first tells Astro to render the component on the client using accompanying JavaScript instead of doing server rendering and sending HTML. Then tell Astro how to hydrate the ingredients.
Astro client directive
Astro’s client directives control how components are hydrated on the page.
<MyComponent client:load />
: hydrate the component on page load.<MyComponent client:idle />
: hydrate the component as soon as the main thread is released (requestIdleCallback()
).<MyComponent client:visible />
: hydrates the component as soon as the element enters the viewport (IntersectionObserver
). Useful for content at the bottom of the page.<MyComponent client:media={QUERY} />
: hydrates the component as soon as the browser matches the given media query (matchMedia
). Useful for sidebar toggles or other elements that only appear on mobile or desktop devices.<MyComponent client:only={string} />
: Hydrates the component on page load and renders only on the client. Takes the framework of the component as a string (e.g."svelte"
).
Build-time approach
Astro is essentially a build tool, so you have complete control over what ends up being sent to the user’s browser. So in addition to doing clever things with lazy-loaded JavaScript, Astro can also be clever about how it serves other assets like CSS.
Additionally, Astro’s goal is to extract as much JavaScript as possible into plain HTML. That means less data over the wire, less browser churn, and faster time to interactivity.
Overall, Astro is certainly for static sites, but it’s a promising, innovative approach and a very active project with nearly 16,000 stars on GitHub.
Copyright © 2022 IDG Communications, Inc.