diff --git a/astro.sidebar.ts b/astro.sidebar.ts index ed4990f336ba0..f7f5230bc2953 100644 --- a/astro.sidebar.ts +++ b/astro.sidebar.ts @@ -90,6 +90,7 @@ export const sidebar = [ group('guides.upgrade.major', { collapsed: true, items: [ + 'guides/upgrade-to/v6', 'guides/upgrade-to/v5', 'guides/upgrade-to/v4', 'guides/upgrade-to/v3', @@ -119,12 +120,14 @@ export const sidebar = [ items: [ 'reference/api-reference', 'reference/modules/astro-actions', + 'reference/modules/astro-app', 'reference/modules/astro-assets', 'reference/modules/astro-config', 'reference/modules/astro-content', 'reference/modules/astro-env', 'reference/modules/astro-i18n', 'reference/modules/astro-middleware', + 'reference/modules/astro-static-paths', 'reference/modules/astro-transitions', 'reference/modules/astro-zod', ], @@ -136,6 +139,8 @@ export const sidebar = [ 'reference/content-loader-reference', 'reference/image-service-reference', 'reference/dev-toolbar-app-reference', + 'reference/session-driver-reference', + 'reference/font-provider-reference', 'reference/container-reference', 'reference/programmatic-reference', ], @@ -143,17 +148,13 @@ export const sidebar = [ group('reference.experimental', { items: [ 'reference/experimental-flags', - 'reference/experimental-flags/csp', - 'reference/experimental-flags/fonts', - 'reference/experimental-flags/live-content-collections', + 'reference/experimental-flags/route-caching', 'reference/experimental-flags/client-prerender', 'reference/experimental-flags/content-intellisense', - 'reference/experimental-flags/preserve-scripts-order', - 'reference/experimental-flags/heading-id-compat', - 'reference/experimental-flags/static-import-meta-env', 'reference/experimental-flags/chrome-devtools-workspace', - 'reference/experimental-flags/fail-on-prerender-conflict', 'reference/experimental-flags/svg-optimization', + 'reference/experimental-flags/queued-rendering', + 'reference/experimental-flags/rust-compiler', ], }), 'reference/legacy-flags', diff --git a/public/_headers b/public/_headers index b505fec8b57ad..9859e1d65fa4c 100644 --- a/public/_headers +++ b/public/_headers @@ -5,3 +5,7 @@ cache-control: max-age=31536000 cache-control: immutable cache-control: public + +# TODO: Remove this before merging to main +/* + x-robots-tag: no-index diff --git a/src/content/docs/de/basics/project-structure.mdx b/src/content/docs/de/basics/project-structure.mdx index 5027ff897bb6e..83a6698c66032 100644 --- a/src/content/docs/de/basics/project-structure.mdx +++ b/src/content/docs/de/basics/project-structure.mdx @@ -125,7 +125,7 @@ Eine Anleitung zur Erstellung einer neuen `package.json`-Datei für dein Projekt Diese Datei wird von jeder Starter-Vorlage generiert und enthält Konfigurations­optionen für dein Astro-Projekt. Hier kannst du die zu verwendenden Integrationen, Build-Optionen, Serveroptionen und mehr angeben. -Astro unterstützt mehrere Dateiformate für seine JavaScript-Konfigurationsdatei: `astro.config.js`, `astro.config.mjs`, `astro.config.cjs` und `astro.config.ts`. Wir empfehlen, in den meisten Fällen `.mjs` oder `.ts` zu verwenden, wenn du TypeScript in deiner Konfigurationsdatei schreiben willst. +Astro unterstützt mehrere Dateiformate für seine JavaScript-Konfigurationsdatei: `astro.config.js`, `astro.config.mjs` und `astro.config.ts`. Wir empfehlen, in den meisten Fällen `.mjs` oder `.ts` zu verwenden, wenn du TypeScript in deiner Konfigurationsdatei schreiben willst. Das Laden von TypeScript-Konfigurationsdateien wird mit [`tsm`](https://github.com/lukeed/tsm) gehandhabt und berücksichtigt die `tsconfig`-Optionen deines Projekts. diff --git a/src/content/docs/de/tutorial/6-islands/4.mdx b/src/content/docs/de/tutorial/6-islands/4.mdx index 544c050e7d5bf..7c60d35154716 100644 --- a/src/content/docs/de/tutorial/6-islands/4.mdx +++ b/src/content/docs/de/tutorial/6-islands/4.mdx @@ -120,7 +120,6 @@ Aktualisiere Astro und alle Integrationen auf die neuesten Versionen, indem du i ```ts title="src/content.config.ts" import { glob } from "astro/loaders"; import { defineCollection } from "astro:content"; - // Importiere Zod import { z } from "astro/zod"; const blog = defineCollection({ @@ -150,7 +149,7 @@ Aktualisiere Astro und alle Integrationen auf die neuesten Versionen, indem du i 1. Erstelle eine neue Seiten­datei `src/pages/posts/[...slug].astro`. Markdown- und MDX-Dateien werden innerhalb einer Sammlung nicht mehr automatisch zu Seiten. Du musst daher eine Seite erstellen, die für die Ausgabe jedes einzelnen Blogbeitrags verantwortlich ist. -2. Füge den folgenden Code ein, um deine Sammlung [abzufragen](/de/guides/content-collections/#querying-collections) und für jede generierte Seite die `slug` und den Seiteninhalt bereitzustellen: +2. Füge den folgenden Code ein, um deine Sammlung [abzufragen](/de/guides/content-collections/#querying-build-time-collections) und für jede generierte Seite die `slug` und den Seiteninhalt bereitzustellen: ```astro title="src/pages/posts/[...slug].astro" --- diff --git a/src/content/docs/en/astro-courses.mdx b/src/content/docs/en/astro-courses.mdx index 8f48e35c5c69a..d2a698de4a613 100644 --- a/src/content/docs/en/astro-courses.mdx +++ b/src/content/docs/en/astro-courses.mdx @@ -48,10 +48,7 @@ Learn from your fellow astronauts with curated collections of guides, articles, - - - diff --git a/src/content/docs/en/basics/project-structure.mdx b/src/content/docs/en/basics/project-structure.mdx index 8b2a0a00bcc4b..0963238cce2cd 100644 --- a/src/content/docs/en/basics/project-structure.mdx +++ b/src/content/docs/en/basics/project-structure.mdx @@ -124,7 +124,7 @@ For help creating a new `package.json` file for your project, check out the [man This file is generated in every starter template and includes configuration options for your Astro project. Here you can specify integrations to use, build options, server options, and more. -Astro supports several file formats for its JavaScript configuration file: `astro.config.js`, `astro.config.mjs`, `astro.config.cjs` and `astro.config.ts`. We recommend using `.mjs` in most cases or `.ts` if you want to write TypeScript in your config file. +Astro supports several file formats for its JavaScript configuration file: `astro.config.js`, `astro.config.mjs` and `astro.config.ts`. We recommend using `.mjs` in most cases or `.ts` if you want to write TypeScript in your config file. TypeScript config file loading is handled using [`tsm`](https://github.com/lukeed/tsm) and will respect your project's `tsconfig` options. diff --git a/src/content/docs/en/getting-started.mdx b/src/content/docs/en/getting-started.mdx index ec9c01835d6d2..9b70bb453fd03 100644 --- a/src/content/docs/en/getting-started.mdx +++ b/src/content/docs/en/getting-started.mdx @@ -5,6 +5,9 @@ i18nReady: true tableOfContents: false editUrl: false next: false +banner: + content: | + Astro v6 is here! Learn how to upgrade your site hero: title: Astro Docs tagline: Guides, resources, and API references to help you build with Astro. diff --git a/src/content/docs/en/guides/actions.mdx b/src/content/docs/en/guides/actions.mdx index cfe4e8622fdab..1b930e30e2cf1 100644 --- a/src/content/docs/en/guides/actions.mdx +++ b/src/content/docs/en/guides/actions.mdx @@ -329,14 +329,14 @@ export const server = { // Matches when the `type` field has the value `create` type: z.literal('create'), name: z.string(), - email: z.string().email(), + email: z.email(), }), z.object({ // Matches when the `type` field has the value `update` type: z.literal('update'), id: z.number(), name: z.string(), - email: z.string().email(), + email: z.email(), }), ]), async handler(input) { @@ -374,7 +374,7 @@ The following example shows a validated newsletter registration form that accept ``` -2. Define a `newsletter` action to handle the submitted form. Validate the `email` field using the `z.string().email()` validator, and the `terms` checkbox using `z.boolean()`: +2. Define a `newsletter` action to handle the submitted form. Validate the `email` field using the `z.email()` validator, and the `terms` checkbox using `z.boolean()`: ```ts title="src/actions/index.ts" ins={5-12} import { defineAction } from 'astro:actions'; @@ -384,7 +384,7 @@ The following example shows a validated newsletter registration form that accept newsletter: defineAction({ accept: 'form', input: z.object({ - email: z.string().email(), + email: z.email(), terms: z.boolean(), }), handler: async ({ email, terms }) => { /* ... */ }, diff --git a/src/content/docs/en/guides/cms/builderio.mdx b/src/content/docs/en/guides/cms/builderio.mdx index f105c872d9275..fe1c8fe317e2f 100644 --- a/src/content/docs/en/guides/cms/builderio.mdx +++ b/src/content/docs/en/guides/cms/builderio.mdx @@ -174,7 +174,7 @@ Things can sometimes go wrong when setting up the preview. If something's not ri * Make sure the site is live - for example, your dev server is running. * Make sure that the URLs match exactly - the one in your Astro project and the one set in the Builder app. * Make sure it's the full URL including the protocol, for example `https://`. -* If you're working in a virtual environment like [IDX](https://idx.dev), [StackBlitz](https://stackblitz.com/), or [Gitpod](https://www.gitpod.io/), you might have to copy and paste the URL again when you restart your workspace, since this usually generates a new URL for your project. +* If you're working in a virtual environment like [Firebase Studio](https://studio.firebase.google.com/) or [StackBlitz](https://stackblitz.com/), you might have to copy and paste the URL again when you restart your workspace, since this usually generates a new URL for your project. For more ideas, read [Builder's troubleshooting guide](https://www.builder.io/c/docs/guides/preview-url-working). ::: diff --git a/src/content/docs/en/guides/cms/cloudcannon.mdx b/src/content/docs/en/guides/cms/cloudcannon.mdx index 26bb1ba162f69..4ee2a2ec75187 100644 --- a/src/content/docs/en/guides/cms/cloudcannon.mdx +++ b/src/content/docs/en/guides/cms/cloudcannon.mdx @@ -144,7 +144,7 @@ The following example will create a new blog post from the CloudCannon Site Dash ## Rendering CloudCannon content -Use Astro's Content Collections API to [query and display your posts and collections](/en/guides/content-collections/#querying-collections), just as you would in any Astro project. +Use Astro's Content Collections API to [query and display your posts and collections](/en/guides/content-collections/#querying-build-time-collections), just as you would in any Astro project. ### Displaying a collection list @@ -159,7 +159,7 @@ const posts = await getCollection('blog'); diff --git a/src/content/docs/en/guides/cms/frontmatter-cms.mdx b/src/content/docs/en/guides/cms/frontmatter-cms.mdx index 825baa7eb22a6..af3a5c32bafc8 100644 --- a/src/content/docs/en/guides/cms/frontmatter-cms.mdx +++ b/src/content/docs/en/guides/cms/frontmatter-cms.mdx @@ -9,7 +9,7 @@ i18nReady: true --- import { FileTree } from '@astrojs/starlight/components'; -[Front Matter CMS](https://frontmatter.codes/) brings the CMS to your editor, it runs within Visual Studio Code, Gitpod, and many more. +[Front Matter CMS](https://frontmatter.codes/) brings the CMS to your editor, allowing you to create and preview content in real-time in Visual Studio Code. ## Integration with Astro diff --git a/src/content/docs/en/guides/cms/keystatic.mdx b/src/content/docs/en/guides/cms/keystatic.mdx index d8fdc1e12276a..c8fe130303978 100644 --- a/src/content/docs/en/guides/cms/keystatic.mdx +++ b/src/content/docs/en/guides/cms/keystatic.mdx @@ -183,7 +183,7 @@ Visit `http://127.0.0.1:4321/keystatic` in the browser to see the Keystatic Admi ## Rendering Keystatic content -Use Astro's Content Collections API to [query and display your posts and collections](/en/guides/content-collections/#querying-collections), just as you would in any Astro project. +[Query and display your posts and collections](/en/guides/content-collections/#querying-build-time-collections), just as you would in any Astro project. ### Displaying a collection list @@ -198,7 +198,7 @@ const posts = await getCollection('posts') diff --git a/src/content/docs/en/guides/configuring-astro.mdx b/src/content/docs/en/guides/configuring-astro.mdx index ef4abace880a8..0e2f3b91d18f8 100644 --- a/src/content/docs/en/guides/configuring-astro.mdx +++ b/src/content/docs/en/guides/configuring-astro.mdx @@ -26,7 +26,7 @@ export default defineConfig({ It is only required if you have something to configure, but most projects will use this file. The `defineConfig()` helper provides automatic IntelliSense in your IDE and is where you will add all your configuration options to tell Astro how to build and render your project to HTML. -We recommend using the default file format `.mjs` in most cases, or `.ts` if you want to write TypeScript in your config file. However, `astro.config.js` and `astro.config.cjs` are also supported. +We recommend using the default file format `.mjs` in most cases, or `.ts` if you want to write TypeScript in your config file. However, `astro.config.js` is also supported. Read Astro's [configuration reference](/en/reference/configuration-reference/) for a full overview of all supported configuration options. diff --git a/src/content/docs/en/guides/content-collections.mdx b/src/content/docs/en/guides/content-collections.mdx index 2d13a4875668e..f93e02a017aab 100644 --- a/src/content/docs/en/guides/content-collections.mdx +++ b/src/content/docs/en/guides/content-collections.mdx @@ -3,6 +3,9 @@ title: Content collections description: >- Manage your content with type safety. i18nReady: true +tableOfContents: + minHeadingLevel: 2 + maxHeadingLevel: 3 --- import { FileTree, CardGrid, LinkCard, Steps } from '@astrojs/starlight/components'; import Since from '~/components/Since.astro' @@ -12,18 +15,13 @@ import ReadMore from "~/components/ReadMore.astro"

-**Content collections** are the best way to manage sets of content in any Astro project. Collections help to organize and query your documents, enable Intellisense and type checking in your editor, and provide automatic TypeScript type-safety for all of your content. -Astro v5.0 introduced the Content Layer API for defining and querying content collections. This performant, scalable API provides built-in content loaders for your local collections. For remote content, you can use third-party and community-built loaders or create your own custom loader and pull in your data from any source. +**Content collections** are the best way to manage sets of content in any Astro project: blog posts, product descriptions, character profiles, recipes, or any structured content. Collections help to organize and query your documents, enable Intellisense and type checking in your editor, and provide automatic TypeScript type-safety for all of your content. -:::note -Projects may continue using the legacy Content Collections API introduced in Astro v2.0. However, we encourage you to [update any existing collections](/en/guides/upgrade-to/v5/#legacy-v20-content-collections-api) when you are able. -::: +Astro provides performant, scalable APIs to load, query, and render content from anywhere: stored locally in your project, hosted remotely, or fetched live from frequently-updating sources. ## What are Content Collections? -You can define a **collection** from a set of data that is structurally similar. This can be a directory of blog posts, a JSON file of product items, or any data that represents multiple items of the same shape. - -Collections stored locally in your project or on your filesystem can have entries of Markdown, MDX, Markdoc, YAML, TOML, or JSON files: +A content collection is a set of related, structurally identical data. This data can be stored in one or several files locally (e.g. a folder of individual Markdown files of blog posts, a single JSON file of product descriptions) or fetched from remote sources such as a database, CMS, or API endpoint. Each member of the collection is called an entry. - src/ @@ -35,32 +33,103 @@ Collections stored locally in your project or on your filesystem can have entrie - authors.json a single file containing all collection entries -With an appropriate collection loader, you can fetch remote data from any external source, such as a CMS, database, or headless payment system. +Collections are defined by the location and shape of its entries and provide a convenient way to query and render your content and associated metadata. You can create a collection any time you have a group of related data or content, stored in the same location, that shares a common structure. + +[Two types of content collections](#types-of-collections) are available to allow you to work with data fetched either at build time or at request time. Both build-time collections and live updating collections use: + +- A required `loader` to retrieve your content and metadata from wherever it is stored and make it available to your project through content-focused APIs. +- An optional collection `schema` that allows you to define the expected shape of each entry for type safety, autocomplete, and validation in your editor. + +Collections stored locally in your project or on your filesystem can use one of Astro's [provided build-time loaders](#build-time-collection-loaders) to fetch data from Markdown, MDX, Markdoc, YAML, TOML, or JSON files. Point Astro to the location of your content, define your data shape, and you're good to go with a blog or similarly content-heavy, mostly static site in no time! + +With [community-built loaders](https://astro.build/integrations/?search=&categories%5B%5D=loaders) or by building a [custom build-time collection loader](#custom-build-time-loaders) or [live loader](#creating-a-live-loader) yourself, you can fetch remote data from any external source, such as a CMS, database, or headless payment system, either at build time or live on demand. + +### Types of collections + +[Build-time content collections](#defining-build-time-content-collections) are updated at build time, and data is saved to a storage layer. This provides excellent performance for most content, but may not be suitable for frequently updating data sources requiring up-to-the-moment data freshness, such as live stock prices. + +For the best performance and scalability, use build-time content collections when one or more of these is true: + +- **Performance is critical** and you want to prerender data at build time. +- **Your data is relatively static** (e.g., blog posts, documentation, product descriptions). +- **You want to benefit from build-time optimization** and caching. +- **You need to process MDX** or **perform image optimization**. +- **Your data can be fetched once and reused** across multiple builds. + +:::tip[Quick start] +See [the official Astro blog starter template](https://github.com/withastro/astro/tree/latest/examples/blog) to get up and running quickly with an example of using the [built-in `glob()` loader](#the-glob-loader) and [defining a schema](#defining-the-collection-schema) for a collection of local Markdown or MDX blog posts. +::: + +[Live content collections](#live-content-collections) fetch their data at runtime rather than build time. This allows you to access frequently updated data from CMSs, APIs, databases, or other sources using a unified API, without needing to rebuild your site when the data changes. However, this can come at a performance cost since data is fetched at each request and returned directly with no data store persistence. + +Live content collections are designed for data that changes frequently and needs to be up-to-date when a page is requested. Consider using them when one or more of these is true: + +- **You need real-time information** (e.g. user-specific data, current stock levels). +- **You want to avoid constant rebuilds** for content that changes often. +- **Your data updates frequently** (e.g. up-to-the-minute product inventory, prices, availability). +- **You need to pass dynamic filters** to your data source based on user input or request parameters. +- **You're building preview functionality** for a CMS where editors need to see draft content immediately. + +Both kinds of collections can exist in the same project, so you can always choose the best type of collection for each individual data source. For example, a build-time collection can manage product descriptions, while a live collection can manage content inventory. + +Both types of collections use similar APIs (e.g. `getCollection()` and `getLiveCollection()`), so that working with collections will feel familiar no matter which one you choose, while still ensuring that you always know which type of collection you are working with. + +We suggest using build-time content collections whenever possible, and using live collections when your content needs updating in real time and the performance tradeoffs are acceptable. Additionally, live content collections have some limitations compared to build-time collections: + +- **No MDX support**: MDX cannot be rendered at runtime +- **No image optimization**: Images cannot be processed at runtime +- **Performance considerations**: Data is fetched on each request (unless cached) +- **No data store persistence**: Data is not saved to the content layer data store + +### When to create a collection + +Define your data as a collection when: + +- You have multiple files or data to organize that share the same overall structure (e.g. a directory of blog posts written in Markdown which all have the same frontmatter properties). +- You have existing content stored remotely, such as in a CMS, and want to take advantage of the collections helper functions instead of using `fetch()` or SDKs. +- You need to fetch (tens of) thousands of related pieces of data at build time, and need a querying and caching method that handles at scale. + +Much of the benefit of using collections comes from: + +- Defining a common data shape to validate that an individual entry is "correct" or "complete", avoiding errors in production. +- Content-focused APIs designed to make querying intuitive (e.g. `getCollection()` instead of `import.meta.glob()`) when importing and rendering content on your pages. +- Access to both built-in loaders and access to the low-level [Content Loader API](/en/reference/content-loader-reference/) for retrieving your content. There are additionally several third-party and community-built loaders available, and you can build your own custom loader to fetch data from anywhere. +- Performance and scalability. Build-time content collections data can be cached between builds and is suitable for tens of thousands of content entries. + +### When not to create a collection + +Collections provide excellent structure, safety, and organization when you have multiple pieces of content that must share the same properties. + +Collections may not be your solution if: + +- You have only one or a small number of different content pages. Consider [making individual page components](/en/basics/astro-pages/) such as `src/pages/about.astro` with your content directly instead. +- You are displaying files that are not processed by Astro, such as PDFs. Place these static assets in the [`public/` directory](/en/basics/project-structure/#public) of your project instead. +- Your data source has its own SDK/client library for imports that is incompatible with or does not offer a content loader, and you prefer to use it directly. ## TypeScript configuration for collections -Content collections rely on TypeScript to provide Zod validation, Intellisense and type checking in your editor. If you are not extending one of Astro's `strict` or `strictest` TypeScript settings, you will need to ensure the following `compilerOptions` are set in your `tsconfig.json`: +Content collections rely on TypeScript to provide Zod validation, Intellisense, and type checking in your editor. By default, Astro configures a [`strict` TypeScript template](/en/guides/typescript/#tsconfig-templates) when you create a new project using the `create astro` CLI command. Both of Astro's `strict` and `strictest` templates include the TypeScript settings your project needs for content collections. + +If you changed this setting to `base` because you are not writing TypeScript in your project, or are not using any of Astro's built-in templates, you will need to also add the following `compilerOptions` in your `tsconfig.json` to use content collections: -```json title="tsconfig.json" ins={5} {6} +```json title="tsconfig.json" ins={4-7} { - // Included with "astro/tsconfigs/strict" or "astro/tsconfigs/strictest" "extends": "astro/tsconfigs/base", + // not needed for `strict` or `strictest` "compilerOptions": { - "strictNullChecks": true, // add if using `base` template - "allowJs": true // required, and included with all Astro templates + "strictNullChecks": true, + "allowJs": true } } ``` -## Defining Collections +## Defining build-time content collections -Individual collections use `defineCollection()` to configure: -- a `loader` for a data source (required) -- a `schema` for type safety (optional, but highly recommended!) +All of your build-time content collections are defined in a special `src/content.config.ts` file (`.js` and `.mjs` extensions are also supported) using `defineCollection()`, and then a single collections object is exported for use in your project. -### The collection config file - -To define collections, you must create a `src/content.config.ts` file in your project (`.js` and `.mjs` extensions are also supported.) This is a special file that Astro will use to configure your content collections based on the following structure: +Each individual collection configures: +- [a build-time `loader`](#build-time-collection-loaders) for a data source (required) +- [a build-time `schema`](#defining-the-collection-schema) for type safety (optional, but highly recommended!) ```ts title="src/content.config.ts" // 1. Import utilities from `astro:content` @@ -72,87 +141,151 @@ import { glob, file } from 'astro/loaders'; // 3. Import Zod import { z } from 'astro/zod'; -// 4. Define your collection(s) -const blog = defineCollection({ /* ... */ }); -const dogs = defineCollection({ /* ... */ }); +// 4. Define a `loader` and `schema` for each collection +const blog = defineCollection({ + loader: glob({ base: './src/content/blog', pattern: '**/*.{md,mdx}' }), + schema: z.object({ + title: z.string(), + description: z.string(), + pubDate: z.coerce.date(), + updatedDate: z.coerce.date().optional(), + }), +}); // 5. Export a single `collections` object to register your collection(s) -export const collections = { blog, dogs }; +export const collections = { blog }; ``` -### Defining the collection `loader` +You can then use the dedicated `getCollection()` and `getEntry()` functions to [query your content collections data](#querying-build-time-collections) and render your content. -The Content Layer API allows you to fetch your content (whether stored locally in your project or remotely) and uses a `loader` property to retrieve your data. +You can choose to [generate page routes](#generating-routes-from-content) from your build-time collection entries at build time for an entirely static, prerendered site. Or, you can render your build-time collections on demand, choosing to delay building your page until it is first requested. This is useful when you have a large number of pages (e.g. thousands or tens of thousands) and want to delay building a static page until it is needed. -#### Built-in loaders +## Build-time collection loaders -Astro provides [two built-in loader functions](/en/reference/content-loader-reference/#built-in-loaders) (`glob()` and `file()`) for fetching your local content, as well as access to the API to construct your own loader and fetch remote data. +Astro provides two built-in loaders (`glob()` and `file()`) for fetching your local content at build time. Pass the location of your data in your project or on your filesystem, and these loaders will automatically handle your data and update the persistent data store content layer. -The [`glob()` loader](/en/reference/content-loader-reference/#glob-loader) creates entries from directories of Markdown, MDX, Markdoc, JSON, YAML, or TOML files from anywhere on the filesystem. It accepts a `pattern` of entry files to match using glob patterns supported by [micromatch](https://github.com/micromatch/micromatch#matching-features), and a base file path of where your files are located. Each entry's `id` will be automatically generated from its file name. Use this loader when you have one file per entry. +To fetch remote data at build time, you can [build a custom loader](#custom-build-time-loaders) to retrieve your data and update the data store. Or, you can use any [third-party or community-published loader integration](https://astro.build/integrations/2/?search=&categories%5B%5D=loaders). Several already exist for popular content management systems as well as common data sources such as Obsidian vaults, GitHub repositories, or Bluesky posts. -The [`file()` loader](/en/reference/content-loader-reference/#file-loader) creates multiple entries from a single local file. Each entry in the file must have a unique `id` key property. It accepts a `base` file path to your file and optionally a [`parser` function](#parser-function) for data files it cannot parse automatically, or to parse data asynchronously. Use this loader when your data file can be parsed as an array of objects. +### The `glob()` loader -```ts title="src/content.config.ts" {6,10} +The [`glob()` loader](/en/reference/content-loader-reference/#glob-loader) fetches entries from directories of Markdown, MDX, Markdoc, JSON, YAML, or TOML files from anywhere on the filesystem. If you store your content entries locally as separate files, such as a directory of blog posts, then the `glob()` loader is all you need to access your content. + +This loader requires a `pattern` of entry files to match using glob patterns supported by [micromatch](https://github.com/micromatch/micromatch#matching-features), and a `base` file path of where your files are located. A unique `id` for each entry will be automatically generated from its file name, but you can [define custom IDs](#defining-custom-ids) if needed. + +```ts title="src/content.config.ts" {5} import { defineCollection } from 'astro:content'; -import { glob, file } from 'astro/loaders'; // Not available with legacy API -import { z } from 'astro/zod'; +import { glob } from 'astro/loaders'; const blog = defineCollection({ loader: glob({ pattern: "**/*.md", base: "./src/data/blog" }), - schema: /* ... */ }); + +export const collections = { blog }; +``` + +#### Defining custom IDs + +When using the [`glob()` loader](#the-glob-loader) with Markdown, MDX, Markdoc, JSON, or TOML files, every content entry [`id`](/en/reference/modules/astro-content/#collectionentryid) is automatically generated in an URL-friendly format based on the content filename. This unique `id` is used to query the entry directly from your collection. It is also useful when [creating new pages and URLs from your content](#generating-routes-from-content). + +You can override a single entry’s generated `id` by adding your own `slug` property to the file frontmatter or data object for JSON files. This is similar to the “permalink” feature of other web frameworks. + +```md title="src/blog/1.md" {3} +--- +title: My Blog Post +slug: my-custom-id/supports/slashes +--- +Your blog post content here. +``` + +```json title="src/categories/1.json" {3} +{ + "title": "My Category", + "slug": "my-custom-id/supports/slashes", + "description": "Your category description here." +} +``` + +You can also pass options to the `glob()` loader's [`generateID()` helper function](/en/reference/content-loader-reference/#generateid) when you define your build-time collection to adjust how `id`s are generated. For example, you may wish to revert the default behavior of converting uppercase letters to lowercase for each collection entry: + +```js title="src/content.config.ts" +const authors = defineCollection({ + /* Retrieve all JSON files in your authors directory while retaining + * uppercase letters in the ID. */ + loader: glob({ + pattern: '**/*.json', + base: "./src/data/authors", + generateId: ({ entry }) => entry.replace(/\.json$/, ''), + }), +}); +``` + +### The `file()` loader + +The [`file()` loader](/en/reference/content-loader-reference/#file-loader) fetches multiple entries from a single local file defined in your collection. The `file()` loader will automatically detect and parse (based on the file extension) a single array of objects from JSON and YAML files, and will treat each top-level table as an independent entry in TOML files. + +```ts title="src/content.config.ts" {5} +import { defineCollection } from 'astro:content'; +import { file } from 'astro/loaders'; + const dogs = defineCollection({ loader: file("src/data/dogs.json"), - schema: /* ... */ }); -const probes = defineCollection({ - // `loader` can accept an array of multiple patterns as well as string patterns - // Load all markdown files in the space-probes directory, except for those that start with "voyager-" - loader: glob({ pattern: ['*.md', '!voyager-*'], base: 'src/data/space-probes' }), - schema: z.object({ - name: z.string(), - type: z.enum(['Space Probe', 'Mars Rover', 'Comet Lander']), - launch_date: z.date(), - status: z.enum(['Active', 'Inactive', 'Decommissioned']), - destination: z.string(), - operator: z.string(), - notable_discoveries: z.array(z.string()), - }), -}); +export const collections = { dogs }; +``` + +Each entry object in the file must have a unique `id` key property so that the entry can be identified and queried. Unlike the `glob()` loader, the `file()` loader will not automatically generate IDs for each entry. -export const collections = { blog, dogs, probes }; +You can provide your entries as an array of objects with an `id` property, or in object form where the unique `id` is the key: + +```json title="src/data/dogs.json" +// Specify an `id` property in each object of an array +[ + { "id": "poodle", "coat": "curly", "shedding": "low" }, + { "id": "afghan", "coat": "short", "shedding": "low" } +] ``` -##### `parser` function +```json title="src/data/dogs.json" +// Each key will be used as the `id` +{ + "poodle": { "coat": "curly", "shedding": "low" }, + "afghan": { "coat": "silky", "shedding": "low" } +} +``` -The `file()` loader accepts a second argument that defines a `parser` function. This allows you to specify a custom parser (e.g. `csv-parse`) to create a collection from a file's contents. +#### Parsing other data formats -The `file()` loader will automatically detect and parse (based on their file extension) a single array of objects from JSON and YAML files, and will treat each top-level table as an independent entry in TOML files. Support for these file types is built-in, and there is no need for a `parser` unless you have a [nested JSON document](#nested-json-documents). To use other files, such as `.csv`, you will need to create a parser function. +Support for parsing single JSON, YAML, and TOML files into collection entries withe the `file()` loader is built-in (unless you have a [nested JSON document](#nested-json-documents)). To load your collection from unsupported file types, such as `.csv`, you will need to create a [parser function](/en/reference/content-loader-reference/#parser). This function can be made async if required (e.g. to fetch files from the web, or if your parser is asyncronous). -The following example shows importing a CSV parser, then loading a `cats` collection into your project by passing both a file path and `parser` function to the `file()` loader: +The following example shows importing a third-party CSV parser then passing a custom `parser` function to the `file()` loader: -```typescript title="src/content.config.ts" +```typescript title="src/content.config.ts" {3} "parser: (text) => parseCsv(text, { columns: true, skipEmptyLines: true })" import { defineCollection } from "astro:content"; import { file } from "astro/loaders"; import { parse as parseCsv } from "csv-parse/sync"; const cats = defineCollection({ - loader: file("src/data/cats.csv", { parser: (text) => parseCsv(text, { columns: true, skipEmptyLines: true })}) + loader: file("src/data/cats.csv", { + parser: (text) => parseCsv(text, { columns: true, skipEmptyLines: true }), + }), }); ``` -###### Nested `.json` documents +##### Nested `.json` documents -The `parser` argument also allows you to load a single collection from a nested JSON document. For example, this JSON file contains multiple collections: +The `parser()` argument can be used to load a single collection from a nested JSON document. For example, this JSON file contains multiple collections: ```json title="src/data/pets.json" {"dogs": [{}], "cats": [{}]} ``` -You can separate these collections by passing a custom `parser` to the `file()` loader for each collection: +You can separate these collections by passing a custom `parser()` function to the `file()` loader for each collection, using Astro's built-in JSON parsing: ```typescript title="src/content.config.ts" +import { file } from "astro/loaders"; +import { defineCollection } from "astro:content"; + const dogs = defineCollection({ loader: file("src/data/pets.json", { parser: (text) => JSON.parse(text).dogs }) }); @@ -161,48 +294,35 @@ const cats = defineCollection({ }); ``` -#### Building a custom loader - -You can build a custom loader to fetch remote content from any data source, such as a CMS, a database, or an API endpoint. +### Custom build-time loaders -Using a loader to fetch your data will automatically create a collection from your remote data. This gives you all the benefits of local collections, such as collection-specific API helpers such as `getCollection()` and `render()` to query and display your data, as well as schema validation. +You can [build a custom loader](/en/reference/content-loader-reference/#building-a-loader) using the Content Loader API to fetch remote content from any data source, such as a CMS, a database, or an API endpoint. -:::tip -Find community-built and third-party loaders in the [Astro integrations directory](https://astro.build/integrations/?search=&categories%5B%5D=loaders). -::: - -##### Inline loaders - -You can define a loader inline, inside your collection, as an async function that returns an array of entries. - -This is useful for loaders that don't need to manually control how the data is loaded and stored. Whenever the loader is called, it will clear the store and reload all the entries. +Then you can import and define your custom loader in your collections config, passing any required values: ```ts title="src/content.config.ts" -const countries = defineCollection({ - loader: async () => { - const response = await fetch("https://restcountries.com/v3.1/all"); - const data = await response.json(); - // Must return an array of entries with an id property, or an object with IDs as keys and entries as values - return data.map((country) => ({ - id: country.cca3, - ...country, - })); - }, - schema: /* ... */ +import { defineCollection } from 'astro:content'; +import { myLoader } from './loader.ts'; + +const blog = defineCollection({ + loader: myLoader({ + url: "https://api.example.com/posts", + apiKey: "my-secret", + }), }); ``` -The returned entries are stored in the collection and can be queried using the `getCollection()` and `getEntry()` functions. - -##### Loader objects +:::tip +Find community-built and third-party loaders in the [Astro integrations directory](https://astro.build/integrations/?search=&categories%5B%5D=loaders). +::: -For more control over the loading process, you can use the Content Loader API to create a loader object. For example, with access to the `load` method directly, you can create a loader that allows entries to be updated incrementally or clears the store only when necessary. +Using a custom loader to fetch your data will automatically create a collection from your remote data. This gives you all the benefits of local collections, including collection-specific API helpers such as `getCollection()` and `render()` to [query and display your data](#querying-build-time-collections), as well as schema validation. -Similar to creating an Astro integration or Vite plugin, you can [distribute your loader as an NPM package](/en/reference/publish-to-npm/) that others can use in their projects. +Similar to creating an Astro integration or Vite plugin, you can [distribute your loader as an npm package](/en/reference/publish-to-npm/) that others can use in their projects. -See the full [Content Loader API](/en/reference/content-loader-reference/) and examples of how to build your own loader. +See the full [Content Loader API](/en/reference/content-loader-reference/) for examples of how to build your own loader. -### Defining the collection schema +## Defining the collection schema Schemas enforce consistent frontmatter or entry data within a collection through Zod validation. A schema **guarantees** that this data exists in a predictable form when you need to reference or query it. If any file violates its collection schema, Astro will provide a helpful error to let you know. @@ -212,12 +332,12 @@ Schemas also power Astro's automatic TypeScript typings for your content. When y In order for Astro to recognize a new or updated schema, you may need to restart the dev server or [sync the content layer](/en/reference/cli-reference/#astro-dev) (s + enter) to define the `astro:content` module. ::: -Every frontmatter or data property of your collection entries must be defined using a [Zod data type](/en/reference/modules/astro-zod/#common-data-type-validators): +Providing a `schema` is optional, but highly recommended! If you choose to use a schema, then every frontmatter or data property of your collection entries must be defined using a [Zod data type](/en/reference/modules/astro-zod/#common-data-type-validators): ```ts title="src/content.config.ts" {7-12,16-20} import { defineCollection } from 'astro:content'; -import { glob, file } from 'astro/loaders'; import { z } from 'astro/zod'; +import { glob, file } from 'astro/loaders'; const blog = defineCollection({ loader: glob({ pattern: "**/*.md", base: "./src/data/blog" }), @@ -240,23 +360,23 @@ const dogs = defineCollection({ export const collections = { blog, dogs }; ``` -#### Defining datatypes with Zod +### Defining datatypes with Zod -Astro uses [Zod](https://github.com/colinhacks/zod) to power its content schemas. With Zod, Astro is able to validate every file's data within a collection *and* provide automatic TypeScript types when you go to query content from inside your project. +Astro uses [Zod](https://github.com/colinhacks/zod) to power its content schemas. With Zod, Astro is able to validate every file's data within a collection *and* provide automatic TypeScript types when you query content from inside your project. -To use Zod in Astro, import the `z` utility from `"astro/zod"`. This is a re-export of the Zod library, and it supports all of the features of Zod. +To use Zod in Astro, import the `z` utility from `"astro/zod"`. This is a re-export of the Zod library, and it supports all of the features of Zod 4. See the [`z` utility reference](/en/reference/modules/astro-zod/) for a cheatsheet of common datatypes and to learn how Zod works and what features are available. -##### Zod schema methods +#### Zod schema methods All [Zod schema methods](/en/reference/modules/astro-zod/#using-zod-methods) (e.g. `.parse()`, `.transform()`) are available, with some limitations. Notably, performing custom validation checks on images using `image().refine()` is unsupported. -#### Defining collection references +### Defining collection references Collection entries can also "reference" other related entries. -With the [`reference()` function](/en/reference/modules/astro-content/#reference) from the Collections API, you can define a property in a collection schema as an entry from another collection. For example, you can require that every `space-shuttle` entry includes a `pilot` property which uses the `pilot` collection's own schema for type checking, autocomplete, and validation. +With the [`reference()` function](/en/reference/modules/astro-content/#reference), you can define a property in a collection schema as an entry from another collection. For example, you can require that every `space-shuttle` entry includes a `pilot` property which uses the `pilot` collection's own schema for type checking, autocomplete, and validation. A common example is a blog post that references reusable author profiles stored as JSON, or related post URLs stored in the same collection: @@ -266,21 +386,21 @@ import { glob } from 'astro/loaders'; import { z } from 'astro/zod'; const blog = defineCollection({ - loader: glob({ pattern: '**/[^_]*.md', base: "./src/data/blog" }), + loader: glob({ base: './src/content/blog', pattern: '**/*.{md,mdx}' }), schema: z.object({ title: z.string(), // Reference a single author from the `authors` collection by `id` author: reference('authors'), - // Reference an array of related posts from the `blog` collection by `slug` + // Reference an array of related posts from the `blog` collection by `id` relatedPosts: z.array(reference('blog')), }) }); const authors = defineCollection({ - loader: glob({ pattern: '**/[^_]*.json', base: "./src/data/authors" }), + loader: glob({ pattern: '**/*.json', base: "./src/data/authors" }), schema: z.object({ name: z.string(), - portfolio: z.string().url(), + portfolio: z.url(), }) }); @@ -289,50 +409,29 @@ export const collections = { blog, authors }; This example blog post specifies the `id`s of related posts and the `id` of the post author: -```yaml title="src/data/blog/welcome.md" +```yaml title="src/content/blog/welcome.md" --- title: "Welcome to my blog" author: ben-holmes # references `src/data/authors/ben-holmes.json` relatedPosts: -- about-me # references `src/data/blog/about-me.md` -- my-year-in-review # references `src/data/blog/my-year-in-review.md` +- about-me # references `src/content/blog/about-me.md` +- my-year-in-review # references `src/content/blog/my-year-in-review.md` --- ``` These references will be transformed into objects containing a `collection` key and an `id` key, allowing you to easily [query them in your templates](/en/guides/content-collections/#accessing-referenced-data). -### Defining custom IDs +## Querying build-time collections -When using the `glob()` loader with Markdown, MDX, Markdoc, or JSON files, every content entry [`id`](/en/reference/modules/astro-content/#id) is automatically generated in an URL-friendly format based on the content filename. The `id` is used to query the entry directly from your collection. It is also useful when creating new pages and URLs from your content. - -You can override an entry’s generated `id` by adding your own `slug` property to the file frontmatter or data object for JSON files. This is similar to the “permalink” feature of other web frameworks. - -```md title="src/blog/1.md" {3} ---- -title: My Blog Post -slug: my-custom-id/supports/slashes ---- -Your blog post content here. -``` - -```json title="src/categories/1.json" {3} -{ - "title": "My Category", - "slug": "my-custom-id/supports/slashes", - "description": "Your category description here." -} -``` - -## Querying Collections - -Astro provides helper functions to query a collection and return one (or more) content entries. +Astro provides helper functions to query a build-time collection and return one or more content entries. - [`getCollection()`](/en/reference/modules/astro-content/#getcollection) fetches an entire collection and returns an array of entries. - [`getEntry()`](/en/reference/modules/astro-content/#getentry) fetches a single entry from a collection. These return entries with a unique `id`, a `data` object with all defined properties, and will also return a `body` containing the raw, uncompiled body of a Markdown, MDX, or Markdoc document. -```js +```astro title="src/pages/index.astro" +--- import { getCollection, getEntry } from 'astro:content'; // Get all entries from a collection. @@ -342,8 +441,7 @@ const allBlogPosts = await getCollection('blog'); // Get a single entry from a collection. // Requires the name of the collection and `id` const poodleData = await getEntry('dogs', 'poodle'); - - +--- ``` The sort order of generated collections is non-deterministic and platform-dependent. This means that if you are calling `getCollection()` and need your entries returned in a specific order (e.g. blog posts sorted by date), you must sort the collection entries yourself: @@ -362,7 +460,9 @@ const posts = (await getCollection('blog')).sort( ### Using content in Astro templates -After querying your collections, you can access each entry's content directly inside of your Astro component template. For example, you can create a list of links to your blog posts, displaying information from your entry's frontmatter using the `data` property. +After querying your collections, you can access each entry's content and metadata directly inside of your Astro component template. + +For example, you can create a list of links to your blog posts, displaying information from your entry's frontmatter using the `data` property: ```astro title="src/pages/index.astro" @@ -377,21 +477,20 @@ const posts = await getCollection('blog'); ))} ``` -#### Rendering body content -Once queried, you can render Markdown and MDX entries to HTML using the [`render()`](/en/reference/modules/astro-content/#render) function property. Calling this function gives you access to rendered HTML content, including both a `` component and a list of all rendered headings. +### Rendering body content + +Once queried, you can render Markdown and MDX entries to HTML using the [`render()`](/en/reference/modules/astro-content/#render) function from `astro:content`. Calling this function gives you access to rendered HTML content, including both a `` component and a list of all rendered headings. -```astro title="src/pages/blog/post-1.astro" {5,8} +```astro title="src/pages/blog/post-1.astro" {2,6,10} --- import { getEntry, render } from 'astro:content'; const entry = await getEntry('blog', 'post-1'); -if (!entry) { - // Handle Error, for example: - throw new Error('Could not find blog post 1'); -} -const { Content, headings } = await render(entry); + +const { Content } = await render(entry); --- +

{entry.data.title}

Published on: {entry.data.published.toDateString()}

``` @@ -422,48 +521,60 @@ const { post } = Astro.props; You can use this to filter by any content criteria you like. For example, you can filter by properties like `draft` to prevent any draft blog posts from publishing to your blog: -```js +```astro title="src/pages/blog.astro" +--- // Example: Filter out content entries with `draft: true` import { getCollection } from 'astro:content'; const publishedBlogEntries = await getCollection('blog', ({ data }) => { return data.draft !== true; }); +--- ``` You can also create draft pages that are available when running the dev server, but not built in production: -```js +```astro title="src/pages/blog.astro" +--- // Example: Filter out content entries with `draft: true` only when building for production import { getCollection } from 'astro:content'; const blogEntries = await getCollection('blog', ({ data }) => { return import.meta.env.PROD ? data.draft !== true : true; }); +--- ``` The filter argument also supports filtering by nested directories within a collection. Since the `id` includes the full nested path, you can filter by the start of each `id` to only return items from a specific nested directory: -```js +```astro title="src/pages/blog.astro" +--- // Example: Filter entries by sub-directory in the collection import { getCollection } from 'astro:content'; const englishDocsEntries = await getCollection('docs', ({ id }) => { return id.startsWith('en/'); }); +--- ``` ### Accessing referenced data -Any [references defined in your schema](/en/guides/content-collections/#defining-collection-references) must be queried separately after first querying your collection entry. Since the [`reference()` function](/en/reference/modules/astro-content/#reference) transforms a reference to an object with `collection` and `id` as keys, you can use the `getEntry()` function to return a single referenced item, or `getEntries()` to retrieve multiple referenced entries from the returned `data` object. +To access [references defined in your schema](/en/guides/content-collections/#defining-collection-references), first query your collection entry. Your references will be available on the returned `data` object. (e.g. `entry.data.author` and `entry.data.relatedPosts`) + +Then, you can use the `getEntry()` function again (or `getEntries()` to retrieve multiple referenced entries) by passing those returned values. The `reference()` function in your schema transforms those values into one or more `collection` and `id` objects as a convenient way to query this related data. -```astro title="src/pages/blog/welcome.astro" + +```astro title="src/pages/blog/adventures-in-space.astro" --- import { getEntry, getEntries } from 'astro:content'; -const blogPost = await getEntry('blog', 'welcome'); +// First, query a blog post +const blogPost = await getEntry('blog', 'Adventures in Space'); -// Resolve a singular reference (e.g. `{collection: "authors", id: "ben-holmes"}`) +// Retrieve a single reference item: the blog post's author +// Equivalent to querying `{collection: "authors", id: "ben-holmes"}` const author = await getEntry(blogPost.data.author); -// Resolve an array of references -// (e.g. `[{collection: "blog", id: "about-me"}, {collection: "blog", id: "my-year-in-review"}]`) + +// Retrieve an array of referenced items: all the related posts +// Equivalent to querying `[{collection: "blog", id: "visiting-mars"}, {collection: "blog", id: "leaving-earth-for-the-first-time"}]` const relatedPosts = await getEntries(blogPost.data.relatedPosts); --- @@ -480,17 +591,17 @@ const relatedPosts = await getEntries(blogPost.data.relatedPosts); ## Generating Routes from Content -Content collections are stored outside of the `src/pages/` directory. This means that no pages or routes are generated for your collection items by default. +Content collections are stored outside of the `src/pages/` directory. This means that no pages or routes are generated for your collection items by default by Astro's [file-based routing](/en/guides/routing/). -You will need to manually create a new [dynamic route](/en/guides/routing/#dynamic-routes) if you want to generate HTML pages for each of your collection entries, such as individual blog posts. Your dynamic route will map the incoming request param (e.g. `Astro.params.slug` in `src/pages/blog/[...slug].astro`) to fetch the correct entry for each page. +You will need to manually create a new [dynamic route](/en/guides/routing/#dynamic-routes) if you want to generate HTML pages for each of your collection entries, such as individual blog posts. Your dynamic route will map the incoming request param (e.g. `Astro.params.id` in `src/pages/blog/[...id].astro`) to fetch the correct entry for each page. The exact method for generating routes will depend on whether your pages are prerendered (default) or rendered on demand by a server. ### Building for static output (default) -If you are building a static website (Astro's default behavior), use the [`getStaticPaths()`](/en/reference/routing-reference/#getstaticpaths) function to create multiple pages from a single page component (e.g. `src/pages/[slug]`) during your build. +If you are building a static website (Astro's default behavior) with build-time collections, use the [`getStaticPaths()`](/en/reference/routing-reference/#getstaticpaths) function to create multiple pages from a single page component (e.g. `src/pages/[id].astro`) during your build. -Call `getCollection()` inside of `getStaticPaths()` to have your collection data available for building static routes. Then, create the individual URL paths using the `id` property of each content entry. Each page is passed the entire collection entry as a prop for [use in your page template](#using-content-in-astro-templates). +Call `getCollection()` inside of `getStaticPaths()` to have your collection data available for building static routes. Then, create the individual URL paths using the `id` property of each content entry. Each page receives the entire collection entry as a prop for [use in your page template](#using-content-in-astro-templates). ```astro title="src/pages/posts/[id].astro" "{ id: post.id }" "{ post }" --- @@ -514,28 +625,37 @@ const { Content } = await render(post); This will generate a page route for every entry in the `blog` collection. For example, an entry at `src/blog/hello-world.md` will have an `id` of `hello-world`, and therefore its final URL will be `/posts/hello-world/`. :::note -If your custom slugs contain the `/` character to produce URLs with multiple path segments, you must use a [rest parameter (e.g. `[...slug]`)](/en/guides/routing/#rest-parameters) in the `.astro` filename for this dynamic routing page. +If your custom slugs contain the `/` character to produce URLs with multiple path segments, you must use a [rest parameter (e.g. `[...id]`)](/en/guides/routing/#rest-parameters) in the `.astro` filename for this dynamic routing page. ::: -### Building for server output (SSR) +### Building routes on demand at request time -If you are building a dynamic website (using Astro's SSR support), you are not expected to generate any paths ahead of time during the build. Instead, your page should examine the request (using `Astro.request` or `Astro.params`) to find the `slug` on-demand, and then fetch it using [`getEntry()`](/en/reference/modules/astro-content/#getentry). +With an adapter installed for [on-demand rendering](/en/guides/on-demand-rendering/), you can generate your dynamic page routes at request time. First, examine the request (using `Astro.request` or `Astro.params`) to find the slug on demand, and then fetch it using one of Astro's content collection helper functions: + +- [`getEntry()`](/en/reference/modules/astro-content/#getentry) for build-time collection pages that are generated once, upon first request. +- [`getLiveEntry()`](/en/reference/modules/astro-content/#getentry) for live collection pages where data is (re)fetched at each request time. ```astro title="src/pages/posts/[id].astro" --- +export const prerender = false; // Not needed in 'server' mode + import { getEntry, render } from "astro:content"; + // 1. Get the slug from the incoming server request const { id } = Astro.params; if (id === undefined) { return Astro.redirect("/404"); } + // 2. Query for the entry directly using the request slug const post = await getEntry("blog", id); + // 3. Redirect if the entry does not exist if (post === undefined) { return Astro.redirect("/404"); } + // 4. Render the entry to HTML in the template const { Content } = await render(post); --- @@ -544,10 +664,211 @@ const { Content } = await render(post); ``` :::tip -Explore the `src/pages/` folder of the [blog tutorial demo code on GitHub](https://github.com/withastro/blog-tutorial-demo/tree/content-collections/src/pages) to see full examples of creating pages from your collections for blog features like a list of blog posts, tags pages, and more! +Explore the `src/pages/` folder of the [blog tutorial demo code on GitHub](https://github.com/withastro/blog-tutorial-demo/tree/content-collections/src/pages) to see full examples of creating dynamic pages from your collections for blog features like a list of blog posts, tags pages, and more! +::: + +## Live content collections + +Live collections use a different API than build-time content collections, although the configuration and helper functions are designed to feel familiar. + +Key differences include: + +1. **Execution time**: Run at request time instead of build time +2. **Configuration file**: Use `src/live.config.ts` instead of `src/content.config.ts` +3. **Collection definition**: Use `defineLiveCollection()` instead of `defineCollection()` +4. **Loader API**: Implement `loadCollection` and `loadEntry` methods instead of the `load` method +5. **Data return**: Return data directly instead of storing in the data store +6. **User-facing functions**: Use `getLiveCollection()`/`getLiveEntry()` instead of `getCollection()`/`getEntry()` + +Additionally, you must have an adapter configured for [on-demand rendering](/en/guides/on-demand-rendering/) of live collection data. + +Define your live collections in the special file `src/live.config.ts` (separate from your `src/content.config.ts` for build-time collections, if you have one). + +Each individual collection configures: +- a [live `loader`](#creating-a-live-loader) for your data source, and optionally for type safety (required) +- a [live collection `schema`](#using-zod-schemas-with-live-collections) for type safety (optional) + +Unlike for build-time collections, there are no built-in live loaders available. You will need to [create a custom live loader](#creating-a-live-loader) for your specific data source or find a third-party loader to pass to your live collection's `loader` property. + +You can optionally [include type safety in your live loaders](/en/reference/content-loader-reference/#the-liveloader-object). Therefore, [defining a Zod `schema`](#using-zod-schemas-with-live-collections) for live collections is optional. However, if you provide one, it will take precedence over the live loader's types. + +```ts title="src/live.config.ts" +// Define live collections for accessing real-time data +import { defineLiveCollection } from 'astro:content'; +import { storeLoader } from '@mystore/astro-loader'; + +const products = defineLiveCollection({ + loader: storeLoader({ + apiKey: process.env.STORE_API_KEY, + endpoint: 'https://api.mystore.com/v1', + }), +}); + +// Export a single `collections` object to register your collection(s) +export const collections = { products }; +``` + +You can then use the dedicated `getLiveCollection()` and `getLiveEntry()` functions to [access your live data](#accessing-live-data) and render your content. + +You can [generate page routes](#generating-routes-from-content) from your live collection entries on demand, fetching your data fresh at runtime upon each request without needing a rebuild of your site like [build-time collections](#defining-build-time-content-collections) do. This is useful when accessing live, up-to-the-moment data is more important than having your content available in a performant data storage layer that persists between site builds. + +### Creating a live loader + +You can build a custom [live loader](/en/reference/content-loader-reference/#live-loaders) using the Live Loader API to fetch remote content fresh upon request from any data source, such as a CMS, a database or an API endpoint. You will have to tell your live loader how to fetch and return content entries from your desired data source, as well as provide error handling for unsuccessful data requests. + +Using a live loader to fetch your data will automatically create a collection from your remote data. This gives you all the benefits of Astro's content collections, including collection-specific API helpers such as `getLiveCollection()` and `render()` to [query and display your data](#querying-build-time-collections), as well as helpful error handling. + +:::tip +Find community-built and third-party live loaders in the [Astro integrations directory](https://astro.build/integrations/?search=&categories%5B%5D=loaders). ::: -## Collection JSON Schemas +See the basics of [building a live loader](/en/reference/content-loader-reference/#building-a-live-loader) using the Live Loader API + +### Using Zod schemas with live collections + +You can use Zod schemas with live collections to validate and transform data at runtime. This Zod validation works the same way as [schemas for build-time collections](#defining-the-collection-schema). + +When you define a schema for a live collection, it takes precedence over [the live loader's types](/en/reference/content-loader-reference/#the-liveloader-object) when you query the collection: + +```ts title="src/live.config.ts" +import { defineLiveCollection } from 'astro:content'; +import { z } from 'astro/zod'; +import { apiLoader } from './loaders/api-loader'; + +const products = defineLiveCollection({ + loader: apiLoader({ endpoint: process.env.API_URL }), + schema: z + .object({ + id: z.string(), + name: z.string(), + price: z.number(), + // Transform the API's category format + category: z.string().transform((str) => str.toLowerCase().replace(/\s+/g, '-')), + // Coerce the date to a Date object + createdAt: z.coerce.date(), + }) + .transform((data) => ({ + ...data, + // Add a formatted price field + displayPrice: `$${data.price.toFixed(2)}`, + })), +}); + +export const collections = { products }; +``` + +When using Zod schemas with live collections, validation errors are automatically caught and returned as `AstroError` objects: + +```astro title="src/pages/store/index.astro" +--- +export const prerender = false; // Not needed in 'server' mode + +import { LiveCollectionValidationError } from 'astro/content/runtime'; +import { getLiveEntry } from 'astro:content'; + +const { entry, error } = await getLiveEntry('products', '123'); + +// You can handle validation errors specifically +if (LiveCollectionValidationError.is(error)) { + console.error(error.message); + return Astro.rewrite('/500'); +} + +// TypeScript knows entry.data matches your Zod schema, not the loader's type +console.log(entry?.data.displayPrice); // e.g., "$29.99" +--- +``` + +See [Zod's README](https://github.com/colinhacks/zod) for complete documentation on how Zod works and what features are available. + + +### Accessing live data + +Astro provides live collection helper functions to access live data on each request and return one (or more) content entries. These can be used similarly to their [build-time collection counterparts](#querying-build-time-collections). + +- [`getLiveCollection()`](/en/reference/modules/astro-content/#getlivecollection) fetches an entire collection and returns an array of entries. +- [`getLiveEntry()`](/en/reference/modules/astro-content/#getliveentry) fetches a single entry from a collection. + +These return entries with a unique `id`, and `data` object with all defined properties from the live loader. When using third-party or community loaders distributed as npm packages, check their own documentation for the expected shape of data returned. + +You can use these functions to access your live data, passing the name of the collection and optionally filtering conditions. + +```astro title="src/pages/store/[slug].astro" +--- +export const prerender = false; // Not needed in 'server' mode + +import { getLiveCollection, getLiveEntry } from 'astro:content'; + +// Use loader-specific filters +const { entries: draftArticles } = await getLiveCollection('articles', { + status: 'draft', + author: 'john-doe', +}); + +// Get a specific product by ID +const { entry: product } = await getLiveEntry('products', Astro.params.slug); +--- +``` + +#### Rendering content + +If your live loader [returns a `rendered` property](/en/reference/content-loader-reference/#livedataentryrendered), you can use [the `render()` function and `` component](#rendering-body-content) to render your content directly in your pages, using the same method as build-time collections. + +You also have access to any [error returned by the live loader](/en/reference/content-loader-reference/#error-handling-in-live-loaders), for example, to rewrite to a 404 page when content cannot be displayed: + +```astro title="src/pages/store/[id].astro" "render(entry)" "" +--- +export const prerender = false; // Not needed in 'server' mode + +import { getLiveEntry, render } from 'astro:content'; +const { entry, error } = await getLiveEntry('articles', Astro.params.id); +if (error) { + return Astro.rewrite('/404'); +} + +const { Content } = await render(entry); +--- + +

{entry.data.title}

+ +``` + +#### Error handling + +Live loaders can fail due to network issues, API errors, or validation problems. The API is designed to make error handling explicit. + +When you call `getLiveCollection()` or `getLiveEntry()`, the error will be one of: + +- The error type defined by the loader (if it returned an error) +- A `LiveEntryNotFoundError` if the entry was not found +- A `LiveCollectionValidationError` if the collection data does not match the expected schema +- A `LiveCollectionCacheHintError` if the cache hint is invalid +- A `LiveCollectionError` for other errors, such as uncaught errors thrown in the loader + +You can use `instanceof` to check the type of an error at runtime: + +```astro title="src/pages/store/[id].astro" "LiveEntryNotFoundError.is(error)" +--- +export const prerender = false; // Not needed in 'server' mode + +import { LiveEntryNotFoundError } from 'astro/content/runtime'; +import { getLiveEntry } from 'astro:content'; + +const { entry, error } = await getLiveEntry('products', Astro.params.id); + +if (error) { + if (error instanceof LiveEntryNotFoundError) { + console.error(`Product not found: ${error.message}`); + Astro.response.status = 404; + } else { + console.error(`Error loading product: ${error.message}`); + return Astro.redirect('/500'); + } +} +--- +``` + +## Using JSON Schema files in your editor

@@ -556,7 +877,7 @@ Astro auto-generates [JSON Schema](https://json-schema.org/) files for collectio A JSON Schema file is generated for each collection in your project and output to the `.astro/collections/` directory. For example, if you have two collections, one named `authors` and another named `posts`, Astro will generate `.astro/collections/authors.schema.json` and `.astro/collections/posts.schema.json`. -### Use JSON Schemas in JSON files +

Use JSON Schemas in JSON files

You can manually point to an Astro-generated schema by setting the `$schema` field in your JSON file. The value should be a relative file path from the data file to the schema. @@ -570,9 +891,9 @@ In the following example, a data file in `src/data/authors/` uses the schema gen } ``` -#### Use a schema for a group of JSON files in VS Code +

Use a schema for a group of JSON files in VS Code

-In VS Code, you can configure a schema to apply for all files in a collection using the [`json.schemas` setting](https://code.visualstudio.com/docs/languages/json#_json-schemas-and-settings). +In VS Code, you can configure a schema to apply to all files in a collection using the [`json.schemas` setting](https://code.visualstudio.com/docs/languages/json#_json-schemas-and-settings). In the following example, all files in the `src/data/authors/` directory will use the schema generated for the `authors` collection: ```json @@ -586,7 +907,7 @@ In the following example, all files in the `src/data/authors/` directory will us } ``` -### Use schemas in YAML files in VS Code +

Use schemas in YAML files in VS Code

In VS Code, you can add support for using JSON schemas in YAML files using the [Red Hat YAML](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml) extension. With this extension installed, you can reference a schema in a YAML file using a special comment syntax: @@ -599,9 +920,9 @@ skills: - Starlight ``` -#### Use schemas for a group of YAML files in VS Code +

Use schemas for a group of YAML files in VS Code

-With the Red Hat YAML extension, you can configure a schema to apply for all YAML files in a collection using the `yaml.schemas` setting. +With the Red Hat YAML extension, you can configure a schema to apply to all YAML files in a collection using the `yaml.schemas` setting. In the following example, all YAML files in the `src/data/authors/` directory will use the schema generated for the `authors` collection: ```json @@ -613,31 +934,3 @@ In the following example, all YAML files in the `src/data/authors/` directory wi ``` See [“Associating schemas”](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml#associating-schemas) in the Red Hat YAML extension documentation for more details. - -## When to create a collection - -You can [create a collection](#defining-collections) any time you have a group of related data or content that shares a common structure. - -Much of the benefit of using collections comes from: - -- Defining a common data shape to validate that an individual entry is "correct" or "complete", avoiding errors in production. -- Content-focused APIs designed to make querying intuitive (e.g. `getCollection()` instead of `import.meta.glob()`) when importing and rendering content on your pages. -- A [Content Loader API](/en/reference/content-loader-reference/) for retrieving your content that provides both built-in loaders and access to the low-level API. There are several third-party and community-built loaders available, and you can build your own custom loader to fetch data from anywhere. -- Performance and scalability. The Content Layer API allows data to be cached between builds and is suitable for tens of thousands of content entries. - -[Define your data](#defining-collections) as a collection when: - -- You have multiple files or data to organize that share the same overall structure (e.g. blog posts written in Markdown which all have the same frontmatter properties). -- You have existing content stored remotely, such as in a CMS, and want to take advantage of the collections helper functions and Content Layer API instead of using `fetch()` or SDKs. -- You need to fetch (tens of) thousands of related pieces of data, and need a querying and caching method that handles at scale. - -### When not to create a collection - -Collections provide excellent structure, safety, and organization when you have **multiple pieces of content that must share the same properties**. - -Collections **may not be your solution** if: - -- You have only one or a small number of different pages. Consider [making individual page components](/en/basics/astro-pages/) such as `src/pages/about.astro` with your content directly instead. -- You are displaying files that are not processed by Astro, such as PDFs. Place these static assets in the [`public/` directory](/en/basics/project-structure/#public) of your project instead. -- Your data source has its own SDK/client library for imports that is incompatible with or does not offer a content loader and you prefer to use it directly. -- You are using APIs that need to be updated in real time. Content collections are only updated at build time, so if you need live data, use other methods of [importing files](/en/guides/imports/#import-statements) or [fetching data](/en/guides/data-fetching/) with [on-demand rendering](/en/guides/on-demand-rendering/). diff --git a/src/content/docs/en/guides/deploy/clever-cloud.mdx b/src/content/docs/en/guides/deploy/clever-cloud.mdx index 6cd0231c45cc1..5dfae0eed2537 100644 --- a/src/content/docs/en/guides/deploy/clever-cloud.mdx +++ b/src/content/docs/en/guides/deploy/clever-cloud.mdx @@ -11,7 +11,7 @@ i18nReady: true import { Tabs, TabItem, Steps } from '@astrojs/starlight/components'; import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'; -[Clever Cloud](https://clever-cloud.com) is a European cloud platform that provides automated, scalable services. +[Clever Cloud](https://clevercloud.com) is a European cloud platform that provides automated, scalable services. ## Project Configuration @@ -45,7 +45,7 @@ To deploy your Astro project to Clever Cloud, you will need to **create a new ap 3. Select a **Node.js** application, or a **static** one. 4. Set up the minimal size for your instance and scalability options. Astro sites can typically be deployed using the **Nano** instance. Depending on your project's specifications and dependencies, you may need to adjust accordingly as you watch the metrics from the **Overview** page. 5. Select a **region** to deploy your instance. -6. Skip [connecting **Add-ons** to your Clever application](https://www.clever-cloud.com/developers/doc/addons/) unless you're using a database or Keycloak. +6. Skip [connecting **Add-ons** to your Clever application](https://www.clevercloud.com/developers/doc/addons/) unless you're using a database or Keycloak. 7. Inject **environment variables**: - For **Node.js**, set the following environment variables based on your package manager: @@ -111,5 +111,5 @@ For example, if you want to deploy your local `main` branch without renaming it, ## Official Resources -- [Clever Cloud documentation for deploying a Node.js application](https://www.clever-cloud.com/developers/doc/applications/javascript/nodejs/) -- [Clever Cloud documentation for deploying Astro as a static application](https://www.clever-cloud.com/developers/guides/astro/) +- [Clever Cloud documentation for deploying a Node.js application](https://www.clevercloud.com/developers/doc/applications/nodejs/) +- [Clever Cloud documentation for deploying Astro as a static application](https://www.clevercloud.com/developers/guides/astro/) diff --git a/src/content/docs/en/guides/deploy/microsoft-azure.mdx b/src/content/docs/en/guides/deploy/microsoft-azure.mdx index 9eba99e7c30ac..889968503d19d 100644 --- a/src/content/docs/en/guides/deploy/microsoft-azure.mdx +++ b/src/content/docs/en/guides/deploy/microsoft-azure.mdx @@ -40,7 +40,7 @@ The GitHub action yaml that is created for you assumes the use of node 14. This ``` "engines": { - "node": ">=18.0.0" + "node": ">=22.12.0" }, ``` diff --git a/src/content/docs/en/guides/deploy/netlify.mdx b/src/content/docs/en/guides/deploy/netlify.mdx index 1d59154bafb77..df5016fe5d20d 100644 --- a/src/content/docs/en/guides/deploy/netlify.mdx +++ b/src/content/docs/en/guides/deploy/netlify.mdx @@ -101,7 +101,7 @@ You can also create a new site on Netlify and link up your Git repository by ins ### Set a Node.js version -If you are using a legacy [build image](https://docs.netlify.com/configure-builds/get-started/#build-image-selection) (Xenial) on Netlify, make sure that your Node.js version is set. Astro requires `v18.20.8` or `v20.3.0` or higher. +If you are using a legacy [build image](https://docs.netlify.com/configure-builds/get-started/#build-image-selection) (Xenial) on Netlify, make sure that your Node.js version is set. Astro requires `v22.12.0` or higher. You can [specify your Node.js version in Netlify](https://docs.netlify.com/configure-builds/manage-dependencies/#node-js-and-javascript) using: - a [`.nvmrc`](https://github.com/nvm-sh/nvm#nvmrc) file in your base directory. diff --git a/src/content/docs/en/guides/endpoints.mdx b/src/content/docs/en/guides/endpoints.mdx index 9bedfb7bdb111..b604d8501eb8b 100644 --- a/src/content/docs/en/guides/endpoints.mdx +++ b/src/content/docs/en/guides/endpoints.mdx @@ -47,6 +47,8 @@ import type { APIRoute } from "astro"; export const GET = (async ({ params, request }) => { /* ... */ }) satisfies APIRoute; ``` +Note that endpoints whose URLs include a file extension (e.g. `src/pages/sitemap.xml.ts`) can only be accessed without a trailing slash (e.g. `/sitemap.xml`), regardless of your [`build.trailingSlash`](/en/reference/configuration-reference/#trailingslash) configuration. + ### `params` and Dynamic routing Endpoints support the same [dynamic routing](/en/guides/routing/#dynamic-routes) features that pages do. Name your file with a bracketed parameter name and export a [`getStaticPaths()` function](/en/reference/routing-reference/#getstaticpaths). Then, you can access the parameter using the `params` property passed to the endpoint function: diff --git a/src/content/docs/en/guides/environment-variables.mdx b/src/content/docs/en/guides/environment-variables.mdx index af2b99b85fd9e..de3a31e6ee9e5 100644 --- a/src/content/docs/en/guides/environment-variables.mdx +++ b/src/content/docs/en/guides/environment-variables.mdx @@ -58,7 +58,6 @@ Astro includes a few environment variables out of the box: - `import.meta.env.DEV`: `true` if your site is running in development; `false` otherwise. Always the opposite of `import.meta.env.PROD`. - `import.meta.env.BASE_URL`: The base URL your site is being served from. This is determined by the [`base` config option](/en/reference/configuration-reference/#base). - `import.meta.env.SITE`: This is set to [the `site` option](/en/reference/configuration-reference/#site) specified in your project's `astro.config`. -- `import.meta.env.ASSETS_PREFIX`: The prefix for Astro-generated asset links if the [`build.assetsPrefix` config option](/en/reference/configuration-reference/#buildassetsprefix) is set. This can be used to create asset links not handled by Astro. Use them like any other environment variable. @@ -320,7 +319,7 @@ envField.enum({ }) ``` -For a complete list of validation fields, see the [`envField` API reference](/en/reference/configuration-reference/#envschema). +For a complete list of validation fields, see the [`envField` API reference](/en/reference/modules/astro-config/#envfield). ### Retrieving secrets dynamically diff --git a/src/content/docs/en/guides/fonts.mdx b/src/content/docs/en/guides/fonts.mdx index a71078b9c8909..0dffeb52019bf 100644 --- a/src/content/docs/en/guides/fonts.mdx +++ b/src/content/docs/en/guides/fonts.mdx @@ -8,129 +8,309 @@ description: >- i18nReady: true --- import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'; -import { Steps } from '@astrojs/starlight/components' +import ReadMore from '~/components/ReadMore.astro'; +import { Steps, Tabs, TabItem } from '@astrojs/starlight/components'; -This guide will show you how to add web fonts to your project and use them in your components. +This guide will show you how to add [web fonts](https://developer.mozilla.org/en-US/docs/Learn/CSS/Styling_text/Web_fonts) to your project and use them in your components. -:::tip[Experimental Fonts API] -Learn about Astro's [experimental Fonts API](/en/reference/experimental-flags/fonts/) that allows you to use fonts from your filesystem and various font providers through a unified, fully customizable, and type-safe API. -::: +Astro provides a way to use fonts from your filesystem and various font providers (e.g. Fontsource, Google) through a unified, [fully customizable](/en/reference/configuration-reference/#fonts), and type-safe API. -## Using a local font file +Web fonts can impact page performance at both load time and rendering time. This API helps you keep your site performant with automatic [web font optimizations](https://web.dev/learn/performance/optimize-web-fonts) including preload links, optimized fallbacks, and opinionated defaults. [See common usage examples](#examples). -This example will demonstrate adding a custom font using the font file `DistantGalaxy.woff`. +The Fonts API focuses on performance and privacy by downloading and caching fonts so they're served from your site. This can avoid sending user data to third-party sites, and also ensures that a consistent set of fonts is available to all your visitors. + +## Configuring custom fonts + +Registering custom fonts for your Astro project is done through [the `fonts` option](/en/reference/configuration-reference/#fonts) in your Astro config. + +For each font you want to use, you must specify its [name](/en/reference/configuration-reference/#fontname), a [CSS variable](/en/reference/configuration-reference/#fontcssvariable), and an Astro font provider. + +Astro provides [built-in support for the most popular font providers](/en/reference/font-provider-reference/#built-in-providers): Adobe, Bunny, Fontshare, Fontsource, Google, Google Icons and NPM, as well as for using your own local font files. Additionally, you can [further customize your font configuration](#granular-font-configuration) to optimize performance and visitor experience. + + +### Using a local font file + +This example will demonstrate adding a custom font using the font file `DistantGalaxy.woff2`. -1. Add your font file to `public/fonts/`. -2. Add the following `@font-face` statement to your CSS. This could be in a global `.css` file you import, a ` + + +1. Find the font you want to use in [Fontsource's catalog](https://fontsource.org/). This example will use [Roboto](https://fontsource.org/fonts/roboto). + +2. Create a new font family in your Astro config file using the [Fontsource provider](/en/reference/font-provider-reference/#fontsource): + + ```js title="astro.config.mjs" + import { defineConfig, fontProviders } from "astro/config"; + + export default defineConfig({ + fonts: [{ + provider: fontProviders.fontsource(), + name: "Roboto", + cssVariable: "--font-roboto", + }] + }); ``` +3. Your font is now configured and ready to be [added to your page head](#applying-custom-fonts) so that it can be used in your project. + -## Using Fontsource +## Applying custom fonts -The [Fontsource](https://fontsource.org/) project simplifies using Google Fonts and other open-source fonts. It provides npm modules you can install for the fonts you want to use. +After [a font is configured](#configuring-custom-fonts), it must be added to your page head with an identifying CSS variable. Then, you can use this variable when defining your page styles. -1. Find the font you want to use in [Fontsource's catalog](https://fontsource.org/). This example will use [Twinkle Star](https://fontsource.org/fonts/twinkle-star). -2. Install the package for your chosen font. - - - - ```shell - npm install @fontsource/twinkle-star - ``` - - - ```shell - pnpm add @fontsource/twinkle-star - ``` - - - ```shell - yarn add @fontsource/twinkle-star - ``` - - - - :::tip - You'll find the correct package name in the “Quick Installation” section of each font page on Fontsource's website. It will start with `@fontsource/` or `@fontsource-variable/` followed by the name of the font. - ::: - -3. Import the font package in the component where you want to use the font. Usually, you will want to do this in a common layout component to make sure the font is available across your site. - - The import will automatically add the necessary `@font-face` rules needed to set up the font. - - ```astro title="src/layouts/BaseLayout.astro" + +1. Import and include the [``](/en/reference/modules/astro-assets/#font-) component with the required `cssVariable` property in the head of your page, usually in a dedicated `Head.astro` component or in a [layout](/en/basics/layouts/) component directly: + + ```astro title="src/layouts/Layout.astro" ins={2,7} --- - import '@fontsource/twinkle-star'; + import { Font } from "astro:assets"; --- + + + + + + + + + ``` -4. Use the font's name as shown in the `body` example on its Fontsource page as the `font-family` value. This will work anywhere you can write CSS in your Astro project. +2. In any page rendered with that layout, including the layout component itself, you can now define styles with your font's `cssVariable` to apply your custom font. - ```css - h1 { - font-family: "Twinkle Star", cursive; - } + In the following example, the `

` heading will have the custom font applied, while the paragraph `

` will not. + + ```astro title="src/pages/example.astro" ins={10-12} + --- + import Layout from "../layouts/Layout.astro"; + --- + +

In a galaxy far, far away...

+ +

Custom fonts make my headings much cooler!

+ + + ``` -
-To optimize your website’s rendering times, you may want to preload fonts that are essential for the initial page display. -See the [Fontsource guide to preloading fonts](https://fontsource.org/docs/getting-started/preload) for more information and usage. +
## Register fonts in Tailwind -If you are using [Tailwind](/en/guides/styling/#tailwind), you can use either of the previous methods on this page to install your font, with some modifications. You can either add an [`@font-face` statement for a local font](#using-a-local-font-file) or use [Fontsource's `import` strategy](#using-fontsource) to install your font. +If you are using [Tailwind](/en/guides/styling/#tailwind) for styling, you will not apply your styles with the `font-face` CSS property. -To register your font in Tailwind: +Instead, after [configuring your custom font](#configuring-custom-fonts) and [adding it to your page head](#applying-custom-fonts), you will need to update your Tailwind configuration to register your font: - -1. Follow either of the guides above, but skip the final step of adding `font-family` to your CSS. -2. Add the typeface name to `src/styles/global.css`. + - This example adds `Inter` to the sans-serif font stack. + - ```css title="src/styles/global.css" ins={3-5} - @import 'tailwindcss'; + ```css title="src/styles/global.css" ins={4} ins="inline" + @import "tailwindcss"; - @theme { - --font-sans: 'Inter', 'sans-serif'; + @theme inline { + --font-sans: var(--font-roboto); } ``` - Now, all sans-serif text (the default with Tailwind) in your project will use your chosen font and the `font-sans` class will also apply the Inter font. - + + + + + ```js title="tailwind.config.mjs" ins={6-8} + /** @type {import("tailwindcss").Config} */ + export default { + content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"], + theme: { + extend: {}, + fontFamily: { + sans: ["var(--font-roboto)"] + } + }, + plugins: [] + }; + ``` + + + + See [Tailwind's docs on adding custom font families](https://tailwindcss.com/docs/font-family#using-custom-values) for more information. -## More resources -- Learn how web fonts work in [MDN's web fonts guide](https://developer.mozilla.org/en-US/docs/Learn/CSS/Styling_text/Web_fonts). -- Generate CSS for your font with [Font Squirrel's Webfont Generator](https://www.fontsquirrel.com/tools/webfont-generator). +## Accessing font data programmatically + +The [`fontData`](/en/reference/modules/astro-assets/#fontdata) object allows you to retrieve lower-level font family data programmatically. For example, you can use it in an [API Route](/en/guides/endpoints/#server-endpoints-api-routes) to generate OpenGraph images using [satori](https://github.com/vercel/satori), combined with proper [formats](/en/reference/configuration-reference/#fontformats) configuration: + +```tsx title="src/pages/og.png.ts" {2} "fontData[\"--font-roboto\"]" +import type{ APIRoute } from "astro"; +import { fontData } from "astro:assets"; +import satori from "satori"; +import { html } from "satori-html"; + +export const GET: APIRoute = async (context) => { + const data = fontData["--font-roboto"]; + + const svg = await satori( + html`
hello, world
`, + { + width: 600, + height: 400, + fonts: [ + { + name: "Roboto", + data: await fetch( + new URL(data[0].src[0].url, context.url.origin), + ).then((res) => res.arrayBuffer()), + weight: 400, + style: "normal", + }, + ], + }, + ); + + // ... +} +``` + +## Granular font configuration + +A font family is defined by a combination of properties such as weights and styles (e.g. `weights: [500, 600]` and `styles: ["normal", "bold"]`), but you may want to download only certain combinations of these. + +For greater control over which font files are downloaded, you can specify the same font (ie. with the same `cssVariable`, `name`, and `provider` properties) multiple times with different combinations. Astro will merge the results and download only the required files. For example, it is possible to download normal `500` and `600` while downloading only italic `500`: + +```js title="astro.config.mjs" +import { defineConfig, fontProviders } from "astro/config"; + +export default defineConfig({ + fonts: [ + { + name: "Roboto", + cssVariable: "--roboto", + provider: fontProviders.google(), + weights: [500, 600], + styles: ["normal"] + }, + { + name: "Roboto", + cssVariable: "--roboto", + provider: fontProviders.google(), + weights: [500], + styles: ["italic"] + } + ] +}); +``` + +## Caching + +The Fonts API caching implementation was designed to be practical in development and efficient in production. During builds, font files are copied to the `_astro/fonts` output directory, so they can benefit from HTTP caching of static assets (usually a year). + +To clear the cache in development, remove the `.astro/fonts` directory. To clear the build cache, remove the `node_modules/.astro/fonts` directory. + +## Examples + +Astro's font feature is based on flexible configuration options. Your own project's font configuration may look different from simplified examples, so the following are provided to show what various font configurations might look like when used in production. + +```js title="astro.config.mjs" +import { defineConfig, fontProviders } from "astro/config"; + +export default defineConfig({ + fonts: [ + { + name: "Roboto", + cssVariable: "--font-roboto", + provider: fontProviders.google(), + // Default included: + // weights: [400] , + // styles: ["normal", "italics"], + // subsets: ["latin"], + // fallbacks: ["sans-serif"], + // formats: ["woff2"], + }, + { + name: "Inter", + cssVariable: "--font-inter", + provider: fontProviders.fontsource(), + // Specify weights that are actually used + weights: [400, 500, 600, 700], + // Specify styles that are actually used + styles: ["normal"], + // Download only font files for characters used on the page + subsets: ["latin", "cyrillic"], + // Download more font formats + formats: ["woff2", "woff"], + }, + { + name: "JetBrains Mono", + cssVariable: "--font-jetbrains-mono", + provider: fontProviders.fontsource(), + // Download only font files for characters used on the page + subsets: ["latin", "latin-ext"], + // Use a fallback font family matching the intended appearance + fallbacks: ["monospace"], + }, + { + name: "Poppins", + cssVariable: "--font-poppins", + provider: fontProviders.local(), + options: { + // Weight and style are not specified so Astro + // will try to infer them for each variant + variants: [ + { + src: [ + "./src/assets/fonts/Poppins-regular.woff2", + "./src/assets/fonts/Poppins-regular.woff", + ] + }, + { + src: [ + "./src/assets/fonts/Poppins-bold.woff2", + "./src/assets/fonts/Poppins-bold.woff", + ] + }, + ] + } + } + ], +}); +``` diff --git a/src/content/docs/en/guides/framework-components.mdx b/src/content/docs/en/guides/framework-components.mdx index 6d5f6d5c1260b..48227f214c438 100644 --- a/src/content/docs/en/guides/framework-components.mdx +++ b/src/content/docs/en/guides/framework-components.mdx @@ -191,7 +191,7 @@ For Svelte and Vue these slots can be referenced using a `` element with t Inside of an Astro file, framework component children can also be hydrated components. This means that you can recursively nest components from any of these frameworks. -```astro title="src/pages/nested-components.astro" {10-11} +```astro title="src/pages/nested-components.astro" {9-10} --- import MyReactSidebar from '../components/MyReactSidebar.jsx'; import MyReactButton from '../components/MyReactButton.jsx'; diff --git a/src/content/docs/en/guides/images.mdx b/src/content/docs/en/guides/images.mdx index 251cf3249509d..8a1ceb4d90939 100644 --- a/src/content/docs/en/guides/images.mdx +++ b/src/content/docs/en/guides/images.mdx @@ -51,7 +51,7 @@ All native HTML tags, including `` and ``, are also available in `.ast For all images in `.astro` files, **the value of the image `src` attribute is determined by the location of your image file**: - A local image from your project `src/` folder uses an import from the file's relative path. - + The image and picture components use the named import directly (e.g. `src={rocket}`), while the `` tag uses the `src` object property of the import (e.g. `src={rocket.src}`). - Remote and `public/` images use a URL path. @@ -321,7 +321,7 @@ import myImage from '../assets/my_image.png'; // Image is 1600x900 Responsive images are images that adjust to improve performance across different devices. These images can resize to fit their container, and can be served in different sizes depending on your visitor's screen size and resolution. -With [responsive image properties](/en/reference/modules/astro-assets/#responsive-image-properties) applied to the `` or `` components, Astro will automatically generate the required `srcset` and `sizes` values for your images, and apply the necessary [styles to ensure they resize correctly](#responsive-image-styles). +With the [layout property](/en/reference/modules/astro-assets/#layout) applied to the `` or `` components, Astro will automatically generate the required `srcset` and `sizes` values for your images, and apply the necessary [styles to ensure they resize correctly](#responsive-image-styles). When this responsive behavior is [configured globally with `image.layout`](/en/reference/configuration-reference/#imagelayout), it will apply to all image components and also to any local and remote images using [the Markdown `![]()` syntax](/en/guides/images/#images-in-markdown-files). @@ -396,7 +396,7 @@ The global styles applied by Astro will depend on the layout type, and are desig The styles use the [`:where()` pseudo-class](https://developer.mozilla.org/en-US/docs/Web/CSS/:where), which has a [specificity](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_cascade/Specificity) of 0, meaning that it is easy to override with your own styles. Any CSS selector will have a higher specificity than `:where()`, so you can easily override the styles by adding your own styles to target the image. -You can override the `object-fit` and `object-position` styles on a per-image basis by setting the `fit` and `position` props on the `` or `` component. +You can override the `object-fit` and `object-position` styles on a per-image basis by setting the `fit` and `position` props on the `` or `` component. #### Responsive images with Tailwind 4 @@ -442,21 +442,21 @@ You can also enforce type safety for your `.svg` assets using the `SvgComponent` ```astro title="src/components/Logo.astro" --- import type { SvgComponent } from "astro/types"; -import HomeIcon from './Home.svg' +import HomeIcon from "./Home.svg"; interface Link { - url: string - text: string - icon: SvgComponent + url: string; + text: string; + icon: SvgComponent; } const links: Link[] = [ - { - url: '/', - text: 'Home', - icon: HomeIcon - } -] + { + url: "/", + text: "Home", + icon: HomeIcon, + }, +]; --- ``` @@ -580,7 +580,7 @@ You can declare an associated image for a content collections entry, such as a b ```md title="src/content/blog/my-post.md" {3} --- title: "My first blog post" -cover: "./firstpostcover.jpeg" # will resolve to "src/content/blog/firstblogcover.jpeg" +cover: "./firstpostcover.jpeg" # will resolve to "src/content/blog/firstpostcover.jpeg" coverAlt: "A photograph of a sunset behind a mountain range." --- @@ -590,8 +590,8 @@ This is a blog post The `image` helper for the content collections schema lets you validate and import the image. ```ts title="src/content.config.ts" -import { defineCollection } from "astro:content"; -import { z } from "astro/zod"; +import { defineCollection } from 'astro:content'; +import { z } from 'astro/zod'; const blogCollection = defineCollection({ schema: ({ image }) => z.object({ @@ -622,7 +622,7 @@ const allBlogPosts = await getCollection("blog"); )) @@ -633,6 +633,24 @@ const allBlogPosts = await getCollection("blog"); The `getImage()` function is intended for generating images destined to be used somewhere else than directly in HTML, for example in an [API Route](/en/guides/endpoints/#server-endpoints-api-routes). When you need options that the `` and `` components do not currently support, you can use the `getImage()` function to create your own custom `` component. +`getImage()` can only be used on the server. If you need to use the resulting image URL on the client (e.g. in a client-side script or framework component), call `getImage()` inside the frontmatter and pass the resulting `src` to the client: + +```astro title="src/components/ClientImage.astro" +--- +import { getImage } from "astro:assets"; +import myBackground from "../background.png"; + +const optimizedBackground = await getImage({ src: myBackground, format: "avif" }); +--- + +
+ + +``` + See more in the [`getImage()` reference](/en/reference/modules/astro-assets/#getimage). @@ -659,7 +677,7 @@ pnpm add sharp ### Configure no-op passthrough service -If your [adapter](https://astro.build/integrations/?search=&categories%5B%5D=adapters) does not support Astro's built-in Sharp image optimization (e.g. Deno, Cloudflare), you can configure a no-op image service to allow you to use the `` and `` components. Note that Astro does not perform any image transformation and processing in these environments. However, you can still enjoy the other benefits of using `astro:assets`, including no Cumulative Layout Shift (CLS), the enforced `alt` attribute, and a consistent authoring experience. +If your [adapter](https://astro.build/integrations/?search=&categories%5B%5D=adapters) does not support Astro's built-in Sharp image optimization (e.g. Cloudflare), you can configure a no-op image service to allow you to use the `` and `` components. Note that Astro does not perform any image transformation and processing in these environments. However, you can still enjoy the other benefits of using `astro:assets`, including no Cumulative Layout Shift (CLS), the enforced `alt` attribute, and a consistent authoring experience. Configure the `passthroughImageService()` to avoid Sharp image processing: diff --git a/src/content/docs/en/guides/imports.mdx b/src/content/docs/en/guides/imports.mdx index 2b7145046b4c6..132658cccef41 100644 --- a/src/content/docs/en/guides/imports.mdx +++ b/src/content/docs/en/guides/imports.mdx @@ -344,7 +344,7 @@ Additionally, glob patterns must begin with one of the following: ### `import.meta.glob()` vs `getCollection()` -[Content collections](/en/guides/content-collections/) provide a [`getCollection()` API](/en/reference/modules/astro-content/#getcollection) for loading multiple files instead of `import.meta.glob()`. If your content files (e.g. Markdown, MDX, Markdoc) are located in collections within the `src/content/` directory, use `getCollection()` to [query a collection](/en/guides/content-collections/#querying-collections) and return content entries. +[Content collections](/en/guides/content-collections/) provide [performant, content-focused APIs](/en/reference/modules/astro-content/) for loading multiple files instead of `import.meta.glob()`. Use `getCollection()` and `getLiveCollection()` to query your collections and return content entries. ## WASM diff --git a/src/content/docs/en/guides/integrations-guide/cloudflare.mdx b/src/content/docs/en/guides/integrations-guide/cloudflare.mdx index 60dd13a2e518b..7a1eb6a865d9d 100644 --- a/src/content/docs/en/guides/integrations-guide/cloudflare.mdx +++ b/src/content/docs/en/guides/integrations-guide/cloudflare.mdx @@ -20,11 +20,14 @@ If you're using Astro as a static site builder, you don't need an adapter. Learn how to deploy your Astro site in our [Cloudflare deployment guide](/en/guides/deploy/cloudflare/). +:::tip[Upgrading to Astro 6?] +Astro 6 requires an upgrade to v13 of this adapter. See the [Cloudflare adapter upgrade instructions for Astro 6](#upgrading-to-v13-and-astro-6) for breaking changes and migration guidance. +::: + ## Why Astro Cloudflare Cloudflare's [Developer Platform](https://developers.cloudflare.com/) lets you develop full-stack applications with access to resources such as storage and AI, all deployed to a global edge network. This adapter builds your Astro project for deployment through Cloudflare. - ## Installation Astro includes an `astro add` command to automate the setup of official integrations. If you prefer, you can [install integrations manually](#manual-install) instead. @@ -77,200 +80,76 @@ Now, you can enable [on-demand rendering per page](/en/guides/on-demand-renderin 2. Add the adapter to your `astro.config.mjs` file: - ```js title="astro.config.mjs" ins={2,5} - import { defineConfig } from 'astro/config'; - import cloudflare from '@astrojs/cloudflare'; - - export default defineConfig({ - adapter: cloudflare(), - }); - ``` - -3. Create a [Wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/): - - ```jsonc title="wrangler.jsonc" - { - "main": "dist/_worker.js/index.js", - "name": "my-astro-app", - // Update to today's date - "compatibility_date": "2025-03-25", - "compatibility_flags": [ - "nodejs_compat", - "global_fetch_strictly_public" - ], - "assets": { - "binding": "ASSETS", - "directory": "./dist" - }, - "observability": { - "enabled": true - } - } - ``` + ```js title="astro.config.mjs" ins={2,5} + import { defineConfig } from 'astro/config'; + import cloudflare from '@astrojs/cloudflare'; -4. Create a `.assetsignore` file in your `public/` folder, and add the following lines to it: + export default defineConfig({ + adapter: cloudflare(), + }); + ``` - ```txt title="public/.assetsignore" - _worker.js - _routes.json - ``` +3. Astro will automatically generate a default configuration, using the package.json name field or the folder name as the Worker name. You can optionally create a [Wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) if you need custom settings. This example declares Cloudflare KV bindings: + ```jsonc title="wrangler.jsonc" + { + "name": "my-astro-app", + // Add your bindings here, e.g.: + // "kv_namespaces": [{ "binding": "MY_KV", "id": "" }] + } + ``` ## Options -The Cloudflare adapter accepts the following options: +The Cloudflare adapter accepts the following options from [`@cloudflare/vite-plugin`](https://developers.cloudflare.com/workers/vite-plugin/): -### `cloudflareModules` +- `auxiliaryWorkers` +- `configPath` +- `inspectorPort` +- `persistState` +- `remoteBindings` +- `experimental.headersAndRedirectsDevModeSupport` -

-**Type:** `boolean`
-**Default:** `true` -

- -Enables [imports of `.wasm`, `.bin`, and `.txt` modules](#cloudflare-module-imports). - -This functionality is enabled by default. If you'd like to disable, set `cloudflareModules` to `false`. +It also accepts the following: ### `imageService`

-**Type:** `'passthrough' | 'cloudflare' | 'compile' | 'custom'`
-**Default:** `'compile'` +**Type:** `'passthrough' | 'cloudflare' | 'cloudflare-binding' | 'compile' | 'custom' | { build: 'compile', runtime?: 'cloudflare-binding' | 'passthrough' }`
+**Default:** `'cloudflare-binding'`

-Determines which image service is used by the adapter. The adapter will default to `compile` mode when an incompatible image service is configured. Otherwise, it will use the globally configured image service: - -* **`cloudflare`:** Uses the [Cloudflare Image Resizing](https://developers.cloudflare.com/images/image-resizing/) service. -* **`passthrough`:** Uses the existing [`noop`](/en/guides/images/#configure-no-op-passthrough-service) service. -* **`compile`:** Uses Astro's default service (sharp), but only on pre-rendered routes at build time. For pages rendered on-demand, all `astro:assets` features are disabled. -* **`custom`:** Always uses the image service configured in [Image Options](/en/reference/configuration-reference/#image-options). **This option will not check to see whether the configured image service works in Cloudflare's `workerd` runtime.** - -```js title="astro.config.mjs" ins={6} -import { defineConfig } from "astro/config"; -import cloudflare from '@astrojs/cloudflare'; - -export default defineConfig({ - adapter: cloudflare({ - imageService: 'cloudflare' - }), -}) -``` - -### `platformProxy` - -Determines whether and how the Cloudflare runtime is added to `astro dev`. It contains proxies to local `workerd` bindings and emulations of Cloudflare specific values, allowing the emulation of the runtime in the Node.js dev process. Read more about the [Cloudflare Runtime](#cloudflare-runtime). +Determines which image service is used by the adapter. The adapter will default to `cloudflare-binding` mode when an incompatible image service is configured. Otherwise, it will use the globally configured image service: -:::note -Proxies provided by this are a best effort emulation of the real production. Although they are designed to be as close as possible to the real thing, there might be a slight differences and inconsistencies between the two. -::: - -#### `platformProxy.enabled` -

-**Type:** `boolean`
-**Default:** `true` -

+- **`cloudflare`:** Uses the [Cloudflare Image Resizing](https://developers.cloudflare.com/images/image-resizing/) service. +- **`cloudflare-binding`:** Uses the [Cloudflare Images binding](https://developers.cloudflare.com/images/transform-images/bindings/) for image transformation. The binding is automatically provisioned when you deploy. +- **`passthrough`:** Uses the existing [`noop`](/en/guides/images/#configure-no-op-passthrough-service) service. +- **`compile`:** Uses a combination of internal dependencies to transform images locally at build time for prerendered routes. The noop `passthrough` option is configured for on-demand rendered pages. +- **`custom`:** Always uses the image service configured in [Image Options](/en/reference/configuration-reference/#image-options). **This option will not check to see whether the configured image service works in Cloudflare's `workerd` runtime.** -Determines whether to enable the Cloudflare runtime in development mode. +It is also possible to configure your image service as an object, setting both a build time and runtime service independently. Currently, `'compile'` is the only available build-time option. The supported runtime options are `'passthrough'` (default) and `'cloudflare-binding'`: -#### `platformProxy.configPath` -

-**Type:** `string`
-**Default:** `undefined` -

- -Defines the path to the Wrangler configuration file. If no value is set, it tracks `wrangler.toml`, `wrangler.json`, and `wrangler.jsonc` in the project root. - -#### `platformProxy.environment` -

-**Type:** `string`
-**Default:** `undefined` -

- -Sets the [Cloudflare environment](https://developers.cloudflare.com/workers/wrangler/environments/) to use. You must select an environment defined in the Wrangler configuration file, otherwise an error occurs. - -#### `platformProxy.persist` -

-**Type:** `boolean | { path: string }`
-**Default:** `true` -

- -Sets whether and where to save binding data locally to the file system. - -- If set to `true`, binding data is stored in `.wrangler/state/v3/`. It is the same as the default setting for wrangler. -- If set to `false`, binding data is not stored in file system. -- If set to `{ path: string }`, binding data is stored in the specified path. - -:::note -`wrangler`'s `--persist-to` option adds a sub directory called `v3` under the hood while the `@astrojs/cloudflare` `persist` property does not. For example, to reuse the same location as running `wrangler dev --persist-to ./my-directory`, you must specify: `persist: { path: "./my-directory/v3" }`. -::: - -The following configuration shows an example of enabling the Cloudflare runtime when running the development server, as well as using a `wrangler.json` config file. It also specifies a custom location for persisting data to the filesystem: - - -```js -import cloudflare from '@astrojs/cloudflare'; +```js title="astro.config.mjs" ins={6} import { defineConfig } from 'astro/config'; +import cloudflare from '@astrojs/cloudflare'; -export default defineConfig({ - adapter: cloudflare({ - platformProxy: { - enabled: true, - configPath: 'wrangler.json', - persist: { - path: './.cache/wrangler/v3' - }, - }, - }), -}); -``` -### `routes.extend` - -On Cloudflare Workers, this option is not applicable. Refer to [Routing on Cloudflare Workers](#routing-on-cloudflare-workers) for more information. - -On Cloudflare Pages, this option allows you to add or exclude custom patterns (e.g. `/fonts/*`) to the generated `_routes.json` file that determines which routes are generated on-demand. This can be useful if you need to add route patterns which cannot be automatically generated, or exclude prerendered routes. - -More information about the custom route patterns can be found in [Cloudflare's routing docs](https://developers.cloudflare.com/pages/functions/routing/#functions-invocation-routes). Any routes specified are not automatically deduplicated and will be appended to the existing routes as is. - -#### `routes.extend.include` - -

-**Type:** `{ pattern: string }[]`
-**Default:** `undefined` -

- -Configures additional routes to be generated on demand by the Cloudflare adapter in the `routes.extend.include` array. - -#### `routes.extend.exclude` - -

-**Type:** `{ pattern: string }[]`
-**Default:** `undefined` -

- -Configures routes to be excluded from on-demand rendering in the `routes.extend.exclude` array. These routes will be prerendered and served statically instead, and will not invoke the server function. Additionally you can use this option to serve any static asset (e.g. images, fonts, css, js, html, txt, json, etc.) files directly without routing the request through the server function. - -```js title="astro.config.mjs" export default defineConfig({ adapter: cloudflare({ - routes: { - extend: { - include: [{ pattern: '/static' }], // Route a prerended page to the server function for on-demand rendering - exclude: [{ pattern: '/pagefind/*' }], // Use Starlight's pagefind search, which is generated statically at build time - } - }, + imageService: { build: 'compile', runtime: 'cloudflare-binding' } }), }); ``` ### `sessionKVBindingName` +

**Type:** `string`
**Default:** `SESSION`

-The `sessionKVBindingName` option allows you to specify the name of the KV binding used for session storage. By default, this is set to `SESSION`, but you can change it to match your own KV binding name. See [Sessions](#sessions) for more information. +Sets the name of the KV binding used for session storage. By default, the KV namespace is automatically provisioned when you deploy, and is named `SESSION`. You can change this name by setting the binding manually in your wrangler config. See [Sessions](#sessions) for more information. ```js title="astro.config.mjs" "MY_SESSION_BINDING" export default defineConfig({ @@ -280,171 +159,126 @@ export default defineConfig({ }); ``` -### `workerEntryPoint` -

- -**Type:** `{ path: string | URL, namedExports: string[] }`
-**Default:** `{ path: '@astrojs/cloudflare/entrypoints/server.js', namedExports: [] }`
- -

- - -A configuration object to specify the [workerEntryPoint](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/rpc/) for your Cloudflare Worker when you use the `astro build` command. - -It allows you to optionally specify both a custom file `path` and `namedExports`: - -```js title="astro.config.mjs" -import cloudflare from '@astrojs/cloudflare'; -import { defineConfig } from 'astro/config'; - -export default defineConfig({ - adapter: cloudflare({ - workerEntryPoint: { - path: 'src/worker.ts', - namedExports: ['MyDurableObject'] - } - }), -}); +```jsonc title="wrangler.jsonc" +{ + "kv_namespaces": [ + { + "binding": "MY_SESSION_BINDING", + } + ] +} ``` -#### `workerEntryPoint.path` +### `imagesBindingName`

- **Type:** `string`
-**Default:** `@astrojs/cloudflare/entrypoints/server.js` - +**Default:** `IMAGES`

-The path to the entry file. This should be a relative path from the root of your Astro project. +Sets the name of the Images binding used when [`imageService`](#imageservice) is set to `cloudflare-binding`. By default, the binding is automatically provisioned with the name `IMAGES` when you deploy. You can change it by setting the binding manually in your wrangler config: -By default, the adapter uses a generic entry file, which only supports the `fetch` handler. +```js title="astro.config.mjs" "MY_IMAGES" +export default defineConfig({ + adapter: cloudflare({ + imageService: 'cloudflare-binding', + imagesBindingName: 'MY_IMAGES', + }), +}); +``` -To support other [Cloudflare invocation handlers](https://developers.cloudflare.com/workers/observability/logs/workers-logs/#invocation-logs), you can create a custom file to use as the entry point. This is useful if you want to use features that require other handlers (e.g. Durable Objects, Cloudflare Queues, Scheduled Invocations). +```jsonc title="wrangler.jsonc" +{ + "images": { + "binding": "MY_IMAGES" + } +} +``` -#### `workerEntryPoint.namedExports` +## Cloudflare runtime -

+The Cloudflare runtime gives you access to environment variables, bindings to Cloudflare resources, and other Cloudflare-specific APIs. -**Type:** `[]`
-**Default:** `[]` - -

+### Environment variables and bindings -An array of named exports to use for the entry file. +Environment variables and bindings are defined in your `wrangler.jsonc` configuration file. -Provide any additional defined named exports of your [custom entry file](#creating-a-custom-cloudflare-worker-entry-file) (e.g. `DurableObject`). If not provided, only default exports will be included. -#### Creating a custom Cloudflare Worker entry file -The custom entry file must export the `createExports()` function with a `default` export including all the handlers you need. +Define [environment variables](https://developers.cloudflare.com/workers/configuration/environment-variables/#add-environment-variables-via-wrangler) that do not store sensitive information in `wrangler.jsonc`: -The following example entry file registers a Durable Object and a queue handler: +```jsonc title="wrangler.jsonc" +{ + "vars": { + "MY_VARIABLE": "test", + }, +} +``` -```ts title="src/worker.ts" -import type { SSRManifest } from 'astro'; -import { App } from 'astro/app'; -import { handle } from '@astrojs/cloudflare/handler' -import { DurableObject } from 'cloudflare:workers'; +[Secrets](https://developers.cloudflare.com/workers/configuration/secrets/) are a special type of environment variable that allow you to attach encrypted text values to your Worker. They need to be defined differently to ensure they are not visible within Wrangler or Cloudflare dashboard after you set them. -class MyDurableObject extends DurableObject { - constructor(ctx: DurableObjectState, env: Env) { - super(ctx, env) - } -} +To define `secrets`, add them through the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/) rather than in your Wrangler config file: -export function createExports(manifest: SSRManifest) { - const app = new App(manifest); - return { - default: { - async fetch(request, env, ctx) { - await env.MY_QUEUE.send("log"); - return handle(manifest, app, request, env, ctx); - }, - async queue(batch, _env) { - let messages = JSON.stringify(batch.messages); - console.log(`consumed from our queue: ${messages}`); - } - } satisfies ExportedHandler, - MyDurableObject: MyDurableObject, - } -} +```bash +npx wrangler secret put ``` -## Cloudflare runtime - -### Usage +To set secrets for local development, add a `.dev.vars` file to the root of the Astro project: -The Cloudflare runtime gives you access to environment variables and bindings to Cloudflare resources defined in your `wrangler.toml`/`wrangler.jsonc` configuration file. +```ini title=".dev.vars" +DB_PASSWORD=myPassword +``` -You can access the bindings from `Astro.locals.runtime`: +Cloudflare environment variables and secrets can be imported from `"cloudflare:workers"`: ```astro title="src/pages/index.astro" --- -const { env } = Astro.locals.runtime; +import { env } from 'cloudflare:workers'; + +const myVariable = env.MY_VARIABLE; +const myKVNamespace = env.MY_KV; --- ``` -You can access the runtime from API endpoints through `context.locals`: -```js title="src/pages/api/someFile.js" -export function GET(context) { - const runtime = context.locals.runtime; +They are also compatible with the [`astro:env` API](/en/guides/environment-variables/#type-safe-environment-variables): - return new Response('Some body'); -} +```js +import { MY_VARIABLE } from 'astro:env/server'; ``` See the [list of all supported bindings](https://developers.cloudflare.com/workers/wrangler/api/#supported-bindings) in the Cloudflare documentation. +### The `cf` object -### Environment variables and secrets +The Cloudflare [`cf` object](https://developers.cloudflare.com/workers/runtime-apis/request/#incomingrequestcfproperties) contains request metadata such as geolocation information. Access it directly from the request: -The Cloudflare runtime treats environment variables as a type of binding. - -For example, you can define an [environment variable](https://developers.cloudflare.com/workers/configuration/environment-variables/#add-environment-variables-via-wrangler) in `wrangler.jsonc` as follows: - -```jsonc title="wrangler.jsonc" -{ - "vars" : { - "MY_VARIABLE": "test" - } -} +```astro title="src/pages/index.astro" +--- +const cf = Astro.request.cf; +const country = cf?.country; +--- ``` -Secrets are a special type of environment variable that allow you to attach encrypted text values to your Worker. They need to be defined differently to ensure they are not visible after you set them. - -To define `secrets`, add them through the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/) rather than in your Wrangler config file. +### Execution context -```bash -npx wrangler secret put -``` - -To set secrets for local development, you also need to add a `.dev.vars` file to the root of the Astro project: - -```ini title=".dev.vars" -DB_PASSWORD=myPassword -``` - -You can then access environment variables, including secrets, from the `env` object available from `Astro.locals.runtime`: +Access the Cloudflare [`ExecutionContext`](https://developers.cloudflare.com/workers/runtime-apis/context/) through `Astro.locals.cfContext`. This is useful for operations like [`waitUntil()`](https://developers.cloudflare.com/workers/runtime-apis/context/#waituntil), or accessing [Durable Object exports](https://developers.cloudflare.com/workers/runtime-apis/context/#exports) within your page. ```astro title="src/pages/index.astro" --- -const { env } = Astro.locals.runtime; -const myVariable = env.MY_VARIABLE; -const secret = env.DB_PASSWORD; +const cfContext = Astro.locals.cfContext; +cfContext.exports.Greeter.greet('Astro'); +cfContext.waitUntil(someAsyncOperation()); --- ``` -Cloudflare environment variables and secrets are compatible with the [`astro:env` API](/en/guides/environment-variables/#type-safe-environment-variables). - ### Typing -`wrangler` provides a `types` command to generate TypeScript types for the bindings. This allows you to type locals without the need to manually type them. Refer to the [Cloudflare documentation](https://developers.cloudflare.com/workers/wrangler/commands/#types) for more information. +`wrangler` provides a [`types`](https://developers.cloudflare.com/workers/wrangler/commands/#types) command to generate TypeScript types for your bindings. This allows you to type your environment without the need for manual type definitions. -Every time you change your configuration files (e.g. `wrangler.toml`, `.dev.vars`) you need to run `wrangler types`. +Run `wrangler types` every time you change your configuration files (e.g. `wrangler.jsonc`, `.dev.vars`). :::note -You can create a pnpm script to run `wrangler types` automatically before other commands. +The following example shows a script configuration to run `wrangler types` automatically before other commands: ```json title="package.json" { @@ -457,117 +291,48 @@ You can create a pnpm script to run `wrangler types` automatically before other } } ``` -::: - -You can type the `runtime` object by [extending global types](/en/guides/typescript/#extending-global-types) using `Runtime`: -```ts title="src/env.d.ts" -type Runtime = import('@astrojs/cloudflare').Runtime; - -declare namespace App { - interface Locals extends Runtime { - otherLocals: { - test: string; - }; - } -} -``` +::: ## Cloudflare Platform ### Headers -You can attach [custom headers](https://developers.cloudflare.com/pages/platform/headers/) to your responses by adding a `_headers` file in your Astro project's `public/` folder. This file will be copied to your build output directory. - -This is available on Cloudflare Workers and Pages. +Add [custom headers](https://developers.cloudflare.com/workers/static-assets/headers/) for static assets by creating a `_headers` file in your Astro project's `public/` folder. This file will be copied to the build output directory. Headers in `_headers` are not applied to responses generated by your Worker code. ### Assets -Assets built by Astro are all named with a hash and therefore can be given long cache headers. By default, Astro on Cloudflare will add such a header for these files. -### Redirects +Assets built by Astro are all named with a hash and, therefore, can be given long cache headers. By default, Astro on Cloudflare will add such a header for these files. -You can declare [custom redirects](https://developers.cloudflare.com/pages/platform/redirects/) to redirect requests to a different URL. To do so, add a `_redirects` file in your Astro project's `public/` folder. This file will be copied to your build output directory. +### Redirects -This is available on Cloudflare Workers and Pages. +Declare [custom redirects for static assets](https://developers.cloudflare.com/workers/static-assets/redirects/) by adding a `_redirects` file in your Astro project's `public/` folder. This file will be copied to your build output directory. For dynamic routes, [configure redirects in Astro directly](/en/guides/routing/#configured-redirects) instead. ### Routes -#### Routing on Cloudflare Workers Routing for static assets is based on the file structure in the build directory (e.g. `./dist`). If no match is found, this will fall back to the Worker for on-demand rendering. Read more about [static asset routing with Cloudflare Workers](https://developers.cloudflare.com/workers/static-assets/routing/). -Unlike [Cloudflare Pages](#routing-on-cloudflare-pages), with Workers, you do not need a `_routes.json` file. - -Currently, the Cloudflare adapter always generates this file. To work around this, create a `.assetsignore` file in your `public/` folder, and add the following lines to it: - ```txt title="public/.assetsignore" - _worker.js - _routes.json - ``` - -#### Routing on Cloudflare Pages - -For Cloudflare Pages, [routing](https://developers.cloudflare.com/pages/platform/functions/routing/#functions-invocation-routes) uses a `_routes.json` file to determine which requests are routed to the server function and which are served as static assets. By default, a `_routes.json` file will be automatically generated for your project based on its files and configuration. - -You can [specify additional routing patterns to follow](#routesextend) in your adapter config, or create your own custom `_routes.json` file to fully override the automatic generation. - - -Creating a custom `public/_routes.json` will override the automatic generation. See [Cloudflare's documentation on creating a custom `_routes.json`](https://developers.cloudflare.com/pages/platform/functions/routing/#create-a-_routesjson-file) for more details. - ## Sessions -The Astro [Sessions API](/en/guides/sessions/) allows you to easily store user data between requests. This can be used for things like user data and preferences, shopping carts, and authentication credentials. Unlike cookie storage, there are no size limits on the data, and it can be restored on different devices. - -Astro automatically configures [Workers KV](https://developers.cloudflare.com/kv/) for session storage when using the Cloudflare adapter. Before using sessions, you need to create a KV namespace to store the data and configure a KV binding in your Wrangler config file. By default, Astro expects the KV binding to be named `SESSION`, but you can choose a different name if you prefer by setting the [`sessionKVBindingName`](#sessionkvbindingname) option in the adapter config. +The Astro [Sessions API](/en/guides/sessions/) allows you to easily store user data between requests. This can be used for things like user data and preferences, shopping carts, and authentication credentials. Unlike cookie storage, there are no size limits on the data, and it can be restored on different devices. - +Astro automatically configures [Workers KV](https://developers.cloudflare.com/kv/) for session storage when using the Cloudflare adapter. Wrangler can [automatically provision](https://developers.cloudflare.com/workers/wrangler/configuration/#automatic-provisioning) the KV namespace when you deploy, so no manual setup is required. Alternatively, you can define the KV binding manually in your `wrangler.jsonc` file and set a custom binding name using the [`sessionKVBindingName`](#sessionkvbindingname) adapter option. -1. Create a KV namespace using the Wrangler CLI and make note of the ID of the new namespace: - - ```sh - npx wrangler kv namespace create "SESSION" - ``` +```astro title="src/components/CartButton.astro" +--- +export const prerender = false; // Not needed in 'server' mode +const cart = await Astro.session?.get('cart'); +--- -2. Declare the KV namespace in your Wrangler config, setting the namespace ID to the one returned by the previous command: - - - - ```json title="wrangler.jsonc" "" - { - "kv_namespaces": [ - { - "binding": "SESSION", - "id": "" - } - ] - } - ``` - - - ```toml title="wrangler.toml" "" - kv_namespaces = [ - { binding = "SESSION", id = "" } - ] - ``` - - - -3. You can then use sessions in your server code: - - ```astro title="src/components/CartButton.astro" "Astro.session?.get('cart')" - --- - export const prerender = false; - const cart = await Astro.session?.get('cart'); - --- - - 🛒 {cart?.length ?? 0} items - ``` +🛒 {cart?.length ?? 0} items +``` - +By default, the KV binding is named `SESSION`. To use a different name, set the [`sessionKVBindingName`](#sessionkvbindingname) option in the adapter config. :::note Writes to Cloudflare KV are [eventually consistent](https://developers.cloudflare.com/kv/concepts/how-kv-works/#consistency) between regions. This means that changes are available immediately within the same region but may take up to 60 seconds to propagate globally. This won't affect most users as they are unlikely to switch regions between requests, but it may be a consideration for some use cases, such as VPN users. ::: - ## Cloudflare Module Imports The Cloudflare `workerd` runtime supports imports of some [non-standard module types](https://developers.cloudflare.com/workers/wrangler/bundling/#including-non-javascript-modules). Most additional file types are also available in Astro: @@ -598,68 +363,269 @@ While this example is trivial, Wasm can be used to accelerate computationally in ## Node.js compatibility -Out of the box, Cloudflare does not support the Node.js runtime APIs. With some configuration, Cloudflare does support a subset of the Node.js runtime APIs. You can find supported Node.js runtime APIs in Cloudflare's [documentation](https://developers.cloudflare.com/workers/runtime-apis/nodejs). +Cloudflare Workers support most Node.js runtime APIs through the `nodejs_compat` compatibility flag. This includes commonly used modules like `node:buffer`, `node:crypto`, `node:path`, and many others. See the [full list of supported Node.js APIs](https://developers.cloudflare.com/workers/runtime-apis/nodejs) in Cloudflare's documentation. -To use these APIs, your page or endpoint must be server-side rendered (not pre-rendered) and must use the `import {} from 'node:*'` import syntax. +To enable Node.js compatibility, add the `nodejs_compat` flag to your Wrangler configuration: -```js title="pages/api/endpoint.js" -export const prerender = false; +```jsonc title="wrangler.jsonc" +{ + "compatibility_flags": ["nodejs_compat"], +} +``` + +Then use the `node:*` import syntax in your server-side code: + +```js title="src/pages/api/endpoint.js" +export const prerender = false; // Not needed in 'server' mode import { Buffer } from 'node:buffer'; ``` -You'll also need to modify the `vite` configuration in your Astro config to allow for the `node:*` import syntax: +For Node.js APIs not yet supported in the Workers runtime, Wrangler can inject polyfills (requires `nodejs_compat` and a compatibility date of 2024-09-23 or later). -```js title="astro.config.mjs" ins={6-10} -import {defineConfig} from "astro/config"; -import cloudflare from '@astrojs/cloudflare'; +See the [Cloudflare documentation on Node.js compatibility](https://developers.cloudflare.com/workers/runtime-apis/nodejs/) for the complete list of supported APIs and configuration details. + +## Local preview + +After building your project with `astro build`, use `astro preview` to test your Cloudflare Workers application locally. The preview runs using Cloudflare's `workerd` runtime, closely mirroring production behavior. +### Meaningful error messages + +By default, errors occurring while running your application in Wrangler are minified. For better debugging, add `vite.build.minify = false` to your `astro.config.mjs`: + +```js title="astro.config.mjs" ins={3-7} export default defineConfig({ - adapter: cloudflare({}), + adapter: cloudflare(), vite: { - ssr: { - external: ['node:buffer'], - }, - }, -}) + build: { + minify: false, + }, + }, +}); ``` -Additionally, you'll need to follow Cloudflare's documentation on how to enable support. For detailed guidance, please refer to the [Cloudflare documentation on enabling Node.js compatibility](https://developers.cloudflare.com/workers/runtime-apis/nodejs/). +## Using with Cloudflare Pages + +The Cloudflare adapter deploys to Cloudflare Workers by default. To deploy to Cloudflare Pages instead, additional manual configuration is required. -:::note[Package Compatibility Implications] -If a project imports a package into the server that uses the Node.js runtime APIs, this can cause issues when deploying to Cloudflare. This issue arises with package that do not use the `node:*` import syntax. It is recommended that you contact the authors of the package to determine if the package supports the above import syntax. If the package does not support this, you may need to use a different package. +:::caution +Cloudflare recommends using Workers for new projects. See Cloudflare's [migration guide from Pages to Workers](https://developers.cloudflare.com/workers/static-assets/migration-guides/migrate-from-pages/) for more information if you have an existing project using Pages. ::: -## Preview with Wrangler + +1. Configure your build output directories in `astro.config.mjs`: + + ```js title="astro.config.mjs" ins={6-9} + import { defineConfig } from 'astro/config'; + import cloudflare from '@astrojs/cloudflare'; -To use [`wrangler`](https://developers.cloudflare.com/workers/wrangler/) to run your application locally, update the preview script. + export default defineConfig({ + adapter: cloudflare(), + build: { + client: './', + server: './_worker.js', + }, + }); + ``` -For Workers: +2. Create a `_routes.json` file in your `public/` folder to route requests to your Worker: -```json title="package.json" -"preview": "wrangler dev" + ```jsonc title="public/_routes.json" + { + "version": 1, + "include": ["/*"], // Alternatively, specify specific routes + "exclude": ["/static/*"] // Exclude static assets if needed + } + ``` + + See the [Cloudflare documentation on routing with Pages](https://developers.cloudflare.com/pages/functions/routing/#create-a-_routesjson-file) for more details. + + +## Upgrading to v13 and Astro 6 + +Astro 6 brings significant improvements to the Cloudflare development experience and requires `@astrojs/cloudflare` v13 or later. Now, `astro dev` uses Cloudflare's Vite plugin and `workerd` runtime to closely mirror production behavior. + +See [the Astro 6 upgrade guide](/en/guides/upgrade-to/v6/) for full instructions on upgrading Astro itself. + +### Development server now uses workerd + +The biggest change for Cloudflare users in Astro 6 is that `astro dev` and `astro preview` now use the Cloudflare Vite plugin to run your site using the real Workers runtime (`workerd`) instead of Node.js. This means your development environment is now a much closer replica of your production environment, with the same runtime, APIs, and behavior. + +This change helps you catch issues during development that would have previously only appeared in production, and features like Durable Objects, R2 bindings, and Workers AI now work exactly as they do when deployed to Cloudflare's platform. + +This change is transparent for most projects. If your project had special configuration for `astro dev` or was relying on Node.js-specific behavior in development, adjust your code or configuration accordingly. + +### Changed: Wrangler entrypoint configuration + +Previously, the `main` field in your Wrangler configuration pointed to the built worker file (e.g. `dist/_worker.js/index.js`). With Astro 6, this has changed to point to a new unified entrypoint provided by the Cloudflare adapter: `@astrojs/cloudflare/entrypoints/server`. + +Update your `wrangler.jsonc` to use the new entrypoint: + +```jsonc title="wrangler.jsonc" del={2} ins={3} +{ + "main": "dist/_worker.js/index.js", + "main": "@astrojs/cloudflare/entrypoints/server", + "name": "my-astro-app", + // ... rest of config +} ``` -For Pages: +This single entrypoint handles both `astro dev` and production deployments. -```json title="package.json" -"preview": "wrangler pages dev ./dist" +### Removed: `Astro.locals.runtime` API + +The `Astro.locals.runtime` object has been removed in favor of direct access to Cloudflare Workers APIs. Access environment variables, the `cf` object, caches, and execution context directly through the provided interfaces. + +**Accessing environment variables:** + +Previously, environment variables were accessed through `Astro.locals.runtime.env`. Now import `env` directly instead: + +```js del={1} ins={2} +const { env } = Astro.locals.runtime; +import { env } from 'cloudflare:workers'; ``` -Developing with [`wrangler`](https://developers.cloudflare.com/workers/wrangler/) gives you access to [Cloudflare bindings](https://developers.cloudflare.com/pages/platform/functions/bindings), [environment variables](https://developers.cloudflare.com/pages/platform/functions/bindings/#environment-variables), and the [cf object](https://developers.cloudflare.com/workers/runtime-apis/request/#incomingrequestcfproperties). Getting hot reloading of the Astro dev server to work with Wrangler might require custom setup. See [community examples](https://github.com/withastro/roadmap/discussions/590). +**Accessing the `cf` object:** -### Meaningful error messages +Previously, the `cf` object was accessed through `Astro.locals.runtime.cf`. Now access it directly from the request: -Currently, errors during running your application in Wrangler are not very useful, due to the minification of your code. For better debugging, you can add `vite.build.minify = false` setting to your `astro.config.mjs`. +```js del={1} ins={2} +const { cf } = Astro.locals.runtime; +const cf = Astro.request.cf; +``` -```js title="astro.config.mjs" ins={3-7} -export default defineConfig({ - adapter: cloudflare(), - vite: { - build: { - minify: false, - }, +**Accessing the caches API:** + +Previously, the caches API was accessed through `Astro.locals.runtime.caches`. Now use the global `caches` object directly: + +```js del={1} +const { caches } = Astro.locals.runtime; + +caches.default.put(request, response); +``` + +**Accessing the execution context:** + +The `Astro.locals.runtime.ctx` object is replaced with `Astro.locals.cfContext`, which contains the Cloudflare `ExecutionContext`: + +```js del={1} ins={2} +const ctx = Astro.locals.runtime.ctx; +const ctx = Astro.locals.cfContext; +``` + +### Changed: Wrangler configuration file is now optional + +The Wrangler configuration file is now optional for simple projects. If you don't have custom configuration, such as Cloudflare bindings (KV, D1, Durable Objects, etc.), Astro will automatically generate a default configuration for you. + +If your `wrangler.jsonc` only contains basic configuration like this: + +```jsonc +{ + "main": "@astrojs/cloudflare/entrypoints/server", + "compatibility_date": "2025-05-21", + "assets": { + "directory": "./dist", + "binding": "ASSETS", }, +} +``` + +You can safely delete this file. Astro handles this configuration automatically. Alternatively, create a minimal `wrangler.jsonc` with just your project name and other custom settings: + +```jsonc title="wrangler.jsonc" +{ + "name": "my-astro-app", +} +``` + +### Changed: Custom entrypoint API + +If you were using a custom `workerEntryPoint` configuration in the adapter options, this has been removed. Instead, specify your custom entrypoint in your Wrangler configuration and create a standard Cloudflare Worker export object directly, rather than using the `createExports()` function. + + +1. Remove the `workerEntryPoint` option from your adapter config: + + ```js title="astro.config.mjs" del={6-9} + import { defineConfig } from 'astro/config'; + import cloudflare from '@astrojs/cloudflare'; + + export default defineConfig({ + adapter: cloudflare({ + workerEntryPoint: { + path: 'src/worker.ts', + namedExports: ['MyDurableObject'], + }, + }), + }); + ``` + +2. Specify the entrypoint in `wrangler.jsonc` instead: + + ```jsonc title="wrangler.jsonc" + { + "main": "./src/worker.ts" + } + ``` + +3. Update your custom worker entry file to use standard Worker syntax. Import the handler from `@astrojs/cloudflare/handler` and export a standard Cloudflare Worker object, alongside any custom exports like Durable Objects: + + ```ts title="src/worker.ts" + import { handle } from '@astrojs/cloudflare/handler'; + import { DurableObject } from 'cloudflare:workers'; + + export class MyDurableObject extends DurableObject { + // ... + } + + export default { + async fetch(request, env, ctx) { + await env.MY_QUEUE.send('log'); + return handle(request, env, ctx); + }, + async queue(batch, _env) { + let messages = JSON.stringify(batch.messages); + console.log(`consumed from our queue: ${messages}`); + }, + } satisfies ExportedHandler; + ``` + + +The manifest is now created internally by the adapter, so it does not need to be passed to your handler. + +### Removed: `cloudflareModules` option + +The `cloudflareModules` adapter option has been removed because it is no longer necessary. Cloudflare natively supports importing `.sql`, `.wasm`, and other module types. + +Remove the `cloudflareModules` option from your Cloudflare adapter configuration if you were using it: + +```ts title="astro.config.mjs" del={5} +import cloudflare from '@astrojs/cloudflare'; + +export default defineConfig({ + adapter: cloudflare({ + cloudflareModules: true + }) }); ``` + +### New: `astro preview` support + +Use `astro preview` to test your Cloudflare Workers application locally before deploying. The preview runs using Cloudflare's `workerd` runtime, closely mirroring production behavior. Run `astro build` followed by `astro preview` to start the preview server. + +### Deprecated: Cloudflare Pages support + +The Astro Cloudflare adapter now only supports deploying to Cloudflare Workers by default. If you are currently deploying to Cloudflare Pages, consider migrating to Workers for the best experience and feature support. + +See Cloudflare's [migration guide from Pages to Workers](https://developers.cloudflare.com/workers/static-assets/migration-guides/migrate-from-pages/) for detailed migration instructions. + +If you need to continue using Cloudflare Pages, see [Using with Cloudflare Pages](#using-with-cloudflare-pages) for the required manual configuration. + +### Changed: `imageService` default + +The default value of `imageService` has changed from `'compile'` to `'cloudflare-binding'` for an improved experience when working with images. + +The `cloudflare-binding` service uses the [Cloudflare Images binding](https://developers.cloudflare.com/images/transform-images/bindings/) to transform images at runtime, and the binding is automatically provisioned when you deploy. + +To revert to the previous behavior, where image transformation was only available on prerendered routes at build time, set `imageService: 'compile'` explicitly in your adapter config. + [astro-integration]: /en/guides/integrations-guide/ diff --git a/src/content/docs/en/guides/integrations-guide/markdoc.mdx b/src/content/docs/en/guides/integrations-guide/markdoc.mdx index eb2307ce5acdd..f2e6f91527758 100644 --- a/src/content/docs/en/guides/integrations-guide/markdoc.mdx +++ b/src/content/docs/en/guides/integrations-guide/markdoc.mdx @@ -113,7 +113,7 @@ Markdoc files can only be used within content collections. Add entries to any co - quick-start.mdoc -Then, query your collection using the [Content Collection APIs](/en/guides/content-collections/#querying-collections): +Then, [query and display your posts and collections](/en/guides/content-collections/#querying-build-time-collections): ```astro title="src/pages/why-markdoc.astro" --- diff --git a/src/content/docs/en/guides/integrations-guide/mdx.mdx b/src/content/docs/en/guides/integrations-guide/mdx.mdx index 56cd324fb1165..c799f48f3ca69 100644 --- a/src/content/docs/en/guides/integrations-guide/mdx.mdx +++ b/src/content/docs/en/guides/integrations-guide/mdx.mdx @@ -96,9 +96,9 @@ It also adds extra features to standard MDX, including support for Markdown-styl `.mdx` files must be written in [MDX syntax](https://mdxjs.com/docs/what-is-mdx/#mdx-syntax) rather than Astro’s HTML-like syntax. -### Using MDX with content collections +### Using local MDX with content collections -To include MDX files in a content collection, make sure that your [collection loader](/en/guides/content-collections/#defining-the-collection-loader) is configured to load content from `.mdx` files: +To include your local MDX files in a content collection, make sure that your [collection loader](/en/guides/content-collections/#build-time-collection-loaders) is configured to load content from `.mdx` files: ```js title="src/content.config.ts" ins="mdx" import { defineCollection } from 'astro:content'; diff --git a/src/content/docs/en/guides/integrations-guide/netlify.mdx b/src/content/docs/en/guides/integrations-guide/netlify.mdx index 7c72f01a5ee39..f1c150836cbf6 100644 --- a/src/content/docs/en/guides/integrations-guide/netlify.mdx +++ b/src/content/docs/en/guides/integrations-guide/netlify.mdx @@ -98,9 +98,9 @@ The [Netlify Blog post on Astro](https://www.netlify.com/blog/how-to-deploy-astr ### Running Astro middleware on Netlify Edge Functions -Any Astro middleware is applied to pre-rendered pages at build-time, and to on-demand-rendered pages at runtime. +By default, Astro middleware is applied to pre-rendered pages at build-time and to on-demand-rendered pages at runtime. -To implement redirects, access control, or custom response headers for pre-rendered pages, run your middleware on Netlify Edge Functions by enabling the [`edgeMiddleware` option](/en/reference/adapter-reference/#edgemiddleware): +To implement redirects, access control, or custom response headers for pre-rendered pages, run your middleware on Netlify Edge Functions by setting the [`middlewareMode` option](/en/reference/adapter-reference/#middlewaremode) to `edge`: ```js title="astro.config.mjs" ins={7} import { defineConfig } from 'astro/config'; @@ -109,12 +109,12 @@ import netlify from '@astrojs/netlify'; export default defineConfig({ // ... adapter: netlify({ - edgeMiddleware: true, + middlewareMode: 'edge', }), }); ``` -When `edgeMiddleware` is enabled, an edge function will execute your middleware code for all requests including static assets, prerendered pages, and on-demand rendered pages. +When `middlewareMode` is set to `'edge'`, an edge function will execute your middleware code for all requests, including static assets, prerendered pages, and on-demand rendered pages. For on-demand rendered pages, the `context.locals` object is serialized using JSON and sent in a header for the serverless function, which performs the rendering. As a security measure, the serverless function will refuse to serve requests with a `403 Forbidden` response unless they come from the generated edge function. @@ -408,39 +408,34 @@ Injects environment variables from your Netlify site into the development enviro This allows you to use the same values in development as you would in production. See [the Netlify docs on environment variables](https://docs.netlify.com/build/environment-variables/overview/) for more information, including how to use different variables for different environments. -## Experimental features - -The following features are also available for use, but may be subject to breaking changes in future updates. Please follow the [`@astrojs/netlify` CHANGELOG](https://github.com/withastro/astro/tree/main/packages/integrations/netlify/CHANGELOG.md) carefully for updates if you are using these features in your project. - -### `experimentalStaticHeaders` +### `staticHeaders`

**Type:** `boolean`
**Default:** `false`
- +

Enables specifying custom headers for prerendered pages in Netlify's configuration. If enabled, the adapter will save [static headers in the Framework API config file](https://docs.netlify.com/frameworks-api/#headers) when provided by Astro features, such as Content Security Policy. -For example, when [experimental Content Security Policy](/en/reference/experimental-flags/csp/) is enabled, `experimentalStaticHeaders` can be used to add the CSP `headers` to your Netlify configuration, instead of creating a `` element: +For example, when [Content Security Policy](/en/reference/configuration-reference/#securitycsp) is enabled, `staticHeaders` can be used to add the CSP `headers` to your Netlify configuration, instead of creating a `` element: ```js title="astro.config.mjs" {9} import { defineConfig } from 'astro/config'; import netlify from '@astrojs/netlify'; export default defineConfig({ - experimental: { + security: { csp: true }, adapter: netlify({ - experimentalStaticHeaders: true + staticHeaders: true }) }); ``` - ## Examples * The [Astro Netlify Edge Starter](https://github.com/sarahetter/astro-netlify-edge-starter) provides an example and a guide in the README. diff --git a/src/content/docs/en/guides/integrations-guide/node.mdx b/src/content/docs/en/guides/integrations-guide/node.mdx index a794acf66582e..aab128b0774d8 100644 --- a/src/content/docs/en/guides/integrations-guide/node.mdx +++ b/src/content/docs/en/guides/integrations-guide/node.mdx @@ -108,84 +108,80 @@ export default defineConfig({ }); ``` -### `experimentalDisableStreaming` +### `staticHeaders`

-**Type:** `boolean`
-**Default:** `false`
- + **Type:** `boolean`
+ **Default:** `false`
+

-Disables Astro's default [HTML streaming](/en/guides/on-demand-rendering/#html-streaming) for pages rendered on demand. - -HTML streaming helps with performance and generally provides a better visitor experience. In most cases, disabling streaming is not recommended. +If enabled, the adapter will serve the headers of prerendered pages using the `Response` object when provided by Astro features, such as Content Security Policy. -However, when you need to disable HTML streaming (e.g. your host only supports non-streamed HTML caching at the CDN level), you can opt out of the default behavior: +For example, when [Content Security Policy](/en/reference/configuration-reference/#securitycsp) is enabled, `staticHeaders` can be used to add the CSP headers to the `Response` object instead of creating a `` element: -```js title="astro.config.mjs" {7} +```js title="astro.config.mjs" {10} import { defineConfig } from 'astro/config'; import node from '@astrojs/node'; export default defineConfig({ + security: { + csp: true + }, adapter: node({ mode: 'standalone', - experimentalDisableStreaming: true, - }), + staticHeaders: true, + }) }); ``` -### `experimentalStaticHeaders` +### `experimentalDisableStreaming`

- **Type:** `boolean`
- **Default:** `false`
- +**Type:** `boolean`
+**Default:** `false`
+

-If enabled, the adapter will serve the headers of prerendered pages using the `Response` object when provided by Astro features, such as Content Security Policy. +Disables Astro's default [HTML streaming](/en/guides/on-demand-rendering/#html-streaming) for pages rendered on demand. -For example, when [experimental Content Security Policy](/en/reference/experimental-flags/csp/) is enabled, `experimentalStaticHeaders` can be used to add the CSP headers to the `Response` object instead of creating a `` element: +HTML streaming helps with performance and generally provides a better visitor experience. In most cases, disabling streaming is not recommended. -```js title="astro.config.mjs" {10} +However, when you need to disable HTML streaming (e.g. your host only supports non-streamed HTML caching at the CDN level), you can opt out of the default behavior: + +```js title="astro.config.mjs" {7} import { defineConfig } from 'astro/config'; import node from '@astrojs/node'; export default defineConfig({ - experimental: { - csp: true - }, adapter: node({ mode: 'standalone', - experimentalStaticHeaders: true, - }) + experimentalDisableStreaming: true, + }), }); ``` -### `experimentalErrorPageHost` +### `bodySizeLimit`

- **Type:** `string | URL`
- **Default:** `undefined`
- +**Type:** `number`
+**Default:** `1073741824` (1 GB)
+

-Specifies an alternate host for loading prerendered [custom error pages](/en/basics/astro-pages/#custom-404-error-page). - -Astro needs to be able to load your 404 page in order to return it in a response. By default, Astro will load prerendered custom error pages from the same host as the one that the request is made to. For example, if a request is made to `https://example.com/nonexistent-page`, Astro will attempt to load the prerendered error page from `https://example.com/404.html`. +Sets the maximum allowed request body size in bytes. When the body of an incoming request exceeds this limit, an error will be thrown when the body is consumed. -Use `experimentalErrorPageHost` when your custom error page must be loaded from a different host, such as when the server is running behind a reverse proxy or in a container that may not have access to the external host URL. You can also use this when it is more efficient to load the prerendered error page from localhost rather than via the public internet. +Set to `Infinity` or `0` to disable the limit entirely. This may be useful if you need to accept very large request bodies, such as for video uploads. -The value can be a string or a URL object. It must be a fully-qualified URL, including the protocol (e.g., `http://localhost:4321`). Astro will always load the prerendered error page from the root path, and any path or query parameters will be ignored. - -```js +```js title="astro.config.mjs" {7} import { defineConfig } from 'astro/config'; import node from '@astrojs/node'; export default defineConfig({ adapter: node({ - // Load pages from localhost, not the public URL. - experimentalErrorPageHost: 'http://localhost:4321', - }) + mode: 'standalone', + bodySizeLimit: 5 * 1024 * 1024 * 1024, // 5 GB + }), }); ``` diff --git a/src/content/docs/en/guides/integrations-guide/sitemap.mdx b/src/content/docs/en/guides/integrations-guide/sitemap.mdx index 7e534abb62e4d..420df3b6303b2 100644 --- a/src/content/docs/en/guides/integrations-guide/sitemap.mdx +++ b/src/content/docs/en/guides/integrations-guide/sitemap.mdx @@ -193,14 +193,14 @@ export default defineConfig({ }); ``` -### `filter` +### `filter()`

**Type:** `(page: string) => boolean`

-All pages are included in your sitemap by default. By adding a custom `filter` function, you can filter included pages by URL. +All pages are included in your sitemap by default. By adding a custom `filter()` function, you can filter included pages by URL. ```js title="astro.config.mjs" ins={8} import { defineConfig } from 'astro/config'; @@ -318,7 +318,7 @@ export default defineConfig({

-**Type:** `{ changefreq?: ChangeFreq; lastmod?: Date; priority?: number; }` +**Type:** \{ changefreq?: ChangeFreq; lastmod?: Date; priority?: number; \}

@@ -346,34 +346,20 @@ export default defineConfig({ }); ``` -### `serialize` +### `serialize()`

-**Type:** `(item: SitemapItem) => SitemapItem | Promise | undefined` +**Type:** (item: SitemapItem) => SitemapItem | Promise\ | undefined

-A function called for each sitemap entry just before writing to a disk. This function can be asynchronous. +Generates an editable representation of each sitemap entry before returning either a [`SitemapItem`](#sitemapitem) or `undefined` to remove it from the sitemap. This function can be asynchronous and is called for each sitemap entry just before writing to disk. -It receives as its parameter a `SitemapItem` object that can have these properties: - -* `url` (absolute page URL). This is the only property that is guaranteed to be on `SitemapItem`. -* `changefreq` -* `lastmod` (ISO formatted date, `String` type) -* `priority` -* `links`. - -This `links` property contains a `LinkItem` list of alternate pages including a parent page. - -The `LinkItem` type has two fields: `url` (the fully-qualified URL for the version of this page for the specified language) and `lang` (a supported language code targeted by this version of the page). - -The `serialize` function should return `SitemapItem`, touched or not. - -The example below shows the ability to add sitemap specific properties individually. +The following example filters a page from the sitemap and updates a specific entry to modify its `changefreq`, `lastmod`, and `priority` properties: ```js title="astro.config.mjs" ins={8-18} import { defineConfig } from 'astro/config'; -import sitemap from '@astrojs/sitemap'; +import sitemap, { ChangeFreqEnum } from '@astrojs/sitemap'; export default defineConfig({ site: 'https://example.com', @@ -384,8 +370,8 @@ export default defineConfig({ return undefined; } if (/your-special-page/.test(item.url)) { - item.changefreq = 'daily'; - item.lastmod = new Date(); + item.changefreq = ChangeFreqEnum.DAILY; + item.lastmod = new Date().toISOString(); item.priority = 0.9; } return item; @@ -399,13 +385,13 @@ export default defineConfig({

-**Type:** `Record SitemapItem | undefined>` +**Type:** Record\SitemapItem) => SitemapItem | undefined\>

A map of functions that allows you to split your sitemap into multiple files based on custom logic. Each key in the object becomes the name of a separate sitemap file, and its corresponding function determines which URLs will be included in that chunk. This can be useful for instance if a specific section of your website changes very often and you'd like to specify a different change frequency for its entries. -Each chunk function receives a `SitemapItem` and for each item returns either: +Each chunk function receives a [`SitemapItem`](#sitemapitem) and for each item returns either: * the modified `SitemapItem`, if the URL should be included in this chunk * `undefined`, if the URL should not be included in this chunk @@ -413,7 +399,7 @@ The example below shows how to split URLs into different sitemap files based on ```js title="astro.config.mjs" ins={8-25} import { defineConfig } from 'astro/config'; -import sitemap from '@astrojs/sitemap'; +import sitemap, { ChangeFreqEnum } from '@astrojs/sitemap'; export default defineConfig({ site: 'https://example.com', @@ -422,16 +408,16 @@ export default defineConfig({ chunks: { 'blog': (item) => { if (/blog/.test(item.url)) { - item.changefreq = 'weekly'; - item.lastmod = new Date(); + item.changefreq = ChangeFreqEnum.WEEKLY; + item.lastmod = new Date().toISOString(); item.priority = 0.9; return item; } }, 'glossary': (item) => { if (/glossary/.test(item.url)) { - item.changefreq = 'monthly'; - item.lastmod = new Date(); + item.changefreq = ChangeFreqEnum.MONTHLY; + item.lastmod = new Date().toISOString(); item.priority = 0.7; return item; } @@ -602,6 +588,164 @@ export default defineConfig({ }); ``` +## Astro Sitemap utilities reference + +```ts +import { + ChangeFreqEnum, +} from "@astrojs/sitemap"; +``` + +### `ChangeFreqEnum` + +

+ + +

+ +A [Typescript enumeration](https://www.typescriptlang.org/docs/handbook/enums.html) where each key is the uppercase version of a valid value defined in the [specification of ``](https://www.sitemaps.org/protocol.html#changefreqdef). + +The following example uses `serialize()` to update the [`changefreq`](#sitemapitemchangefreq) of the blog index: + +```js title="astro.config.mjs" {10} "ChangeFreqEnum" +import { defineConfig } from 'astro/config'; +import sitemap, { ChangeFreqEnum } from '@astrojs/sitemap'; + +export default defineConfig({ + site: 'https://example.com', + integrations: [ + sitemap({ + serialize(item) { + if (/blog/.test(item.url)) { + item.changefreq = ChangeFreqEnum.DAILY; + } + + return item; + }, + }), + ], +}); +``` + +## Astro Sitemap types reference + +```ts +import type { + ChangeFreq, + LinkItem, + SitemapItem, + SitemapOptions, +} from "@astrojs/sitemap"; +``` + +### `ChangeFreq` + +

+ +**Type:** `"daily" | "monthly" | "always" | "hourly" | "weekly" | "yearly" | "never"` +

+ +A union of valid values to specify the update frequency of an entry. + +### `LinkItem` + +

+ +**Type:** `{ lang: string; hreflang?: string; url: string; }` +

+ +Describes the URL of a page. This could be the default version of the document or one of its translations. + +#### `LinkItem.lang` + +

+ +**Type:** `string` +

+ +Specifies the language code supported by this version of the page. When a value is set, you do not need to also set [`hreflang`](#linkitemhreflang). + +#### `LinkItem.hreflang` + +

+ +**Type:** `string` +

+ +Specifies the language code supported by this version of the page. When a value is set, you do not need to also set [`lang`](#linkitemlang). + +#### `LinkItem.url` + +

+ +**Type:** `string` +

+ +Specifies the absolute URL of the page for the specified language. + +### `SitemapItem` + +

+ +**Type:** `{ url: string; lastmod?: string | undefined; changefreq?: ChangeFreqEnum | undefined; priority?: number | undefined; links?: LinkItem[] | undefined; }` +

+ +Describes an entry in a sitemap. This contains its `url` and additional optional properties. + +#### `SitemapItem.url` + +

+ +**Type:** `string` +

+ +Specifies the absolute page URL. + +#### `SitemapItem.lastmod` + +

+ +**Type:** `string | undefined` +

+ +Defines the ISO formatted date of last modification of the page as a string. + +#### `SitemapItem.changefreq` + +

+ +**Type:** ChangeFreqEnum | undefined +

+ +Defines how frequently the page is likely to change. + +#### `SitemapItem.priority` + +

+ +**Type:** `number | undefined` +

+ +Defines the priority of this URL relative to other URLs on your site. The value should be a number in the range from `0.0` to `1.0`. + +#### `SitemapItem.links` + +

+ +**Type:** LinkItem[] | undefined +

+ +Defines a list of alternate pages, including the current page. + +### `SitemapOptions` + +

+ +**Type:** `object` +

+ +Describes the [configuration options](#configuration). + ## Examples * The official Astro website uses Astro Sitemap to generate [its sitemap](https://astro.build/sitemap-index.xml). diff --git a/src/content/docs/en/guides/integrations-guide/vercel.mdx b/src/content/docs/en/guides/integrations-guide/vercel.mdx index c101b4e3d89dc..ba4e15839495c 100644 --- a/src/content/docs/en/guides/integrations-guide/vercel.mdx +++ b/src/content/docs/en/guides/integrations-guide/vercel.mdx @@ -411,22 +411,51 @@ export default defineConfig({ }); ``` +### `staticHeaders` + +

+**Type:** `boolean`
+**Default:** `false`
+**Available for:** Serverless
+ +

+ +Enables specifying custom headers for prerendered pages in Vercel's configuration. + +If enabled, the adapter will save [static headers in the Vercel `vercel.json` file](https://vercel.com/docs/project-configuration#headers) when provided by Astro features, such as Content Security Policy. + +For example, when [Content Security Policy](/en/reference/configuration-reference/#securitycsp) is enabled, `staticHeaders` can be used to add the CSP `headers` to your Vercel configuration, instead of creating a `` element: + +```js title="astro.config.mjs" {9} +import { defineConfig } from 'astro/config'; +import vercel from '@astrojs/vercel'; + +export default defineConfig({ + security: { + csp: true + }, + adapter: vercel({ + staticHeaders: true + }) +}); +``` + ### Running Astro middleware on Vercel Edge Functions -The `@astrojs/vercel` adapter can create an [edge function](https://vercel.com/docs/functions/edge-functions) from an Astro middleware in your code base. When `edgeMiddleware` is enabled, an edge function will execute your middleware code for all requests including static assets, prerendered pages, and on-demand rendered pages. +The `@astrojs/vercel` adapter can create an [edge function](https://vercel.com/docs/functions/edge-functions) from an Astro middleware in your code base. When [`middlewareMode`](/en/reference/adapter-reference/#middlewaremode) is set to `'edge'`, an edge function will execute your middleware code for all requests, including static assets, prerendered pages, and on-demand rendered pages. For on-demand rendered pages, the `context.locals` object is serialized using JSON and sent in a header for the serverless function, which performs the rendering. As a security measure, the serverless function will refuse to serve requests with a `403 Forbidden` response unless they come from the generated edge function. -This is an opt-in feature. To enable it, set `edgeMiddleware` to `true`: +This is an opt-in feature. To enable it, set `middlewareMode` to `'edge'`: -```js title="astro.config.mjs" "edgeMiddleware: true" +```js title="astro.config.mjs" "middlewareMode: 'edge'" import { defineConfig } from 'astro/config'; import vercel from '@astrojs/vercel'; export default defineConfig({ // ... adapter: vercel({ - edgeMiddleware: true, + middlewareMode: 'edge', }), }); ``` @@ -503,37 +532,5 @@ The `@astrojs/vercel` adapter supports specific Node.js versions for deploying y Check out the [Vercel documentation](https://vercel.com/docs/functions/serverless-functions/runtimes/node-js#default-and-available-versions) to learn more. -## Experimental features - -The following features are also available for use, but may be subject to breaking changes in future updates. Please follow the [`@astrojs/vercel` CHANGELOG](https://github.com/withastro/astro/tree/main/packages/integrations/vercel/CHANGELOG.md) carefully for updates if you are using these features in your project. - -### `experimentalStaticHeaders` - -

-**Type:** `boolean`
-**Default:** `false`
-**Available for:** Serverless
- -

- -Enables specifying custom headers for prerendered pages in Vercel's configuration. - -If enabled, the adapter will save [static headers in the Vercel `vercel.json` file](https://vercel.com/docs/project-configuration#headers) when provided by Astro features, such as Content Security Policy. - -For example, when [experimental Content Security Policy](/en/reference/experimental-flags/csp/) is enabled, `experimentalStaticHeaders` can be used to add the CSP `headers` to your Vercel configuration, instead of creating a `` element: - -```js title="astro.config.mjs" {9} -import { defineConfig } from 'astro/config'; -import vercel from '@astrojs/vercel'; - -export default defineConfig({ - experimental: { - csp: true - }, - adapter: vercel({ - experimentalStaticHeaders: true - }) -}); -``` [astro-integration]: /en/guides/integrations-guide/ diff --git a/src/content/docs/en/guides/internationalization.mdx b/src/content/docs/en/guides/internationalization.mdx index 3180d449571b5..7fc1b1a3d3174 100644 --- a/src/content/docs/en/guides/internationalization.mdx +++ b/src/content/docs/en/guides/internationalization.mdx @@ -170,15 +170,11 @@ Set this option when all routes will have their `/locale/` prefix in their URL a - URLs without a locale prefix, (e.g. `example.com/about/`) will return a 404 (not found) status code unless you specify a [fallback strategy](#fallback). -### `redirectToDefaultLocale` +#### Opting out of redirects for the home URL -

+Even with your default locale routes prefixed, this behaviour does not apply by default to your site's index page. This allows you to have a home page that exists outside of your configured locale structure, where all of your localized routes are prefixed except the home URL of your site. -Configures whether or not the home URL (`/`) generated by `src/pages/index.astro` will redirect to `/`. - -Setting `prefixDefaultLocale: true` will also automatically set `redirectToDefaultLocale: true` in your `routing` config object. By default, the required `src/pages/index.astro` file will automatically redirect to the index page of your default locale. - -You can opt out of this behavior by [setting `redirectToDefaultLocale: false`](/en/reference/configuration-reference/#i18nroutingredirecttodefaultlocale). This allows you to have a site home page that exists outside of your configured locale folder structure. +You can opt out of this behavior so that your main site URL will also redirect to a prefixed, localized route for your default locale. When `prefixDefaultLocale: true` is set, you can additionally configure `redirectToDefaultLocale: true`. This will ensure that the home URL (`/`) generated by `src/pages/index.astro` will redirect to `/[defaultLocale]/`. ### `manual` diff --git a/src/content/docs/en/guides/markdown-content.mdx b/src/content/docs/en/guides/markdown-content.mdx index 1a81d7fd47591..e6439ebeaa121 100644 --- a/src/content/docs/en/guides/markdown-content.mdx +++ b/src/content/docs/en/guides/markdown-content.mdx @@ -401,7 +401,7 @@ When using the frontmatter `layout` property, you must include the ` + ``` @@ -405,7 +405,7 @@ const randomUser = data.results[0]; --- ``` -See more about local files imports with [`import.meta.glob()`](/en/guides/imports/#importmetaglob), [querying using the Collections API](/en/guides/content-collections/#querying-collections) or [fetching remote data](/en/guides/data-fetching/). +See more about local files imports with [`import.meta.glob()`](/en/guides/imports/#importmetaglob), [querying with content collections](/en/guides/content-collections/#querying-build-time-collections) or [fetching remote data](/en/guides/data-fetching/). ### Next Styling to Astro diff --git a/src/content/docs/en/guides/migrate-to-astro/from-nuxtjs.mdx b/src/content/docs/en/guides/migrate-to-astro/from-nuxtjs.mdx index 935610c7c83c8..867a81dd46918 100644 --- a/src/content/docs/en/guides/migrate-to-astro/from-nuxtjs.mdx +++ b/src/content/docs/en/guides/migrate-to-astro/from-nuxtjs.mdx @@ -79,7 +79,7 @@ You can pass a `--template` argument to the `create astro` command to start a ne Then, copy your existing Nuxt project files over to your new Astro project in a separate folder outside of `src`. :::tip -Visit https://astro.new for the full list of official starter templates, and links for opening a new project in IDX, StackBlitz, CodeSandbox, or Gitpod. +Visit https://astro.new for the full list of official starter templates, and links for opening a new project in Firebase Studio, StackBlitz, or CodeSandbox. ::: ### Install integrations (optional) @@ -161,7 +161,7 @@ Compare the following Nuxt component and a corresponding Astro component: text-align: center; margin-bottom: 1em; } - ``` @@ -180,10 +180,10 @@ Compare the following Nuxt component and a corresponding Astro component: {message === "Not Found" ?

The repository you're looking up doesn't exist

: <> -
- -