**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!).
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.
Frontend 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/Preact**
- [ ]**Redux: Partial Support** (Note: You can access a Redux store directly, but full `react-redux` support requires the ability to set a custom `<Provider>` wrapper to every component island. Planned.)
- [ ]**Vuex: Partial Support** (Note: You can access a vuex store directly, but full `vuex` support requires the ability to set a custom `vue.use(store)` call to every component island. Planned.)
¹ _`.astro` files have no runtime, therefore Scoped CSS takes the place of CSS Modules (styles are still scoped to components, but don’t need dynamic values)_
#### 🖍 Styling by Framework
##### Astro
Styling in an Astro component is done by adding a `<style>` tag anywhere. By default, all styles are **scoped**, meaning they only apply to the current component. To create global styles, add a `:global()` wrapper around a selector (the same as if you were using [CSS Modules][css-modules]).
-`<style>` tags within `.astro` files will be extracted and optimized for you on build. So you can write CSS without worrying too much about delivery.
- For best result, only have one `<style>` tag per-Astro component. This isn’t necessarily a limitation, but it may result in better optimization at buildtime.
- If you want to import third-party libraries into an Astro component, you can use [Sass][sass]! In particular, [@use][sass-use] may come in handy (e.g. `@use "bootstrap/scss/bootstrap"`);
`.jsx` files support both global CSS and CSS Modules. To enable the latter, use the `.module.css` extension (or `.module.scss`/`.module.sass` if using Sass).
We also automatically add browser prefixes using [Autoprefixer][autoprefixer]. By default, Astro loads the default values, but you may also specify your own by placing a [Browserslist][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.
// 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.
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.
- The paginated blog posts live at `/posts/1` for page 1, `/posts/2` for page 2, etc.
- We also want to add `/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):
```html
// /astro/pages/$posts.astro
---
import Pagination from '../components/Pagination.astro';
import PostPreview from '../components/PostPreview.astro';
-`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.
```diff
// /astro/pages/$tag.astro
---
import Pagination from '../components/Pagination.astro';
import PostPreview from '../components/PostPreview.astro';
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)
-`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.astro`to 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.
- Having to load different collections in different `$[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!
- Stay true to `/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.
| `site` | `string` | Your website’s public root domain. Set it with `site: "https://mysite.com"` in your [Astro config][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][collections].
`.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/)