
Security News
Deno 2.2 Improves Dependency Management and Expands Node.js Compatibility
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
Astro is a modern static site builder that allows you to build fast, optimized websites using your favorite tools and frameworks. It focuses on delivering a zero-JavaScript runtime by default, making it ideal for performance-focused web projects.
Static Site Generation
Astro allows you to generate static sites with ease. The configuration file specifies the site URL and output directory for the build.
```javascript
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
site: 'https://example.com',
build: {
outDir: 'dist',
},
});
```
Component Framework Agnostic
Astro supports multiple component frameworks like React, Vue, Svelte, and more. You can use your preferred framework within Astro components.
```jsx
// src/pages/index.astro
---
import React from 'react';
import MyComponent from '../components/MyComponent.jsx';
---
<MyComponent />
```
Markdown Support
Astro has built-in support for Markdown, making it easy to write content for your static site.
```markdown
---
title: 'My Blog Post'
date: '2023-10-01'
---
# Hello, World!
This is a blog post written in Markdown.
```
Partial Hydration
Astro's partial hydration feature allows you to load JavaScript only when necessary, improving performance by reducing the amount of JavaScript sent to the client.
```jsx
// src/components/InteractiveComponent.jsx
import { useState } from 'react';
export default function InteractiveComponent() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}
// src/pages/index.astro
---
import InteractiveComponent from '../components/InteractiveComponent.jsx';
---
<InteractiveComponent client:load />
```
Next.js is a popular React framework for building server-side rendered and static web applications. It offers a rich set of features like API routes, dynamic routing, and built-in CSS support. Compared to Astro, Next.js is more focused on server-side rendering and full-stack capabilities.
Gatsby is a React-based framework for building static sites and apps. It emphasizes performance and scalability, offering a rich plugin ecosystem and GraphQL data layer. While Gatsby is similar to Astro in terms of static site generation, it is more tightly coupled with React and has a steeper learning curve.
Eleventy is a simpler static site generator that focuses on flexibility and minimalism. It supports multiple templating languages and is highly configurable. Unlike Astro, Eleventy does not have built-in support for modern JavaScript frameworks like React or Vue.
Astro is a next-generation static-site generator with partial hydration. Use your favorite JS framework and ship bare-minimum JS (or none at all!).
# currently hidden during private beta, please don't share :)
npm install astro@shhhhh
# NOTE: There is currently a bug in Snowpack that prevents you
# from using astro outside of the monorepo setup that we have here.
# For now, do all development inside the `examples/` directory for this repo.
Add a dev
npm script to your /package.json
file:
{
"scripts": {
"dev": "astro dev ."
}
}
Then run:
npm run dev
To configure Astro, add a astro.config.mjs
file in the root of your project. All settings are optional. Here are the defaults:
export default {
/** Where to resolve all URLs relative to. Useful if you have a monorepo project. */
projectRoot: '.',
/** Path to Astro components, pages, and data */
astroRoot: './astro',
/** When running `astro build`, path to final static output */
dist: './_site',
/** A folder of static files Astro will copy to the root. Useful for favicons, images, and other files that don’t need processing. */
public: './public',
/** Extension-specific handlings */
extensions: {
/** Set this to "preact" or "react" to determine what *.jsx files should load */
'.jsx': 'react',
},
/** Your public domain, e.g.: https://my-site.dev/ */
site: '',
};
By default, Astro outputs zero client-side JS. If you'd like to include an interactive component in the client output, you may use any of the following techniques.
<MyComponent />
will render an HTML-only version of MyComponent
(default)<MyComponent:load />
will render MyComponent
on page load<MyComponent:idle />
will use requestIdleCallback() to render MyComponent
as soon as main thread is free<MyComponent:visible />
will use an IntersectionObserver to render MyComponent
when the element enters the viewportFrontend state management depends on your framework of choice. Below is a list of popular frontend state management libraries, and their current support with Astro.
Our goal is to support all popular state management libraries, as long as there is no technical reason that we cannot.
react-redux
support requires the ability to set a custom <Provider>
wrapper to every component island. Planned.)vuex
support requires the ability to set a custom vue.use(store)
call to every component island. Planned.)Are we missing your favorite state management library? Add it to the list above in a PR (or create an issue)!
If you’ve used Svelte’s styles before, Astro works almost the same way. In any .astro
file, start writing styles in a <style>
tag like so:
<style>
.scoped {
font-weight: bold;
}
</style>
<div class="scoped">I’m a scoped style</div>
Astro also supports Sass out-of-the-box; no configuration needed:
<style lang="scss">
@use "../tokens" as *;
.title {
color: $color.gray;
}
</style>
<h1 class="title">Title</h1>
Supports:
lang="scss"
: load as the .scss
extensionlang="sass"
: load as the .sass
extension (no brackets; indent-style)We also automatically add browser prefixes using Autoprefixer. By default, Astro loads the default values, but you may also specify your own by placing a Browserslist file in your project root.
By default, any Astro component can fetch data from any API or local *.md
files. But what if you had a blog you wanted to paginate? What if you wanted to generate dynamic URLs based on metadata (e.g. /tag/[tag]/
)? Or do both together? Astro Collections are a way to do all of that. It’s perfect for generating blog-like content, or scaffolding out dynamic URLs from your data.
First, decide on a URL schema. For our example, perhaps you want all your paginated posts at /posts/1
, /posts/2
, etc. But in addition, you also wanted /tag/[tag]
and /year/[year]
collections where posts are filtered by tag or year.
Next, for each “owner” of a URL tree, create a /astro/pages/$[collection].astro
file. So in our example, we’d need 3:
└── astro/
└── pages/
├── $posts.astro -> /posts/1, /posts/2, …
├── $tag.astro -> /tag/[tag]/1, /tag/[tag]/2, …
└── $year.astro -> /year/[year]/1, /year/[year]/2, …
Lastly, in each $[collection].astro
file, add 2 things:
export let collection: any;
export async function createCollection() {
return {
async data() {
// return data here to load (we’ll cover how later)
},
};
}
These are important so your data is exposed to the page as a prop, and also Astro has everything it needs to gather your data and generate the proper routes. How it does this is more clear if we walk through a practical example.
Assume we have Markdown files that have title
, tag
, and date
in their frontmatter, like so:
---
title: My Blog Post
tag: javascript
date: 2021-03-01 09:34:00
---
# My Blog post
…
It’s important to know that these could be anything! There’s no restrictions around what can go in your frontmatter, but these will explain values we see later. Assume nothing is “special“ or reserved; we named everything.
Also, assume we want the following final routes:
/post/[slug]
./posts/1
for page 1, /posts/2
for page 2, etc./tag/[tag]/1
for tagged posts, page 1, or /year/[year]/1
for posts by year. We’ll add these at the end.Let’s start with paginated posts. Since we want /posts/
to be the root, we’ll create a file at /astro/pages/$posts.astro
(the $
indicates that this is a multi-route page):
// /astro/pages/$posts.astro
---
import Pagination from '../components/Pagination.astro';
import PostPreview from '../components/PostPreview.astro';
export let collection: any;
export async function createCollection() {
const allPosts = Astro.fetchContent('./post/*.md'); // load data that already lives at `/post/[slug]`
allPosts.sort((a, b) => new Date(b.date) - new Date(a.date)); // sort newest -> oldest (we got "date" from frontmatter!)
// (load more data here, if needed)
return {
async data() {
return allPosts;
},
pageSize: 10, // how many we want to show per-page (default: 25)
};
}
---
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Blog Posts: page {collection.page.current}</title>
<link rel="canonical" href={collection.url.current} />
<link rel="prev" href={collection.url.prev} />
<link rel="next" href={collection.url.next} />
</head>
<body>
<main>
<h5>Results {collection.start + 1}–{collection.start + 1 + collection.page.size} of {collection.total}</h6>
{collection.data.map((post) => (
<PostPreview post={post} />
)}
</main>
<footer>
<Pagination
currentPage={collection.page.current}
totalPages={collection.page.last}
prevURL={collection.url.prev}
nextURL={collection.url.next}
/>
</footer>
</body>
</html>
Let’s walk through some of the key parts:
export let collection
: this is important because it exposes a prop to the page for Astro to return with all your data loaded. ⚠️ It must be named collection
.export async function createCollection()
: this is also required, and must be named this exactly. This is an async function that lets you load data from anywhere (even a remote API!). At the end, you must return an object with { data: yourData }
. There are other options such as pageSize
we’ll cover later.{collection.data.map((post) => (…
: this lets us iterate over all the markdown posts. This will take the shape of whatever you loaded in createCollection()
. It will always be an array.{collection.page.current}
: this, and other properties, simply return more info such as what page a user is on, what the URL is, etc. etc.It should be noted that the above example shows <PostPreview />
and <Pagination />
components. Pretend those are custom components that you made to display the post data, and the pagination navigation. There’s nothing special about them; only consider those examples of how you’d use collection data to display everything the way you’d like.
In our earlier example, we covered simple pagination for /posts/1
, but we’d still like to make /tag/[tag]/1
and /year/[year]/1
. To do that, we’ll create 2 more collections: /astro/pages/$tag.astro
and astro/pages/$year.astro
. Assume that the markup is the same, but we’ve expanded the createCollection()
function with more data.
// /astro/pages/$tag.astro
---
import Pagination from '../components/Pagination.astro';
import PostPreview from '../components/PostPreview.astro';
export let collection: any;
export async function createCollection() {
const allPosts = Astro.fetchContent('./post/*.md');
allPosts.sort((a, b) => new Date(b.date) - new Date(a.date));
+ const allTags = [...new Set(allPosts.map((post) => post.tags).flat())]; // gather all unique tags (we got "tag" from frontmatter!)
+ allTags.sort((a, b) => a.localeCompare(b)); // sort tags A -> Z
+ const routes = allTags.map((tag) => ({ tag })); // this is where we set { params: { tag } }
return {
- async data() {
- return allPosts;
+ async data({ params }) {
+ return allPosts.filter((post) => post.tag === params.tag); // filter post by "date" frontmatter, from params (we get `{ params }` from the routes array above)
},
pageSize: 10,
+ routes,
+ permalink: ({ params }) => `/tag/${params.tag}/` // important! the root must match (/tag/[tag] -> $tag.astro)
};
}
---
Some important concepts here:
routes = allTags.map((tag) => ({ tag }))
: Astro handles pagination for you automatically. But when it needs to generate multiple routes, this is where you tell Astro about all the possible routes. This way, when you run astro build
, your static build isn’t missing any pages.permalink: ({ params }) =>
/tag/${params.tag}/: this is where you tell Astro what the generated URL should be. Note that while you have control over this, the root of this must match the filename (it’s best **NOT** to use
/pages/$tag.astroto generate
/year/$year.astro; that should live at
/pages/$year.astro` as a separate file).allPosts.filter((post) => post.tag === params.tag)
: we aren’t returning all posts here; we’re only returning posts with a matching tag. What tag, you ask? The routes
array has [{ tag: 'javascript' }, { tag: '…
, and all the routes we need to gather. So we first need to query everything, but only return the .filter()
ed posts at the very end.Other things of note is that we are sorting like before, but we filter by the frontmatter tag
property, and return those at URLs.
These are still paginated, too! But since there are other conditions applied, they live at a different URL.
Lastly, what about /year/*
? Well hopefully you can figure that out from here. It follows the exact same pattern, except using post.date
frontmatter. You’ll grab the year from that date string, and sort probably newest to oldest rather than alphabetical. You’ll also change params.tag
to params.year
(or whatever you name it), but otherwise most everything else should be the same.
$[collection].astro
files might seem like a pain at first, until you remember you can create reusable components! Treat /pages/*.astro
files as your one-off routing & data fetching logic, and treat /components/*.astro
as your reusable markup. If you find yourself duplicating things too much, you can probably use a component instead!/pages/$[collection].astro
naming. If you have an /all-posts/*
route, then use /pages/$all-posts.astro
to manage that. Don’t try and trick permalink
to generate too many URL trees; it’ll only result in pages being missed when it comes time to build.Astro.fetchContent()
fetch()
to make it happen!Astro can be configured to use Tailwind easily! Install the dependencies:
npm install --save-dev tailwindcss
And also create a tailwind.config.js
in your project root:
// tailwind.config.js
module.exports = {
mode: 'jit',
purge: ['./public/**/*.html', './astro/**/*.{astro,js,jsx,ts,tsx,vue}'],
// more options here
};
Then add Tailwind utilities to any Astro component that needs it:
<style>
@tailwind base;
@tailwind components;
@tailwind utilities;
</style>
You should see Tailwind styles compile successfully in Astro.
💁 Tip: to reduce duplication, try loading @tailwind base
from a parent page (./pages/*.astro
) instead of the component itself.
Add a build
npm script to your /package.json
file:
{
"scripts": {
"dev": "astro dev .",
"build": "astro build ."
}
}
Then run:
npm run build
Now upload the contents of /_site_
to your favorite static site host.
Astro
globalThe Astro
global is available in all contexts in .astro
files. It has the following functions:
config
Astro.config
returns an object with the following properties:
Name | Type | Description |
---|---|---|
site | string | Your website’s public root domain. Set it with site: "https://mysite.com" in your Astro config |
fetchContent()
Astro.fetchContent()
is a way to load local *.md
files into your static site setup. You can either use this on its own, or within Astro Collections.
// ./astro/components/my-component.astro
---
const data = Astro.fetchContent('../pages/post/*.md'); // returns an array of posts that live at ./astro/pages/post/*.md
---
<div>
{data.slice(0, 3).map((post) => (
<article>
<h1>{post.title}</h1>
<p>{post.description}</p>
<a href={post.url}>Read more</a>
</article>
))}
</div>
.fetchContent()
only takes one parameter: a relative URL glob of which local files you’d like to import. Currently only *.md
files are supported. It’s synchronous, and returns an array of items of type:
{
url: string; // the URL of this item (if it’s in pages/)
content: string; // the HTML of this item
// frontmatter data expanded here
}[];
FAQs
Astro is a modern site builder with web best practices, performance, and DX front-of-mind.
The npm package astro receives a total of 253,906 weekly downloads. As such, astro popularity was classified as popular.
We found that astro demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
Security News
React's CRA deprecation announcement sparked community criticism over framework recommendations, leading to quick updates acknowledging build tools like Vite as valid alternatives.
Security News
Ransomware payment rates hit an all-time low in 2024 as law enforcement crackdowns, stronger defenses, and shifting policies make attacks riskier and less profitable.