# Astro > astro documentation pack ## [Astro](https://docharvest.github.io/docs/astro/) Contents astro Astro Astro - [Astro Coursesastro-courses](/docs/astro/astro-courses/) - [Contribute to Astrocontribute](/docs/astro/contribute/) - [Develop and builddevelop-and-build](/docs/astro/develop-and-build/) - [Editor setupeditor-setup](/docs/astro/editor-setup/) - [Getting startedgetting-started](/docs/astro/getting-started/) - [Install Astroinstall-and-setup](/docs/astro/install-and-setup/) - [Astro recipesrecipes](/docs/astro/recipes/) - [Upgrade Astroupgrade-astro](/docs/astro/upgrade-astro/) - [Componentsbasics/astro-components](/docs/astro/basics/astro-components/) - [Pagesbasics/astro-pages](/docs/astro/basics/astro-pages/) - [Layoutsbasics/layouts](/docs/astro/basics/layouts/) - [Project structurebasics/project-structure](/docs/astro/basics/project-structure/) - [Islands architectureconcepts/islands](/docs/astro/concepts/islands/) - [Why Astro?concepts/why-astro](/docs/astro/concepts/why-astro/) - [Actionsguides/actions](/docs/astro/guides/actions/) - [Authenticationguides/authentication](/docs/astro/guides/authentication/) - [Use a backend service with Astroguides/backend](/docs/astro/guides/backend/) - [Building Astro sites with AI toolsguides/build-with-ai](/docs/astro/guides/build-with-ai/) - [Route cachingguides/caching](/docs/astro/guides/caching/) - [Scripts and event handlingguides/client-side-scripts](/docs/astro/guides/client-side-scripts/) - [Use a CMS with Astroguides/cms](/docs/astro/guides/cms/) - [Configuration overviewguides/configuring-astro](/docs/astro/guides/configuring-astro/) - [Content collectionsguides/content-collections](/docs/astro/guides/content-collections/) - [Data fetchingguides/data-fetching](/docs/astro/guides/data-fetching/) - [Deploy your Astro Siteguides/deploy](/docs/astro/guides/deploy/) - [Dev toolbarguides/dev-toolbar](/docs/astro/guides/dev-toolbar/) - [E-commerceguides/ecommerce](/docs/astro/guides/ecommerce/) - [Endpointsguides/endpoints](/docs/astro/guides/endpoints/) - [Using environment variablesguides/environment-variables](/docs/astro/guides/environment-variables/) - [Using custom fontsguides/fonts](/docs/astro/guides/fonts/) - [Front-end frameworksguides/framework-components](/docs/astro/guides/framework-components/) - [Imagesguides/images](/docs/astro/guides/images/) - [Imports referenceguides/imports](/docs/astro/guides/imports/) - [Working with integrationsguides/integrations](/docs/astro/guides/integrations/) - [Internationalization (i18n) Routingguides/internationalization](/docs/astro/guides/internationalization/) - [Markdown in Astroguides/markdown-content](/docs/astro/guides/markdown-content/) - [Image and video hosting with Astroguides/media](/docs/astro/guides/media/) - [Middlewareguides/middleware](/docs/astro/guides/middleware/) - [Migrate an existing project to Astroguides/migrate-to-astro](/docs/astro/guides/migrate-to-astro/) - [On-demand renderingguides/on-demand-rendering](/docs/astro/guides/on-demand-rendering/) …and 376 more in the sidebar. [llms.txt](/docs/astro/llms.txt) for agents ## [Astro Courses](https://docharvest.github.io/docs/astro/astro-courses/) Contents astro Astro Courses Astro Astro Courses Want to get started learning Astro with a course or tutorial? You can learn the basics of Astro with our [official docs Build a Blog tutorial](/en/tutorial/0-introduction/), or explore our collection of recommended Astro educational content. ## Education Partners :::tip\[Support Astro while you learn\] Use Astro's affiliate links for discounts with our education partners and some of your purchase goes directly back to funding development of the Astro open source project! ::: Learn Astro from trusted Astro educators, with video lessons, interactive challenges, and projects! ## Community learning resources Learn from your fellow astronauts with curated collections of guides, articles, and blog posts. ## [Components](https://docharvest.github.io/docs/astro/basics/astro-components/) Contents astro Components Astro Components **Astro components** are the basic building blocks of any Astro project. They are HTML-only templating components with no client-side runtime and use the `.astro` file extension. :::note If you know HTML, you already know enough to write your first Astro component. Learn more in the [Astro syntax reference](/en/reference/astro-syntax/). ::: Astro components are extremely flexible. An Astro component can be as small as a snippet of HTML, like a collection of common `` tags that make SEO easy to work with. Components can be reusable UI elements, like a header or a profile card. Astro components can even contain an entire page layout or, when located in the special `src/pages/` folder, be an entire page itself. The most important thing to know about Astro components is that they **don't render on the client**. They render to HTML either at build-time or on-demand. You can include JavaScript code inside of your component frontmatter, and all of it will be stripped from the final page sent to your users' browsers. The result is a faster site, with zero JavaScript footprint added by default. When your Astro component does need client-side interactivity, you can add [standard HTML `
Target here
``` The `.astro` partial must exist at the corresponding file path, and include an export defining the page as a partial: ``` --- ---
I was clicked!
``` See the [htmx documentation](https://htmx.org/docs/) for more details on using htmx. ## [Layouts](https://docharvest.github.io/docs/astro/basics/layouts/) Contents astro Layouts Astro Layouts ## const { title } = Astro.props; # {title} \`\`\` ``` --- ---

My page content, wrapped in a layout!

``` Learn more about [slots](/en/basics/astro-components/#slots). ## Using TypeScript with layouts Any Astro layout can be modified to introduce type safety & autocompletion by providing the types for your props: ``` --- interface Props { title: string; description: string; publishDate: string; viewCount: number; } const { title, description, publishDate, viewCount } = Astro.props; --- {title}

Published on {publishDate}

Viewed by {viewCount} folks

``` ## Markdown Layouts Page layouts are especially useful for individual Markdown pages which otherwise would not have any page formatting. Astro provides a special `layout` frontmatter property intended for [individual `.md` files located within `src/pages/` using file-based routing](/en/guides/markdown-content/#individual-markdown-pages) to specify which `.astro` component to use as the page layout. This component allows you to provide `` content like meta tags (e.g. ``) and styles for the Markdown page. By default, this specified component can automatically access data from the Markdown file. This is not recognized as a special property when using [content collections](/en/guides/content-collections/) to query and render your content. ``` --- layout: ../layouts/BlogPostLayout.astro title: "Hello, World!" author: "Matthew Phillips" date: "09 Aug 2022" --- All frontmatter properties are available as props to an Astro layout component. The `layout` property is the only special one provided by Astro. You can use it in Markdown files located within `src/pages/`. ``` A typical layout for a Markdown page includes: 1. The `frontmatter` prop to access the Markdown page's frontmatter and other data. 2. A default [``](/en/basics/astro-components/#slots) to indicate where the page's Markdown content should be rendered. ``` --- // 1. The frontmatter prop gives access to frontmatter and other data const { frontmatter } = Astro.props; --- {frontmatter.title}

{frontmatter.title} by {frontmatter.author}

Written on: {frontmatter.date}

``` You can set a layout’s [`Props` type](/en/guides/typescript/#component-props) with the `MarkdownLayoutProps` helper: ``` --- type Props = MarkdownLayoutProps<{ // Define frontmatter props here title: string; author: string; date: string; }>; // Now, `frontmatter`, `url`, and other Markdown layout properties // are accessible with type safety const { frontmatter, url } = Astro.props; --- {frontmatter.title}

{frontmatter.title} by {frontmatter.author}

Written on: {frontmatter.date}

``` ### Markdown Layout Props A Markdown layout will have access to the following information via `Astro.props`: - **`file`** - The absolute path of this file (e.g. `/home/user/projects/.../file.md`). - **`url`** - The URL of the page (e.g. `/en/guides/markdown-content`). - **`frontmatter`** - All frontmatter from the Markdown or MDX document. - **`frontmatter.file`** - The same as the top-level `file` property. - **`frontmatter.url`** - The same as the top-level `url` property. - **`headings`** - A list of headings (`h1 -> h6`) in the Markdown or MDX document with associated metadata. This list follows the type: `{ depth: number; slug: string; text: string }[]`. - **`rawContent()`** - A function that returns the raw Markdown document as a string. - **`compiledContent()`** - An async function that returns the Markdown document compiled to an HTML string. :::note A Markdown layout will have access to all the Markdown file's [available properties](/en/guides/markdown-content/#available-properties) from `Astro.props` **with two key differences:** - Heading information (i.e. `h1 -> h6` elements) is available via the `headings` array, rather than a `getHeadings()` function. - `file` and `url` are _also_ available as nested `frontmatter` properties (i.e. `frontmatter.url` and `frontmatter.file`). ::: ### Importing Layouts Manually (MDX) You can also use the special Markdown layout property in the frontmatter of MDX files to pass `frontmatter` and `headings` props directly to a specified layout component in the same way. To pass information to your MDX layout that does not (or cannot) exist in your frontmatter, you can instead import and use a `` component. This works like any other Astro component, and will not receive any props automatically. Pass it any necessary props directly: ``` --- layout: ../../layouts/BaseLayout.astro title: 'My first MDX post' publishDate: '21 September 2022' --- } Welcome to my new Astro blog, using MDX! ``` Then, your values are available to you through `Astro.props` in your layout, and your MDX content will be injected into the page where your `` component is written: ``` --- const { title, fancyJsHelper } = Astro.props; ---

{title}

{fancyJsHelper()}

``` When using any layout (either through the frontmatter `layout` property or by importing a layout), you must include the `` tag in your layout as Astro will no longer add it automatically to your MDX page. Learn more about Astro’s Markdown and MDX support in our [Markdown guide](/en/guides/markdown-content/). ## Nesting Layouts Layout components do not need to contain an entire page worth of HTML. You can break your layouts into smaller components, and combine layout components to create even more flexible, page templates. This pattern is useful when you want to share some code across multiple layouts. For example, a `BlogPostLayout.astro` layout component could style a post's title, date and author. Then, a site-wide `BaseLayout.astro` could handle the rest of your page template, like navigation, footers, SEO meta tags, global styles, and fonts. You can also pass props received from your post to another layout, just like any other nested component. ``` --- // src/layouts/BlogPostLayout.astro const { frontmatter } = Astro.props; ---

{frontmatter.title}

Post author: {frontmatter.author}

``` ## [Project structure](https://docharvest.github.io/docs/astro/basics/project-structure/) Contents astro Project structure Astro Project structure Your new Astro project generated from the `create astro` CLI wizard already includes some files and folders. Others, you will create yourself and add to Astro's existing file structure. Here's how an Astro project is organized, and some files you will find in your new project. ## Directories and Files Astro leverages an opinionated folder layout for your project. Every Astro project root should include the following directories and files: - `src/*` - Your project source code (components, pages, styles, images, etc.) - `public/*` - Your non-code, unprocessed assets (fonts, icons, etc.) - `package.json` - A project manifest. - `astro.config.mjs` - An Astro configuration file. (recommended) - `tsconfig.json` - A TypeScript configuration file. (recommended) ### Example Project Tree A common Astro project directory might look like this: \- public/ - robots.txt - favicon.svg - my-cv.pdf - src/ - blog/ - post1.md - post2.md - post3.md - components/ - Header.astro - Button.jsx - images/ - image1.jpg - image2.jpg - image3.jpg - layouts/ - PostLayout.astro - pages/ - posts/ - \[post\].astro - about.astro - \*\*index.astro\*\* - rss.xml.js - styles/ - global.css - content.config.ts - astro.config.mjs - package.json - tsconfig.json ### `src/` The `src/` folder is where most of your project source code lives. This includes: - [Pages](/en/basics/astro-pages/) - [Layouts](/en/basics/layouts/) - [Astro components](/en/basics/astro-components/) - [UI framework components (React, etc.)](/en/guides/framework-components/) - [Styles (CSS, Sass)](/en/guides/styling/) - [Markdown](/en/guides/markdown-content/) - [Images to be optimized and processed by Astro](/en/guides/images/) Astro processes, optimizes, and bundles your `src/` files to create the final website that is shipped to the browser. Unlike the static `public/` directory, your `src/` files are built and handled for you by Astro. Some files (like Astro components) are not even sent to the browser as written but are instead rendered to static HTML. Other files (like CSS) are sent to the browser but may be optimized or bundled with other CSS files for performance. :::tip While this guide describes some popular conventions used in the Astro community, the only directory reserved by Astro is `src/pages/`. You are free to rename and reorganize any other directories in a way that works best for you. ::: ### `src/pages` Pages routes are created for your site by adding [supported file types](/en/basics/astro-pages/#supported-page-files) to this directory. :::caution `src/pages` is a **required** sub-directory in your Astro project. Without it, your site will have no pages or routes! ::: ### `src/components` **Components** are reusable units of code for your HTML pages. These could be [Astro components](/en/basics/astro-components/), or [UI framework components](/en/guides/framework-components/) like React or Vue. It is common to group and organize all of your project components together in this folder. This is a common convention in Astro projects, but it is not required. Feel free to organize your components however you like! ### `src/layouts` [Layouts](/en/basics/layouts/) are Astro components that define the UI structure shared by one or more [pages](/en/basics/astro-pages/). Just like `src/components`, this directory is a common convention but not required. ### `src/styles` It is a common convention to store your CSS or Sass files in a `src/styles` directory, but this is not required. As long as your styles live somewhere in the `src/` directory and are imported correctly, Astro will handle and optimize them. ### `public/` The `public/` directory is for files and assets in your project that do not need to be processed during Astro's build process. The files in this folder will be copied into the build folder untouched, and then your site will be built. This behavior makes `public/` ideal for common assets that do not require any processing, like some images and fonts, or special files such as `robots.txt` and `manifest.webmanifest`. You can place CSS and JavaScript in your `public/` directory, but be aware that those files will not be bundled or optimized in your final build. :::tip As a general rule, any CSS or JavaScript that you write yourself should live in your `src/` directory. ::: ### `package.json` This is a file used by JavaScript package managers to manage your dependencies. It also defines the scripts that are commonly used to run Astro (ex: `npm run dev`, `npm run build`). There are [two kinds of dependencies](https://docs.npmjs.com/specifying-dependencies-and-devdependencies-in-a-package-json-file) you can specify in a `package.json`: `dependencies` and `devDependencies`. In most cases, these work the same: Astro needs all dependencies at build time, and your package manager will install both. We recommend putting all of your dependencies in `dependencies` to start, and only use `devDependencies` if you find a specific need to do so. For help creating a new `package.json` file for your project, check out the [manual setup](/en/install-and-setup/#manual-setup) instructions. ### `astro.config.mjs` 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` 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. See the [configuration reference](/en/reference/configuration-reference/) for complete details. ### `tsconfig.json` This file is generated in every starter template and includes TypeScript configuration options for your Astro project. Some features (like npm package imports) aren’t fully supported in the editor without a `tsconfig.json` file. See the [TypeScript Guide](/en/guides/typescript/) for details on setting configurations. ## [Islands architecture](https://docharvest.github.io/docs/astro/concepts/islands/) Contents astro Islands architecture Astro Islands architecture Astro helped pioneer and popularize a new frontend architecture pattern called **Islands Architecture.** Islands architecture works by rendering the majority of your page to fast, static HTML with smaller "islands" of JavaScript added when interactivity or personalization is needed on the page (an image carousel, for example). This avoids the monolithic JavaScript payloads that slow down the responsiveness of many other, modern JavaScript web frameworks. ## A brief history The term "component island" was first coined by Etsy's frontend architect [Katie Sylor-Miller](https://sylormiller.com/) in 2019. This idea was then expanded on and documented in [this post](https://jasonformat.com/islands-architecture/) by Preact creator Jason Miller on August 11, 2020. > The general idea of an "Islands" architecture is deceptively simple: render HTML pages on the server, and inject placeholders or slots around highly dynamic regions \[...\] that can then be "hydrated" on the client into small self-contained widgets, reusing their server-rendered initial HTML. > — Jason Miller, Creator of Preact The technique that this architectural pattern builds on is also known as **partial** or **selective hydration.** In contrast, most JavaScript-based web frameworks hydrate & render an entire website as one large JavaScript application (also known as a single-page application, or SPA). SPAs provide simplicity and power but suffer from page-load performance problems due to heavy client-side JavaScript usage. SPAs have their place, even [embedded inside an Astro page](/en/guides/migrate-to-astro/from-create-react-app/). But, SPAs lack the native ability to selectively and strategically hydrate, making them a heavy-handed choice for most projects on the web today. Astro became popular as the first mainstream JavaScript web framework with selective hydration built-in, using that same component islands pattern first coined by Sylor-Miller. We've since expanded and evolved on Sylor-Miller's original work, which helped to inspire a similar component island approach to dynamically server-rendered content. ## What is an island? In Astro, an island is an enhanced UI component on an otherwise static page of HTML. A [**client island**](#client-islands) is an interactive JavaScript UI component that is hydrated separately from the rest of the page, while a [**server island**](#server-islands) is a UI component that server-renders its dynamic content separately from the rest of the page. Both islands run expensive or slower processes independently, on a per-component basis, for optimized page loads. ## Island components Astro components are the building blocks of your page template. They render to static HTML with no client-side runtime. Think of a client island as an interactive widget floating in a sea of otherwise static, lightweight, server-rendered HTML. Server islands can be added for personalized or dynamic server-rendered elements, such as a logged in visitor's profile picture. Header (interactive island) Sidebar (static HTML) Static content like text, images, etc. Image carousel (interactive island) Footer (static HTML) Source: \[Islands Architecture: Jason Miller\](https://jasonformat.com/islands-architecture/) An island always runs in isolation from other islands on the page, and multiple islands can exist on a page. Client islands can still share state and communicate with each other, even though they run in different component contexts. This flexibility allows Astro to support multiple UI frameworks like [React](https://react.dev/), [Preact](https://preactjs.com/), [Svelte](https://svelte.dev/), [Vue](https://vuejs.org/), and [SolidJS](https://www.solidjs.com/). Because they are independent, you can even mix several frameworks on each page. :::tip Although most developers will stick to just one UI framework, Astro supports multiple frameworks in the same project. This allows you to: - Choose the framework that is best for each component. - Learn a new framework without needing to start a new project. - Collaborate with others even when working in different frameworks. - Incrementally convert an existing site to another framework with no downtime. ::: ## Client Islands By default, Astro will automatically render every UI component to just HTML & CSS, **stripping out all client-side JavaScript automatically.** ``` ``` This may sound strict, but this behavior is what keeps Astro websites fast by default and protects developers from accidentally sending unnecessary or unwanted JavaScript that might slow down their website. Turning any static UI component into an interactive island requires only a `client:*` directive. Astro then automatically builds and bundles your client-side JavaScript for optimized performance. ``` ``` With islands, client-side JavaScript is only loaded for the explicit interactive components that you mark using `client:*` directives. And because interaction is configured at the component-level, you can handle different loading priorities for each component based on its usage. For example, `client:idle` tells a component to load when the browser becomes idle, and `client:visible` tells a component to load only once it enters the viewport. ### Benefits of client islands The most obvious benefit of building with Astro Islands is performance: the majority of your website is converted to fast, static HTML and JavaScript is only loaded for the individual components that need it. JavaScript is one of the slowest assets that you can load per-byte, so every byte counts. Another benefit is parallel loading. In the example illustration above, the low-priority "image carousel" island doesn't need to block the high-priority "header" island. The two load in parallel and hydrate in isolation, meaning that the header becomes interactive immediately without having to wait for the heavier carousel lower down the page. Even better, you can tell Astro exactly how and when to render each component. If that image carousel is really expensive to load, you can attach a special [client directive](/en/reference/directives-reference/#client-directives) that tells Astro to only load the carousel when it becomes visible on the page. If the user never sees it, it never loads. In Astro, it’s up to you as the developer to explicitly tell Astro which components on the page need to also run in the browser. Astro will only hydrate exactly what’s needed on the page and leave the rest of your site as static HTML. **Client islands are the secret to Astro’s fast-by-default performance story!** Read more about [using JavaScript framework components](/en/guides/framework-components/) in your project. ## Server islands Server islands are a way to move expensive or slow server-side code out of the way of the main rendering process, making it easy to combine high-performance static HTML and dynamic server-generated components. Add the [`server:defer` directive](/en/reference/directives-reference/#server-directives) to any Astro component on your page to turn it into its own server island: ``` --- --- ``` This breaks up your page with smaller areas of server-rendered content that each load in parallel. Your page's main content can be rendered immediately with placeholder content, such as a generic avatar, until your island's own content is available. With server islands, having small components of personalized content does not delay the rendering of an otherwise static page. This rendering pattern was built to be portable. It does not depend on any server infrastructure so it will work with any host, from a Node.js server in a Docker container to the serverless provider of your choice. ### Benefits of server islands One benefit of server islands is the ability to render the more highly dynamic parts of your page on the fly. This allows the outer shell and main content to be more aggressively cached, providing faster performance. Another benefit is providing a great visitor experience. Server islands are optimized and load quickly, often even before the browser has even painted the page. But in the short time it takes for your islands to render, you can display custom fallback content and prevent any layout shift. An example of a site that benefits from Astro's server islands is an e-commerce storefront. Although the main content of product pages change infrequently, these pages typically have some dynamic pieces: - The user's avatar in the header. - Special deals and sales for the product. - User reviews. Using server islands for these elements, your visitor will see the most important part of the page, your product, immediately. Generic avatars, loading spinners, and store announcements can be displayed as fallback content until the personalized parts are available. Read more about [using server islands](/en/guides/server-islands/) in your project. ## [Why Astro?](https://docharvest.github.io/docs/astro/concepts/why-astro/) Contents astro Why Astro? Astro Why Astro? **Astro** is the web framework for building **content-driven websites** like blogs, marketing, and e-commerce. Astro is best-known for pioneering a new [frontend architecture](/en/concepts/islands/) to reduce JavaScript overhead and complexity compared to other frameworks. If you need a website that loads fast and has great SEO, then Astro is for you. ## Features **Astro is an all-in-one web framework.** It includes everything you need to create a website, built-in. There are also hundreds of different [integrations](https://astro.build/integrations/) and [API hooks](/en/reference/integrations-reference/) available to customize a project to your exact use case and needs. Some highlights include: - **[Islands](/en/concepts/islands/):** A component-based web architecture optimized for content-driven websites. - **[UI-agnostic](/en/guides/framework-components/):** Supports React, Preact, Svelte, Vue, Solid, HTMX, web components, and more. - **[Server-first](/en/guides/on-demand-rendering/):** Moves expensive rendering off of your visitors' devices. - **[Zero JS, by default](/en/basics/astro-components/):** Less client-side JavaScript to slow your site down. - **[Content collections](/en/guides/content-collections/):** Organize, validate, and provide TypeScript type-safety for your Markdown content. - **[Customizable](/en/guides/integrations/):** Partytown, MDX, and hundreds of integrations to choose from. ## Design Principles Here are five core design principles to help explain why we built Astro, the problems that it exists to solve, and why Astro may be the best choice for your project or team. Astro is... 1. **[Content-driven](#content-driven):** Astro was designed to showcase your content. 2. **[Server-first](#server-first):** Websites run faster when they render HTML on the server. 3. **[Fast by default](#fast-by-default):** It should be impossible to build a slow website in Astro. 4. **[Easy to use](#easy-to-use):** You don't need to be an expert to build something with Astro. 5. **[Developer-focused](#developer-focused):** You should have the resources you need to be successful. ### Content-driven **Astro was designed for building content-rich websites.** This includes marketing sites, publishing sites, documentation sites, blogs, portfolios, landing pages, community sites, and e-commerce sites. If you have content to show, it needs to reach your reader quickly. By contrast, most modern web frameworks were designed for building _web applications_. These frameworks excel at building more complex, application-like experiences in the browser: logged-in admin dashboards, inboxes, social networks, todo lists, and even native-like applications like [Figma](https://figma.com/) and [Ping](https://ping.gg/). However with that complexity, they can struggle to provide great performance when delivering your content. Astro's focus on content from its beginnings as a static site builder have allowed Astro to **sensibly scale up to performant, powerful, dynamic web applications** that still respect your content and your audience. Astro's unique focus on content lets Astro make tradeoffs and deliver unmatched performance features that wouldn't make sense for more application-focused web frameworks to implement. ### Server-first **Astro leverages server rendering over client-side rendering in the browser as much as possible.** This is the same approach that traditional server-side frameworks -- PHP, WordPress, Laravel, Ruby on Rails, etc. -- have been using for decades. But you don't need to learn a second server-side language to unlock it. With Astro, everything is still just HTML, CSS, and JavaScript (or TypeScript, if you prefer). This approach stands in contrast to other modern JavaScript web frameworks like Next.js, SvelteKit, Nuxt, Remix, and others. These frameworks were built for client-side rendering of your entire website and include server-side rendering mainly to address performance concerns. This approach has been dubbed the **Single-Page App (SPA)**, in contrast with Astro's **Multi-Page App (MPA)** approach. The SPA model has its benefits. However, these come at the expense of additional complexity and performance tradeoffs. These tradeoffs harm page performance -- critical metrics like [Time to Interactive (TTI)](https://web.dev/interactive/) -- which doesn't make much sense for content-focused websites where first-load performance is essential. Astro's server-first approach allows you to opt in to client-side rendering only if, and exactly as, necessary. You can choose to add UI framework components that run on the client. You can take advantage of Astro's view transitions router for finer control over select page transitions and animations. Astro's server-first rendering, either pre-rendered or on-demand, provides performant defaults that you can enhance and extend. ### Fast by default Good performance is always important, but it is _especially_ critical for websites whose success depends on displaying your content. It has been well-proven that poor performance loses you engagement, conversions, and money. For example: - Every 100ms faster → 1% more conversions ([Mobify](https://web.dev/why-speed-matters/), earning +$380,000/yr) - 50% faster → 12% more sales ([AutoAnything](https://www.digitalcommerce360.com/2010/08/19/web-accelerator-revs-conversion-and-sales-autoanything/)) - 20% faster → 10% more conversions ([Furniture Village](https://www.thinkwithgoogle.com/intl/en-gb/marketing-strategies/app-and-mobile/furniture-village-and-greenlight-slash-page-load-times-boosting-user-experience/)) - 40% faster → 15% more sign-ups ([Pinterest](https://medium.com/pinterest-engineering/driving-user-growth-with-performance-improvements-cfc50dafadd7)) - 850ms faster → 7% more conversions ([COOK](https://web.dev/why-speed-matters/)) - Every 1 second slower → 10% fewer users ([BBC](https://www.creativebloq.com/features/how-the-bbc-builds-websites-that-scale)) In many web frameworks, it is easy to build a website that looks great during development only to load painfully slow once deployed. JavaScript is often the culprit, since many phones and lower-powered devices rarely match the speed of a developer's laptop. Astro's magic is in how it combines the two values explained above -- a content focus with a server-first architecture -- to make tradeoffs and deliver features that other frameworks cannot. The result is amazing web performance for every website, out of the box. Our goal: **It should be nearly impossible to build a slow website with Astro.** An Astro website can [load 40% faster with 90% less JavaScript](https://twitter.com/t3dotgg/status/1437195415439360003) than the same site built with the most popular React web framework. But don't take our word for it: watch Astro's performance leave Ryan Carniato (creator of Solid.js and Marko) [speechless](https://youtu.be/2ZEMb_H-LYE?t=8163). ### Easy to use **Astro's goal is to be accessible to every web developer.** Astro was designed to feel familiar and approachable regardless of skill level or past experience with web development. The `.astro` UI language is a superset of HTML: any valid HTML is valid Astro templating syntax! So, if you can write HTML, you can write Astro components! But, it also combines some of our favorite features borrowed from other component languages like JSX expressions (React) and CSS scoping by default (Svelte and Vue). This closeness to HTML also makes it easier to use progressive enhancement and common accessibility patterns without any overhead. We then made sure that you could also use your favorite UI component languages that you already know, and even reuse components you might already have. React, Preact, Svelte, Vue, Solid, and others, including web components, are all supported for authoring UI components in an Astro project. Astro was designed to be less complex than other UI frameworks and languages. One big reason for this is that Astro was designed to render on the server, not in the browser. That means that you don't need to worry about hooks (React), stale closures (also React), refs (Vue), observables (Svelte), atoms, selectors, reactions, or derivations. There is no reactivity on the server, so all of that complexity melts away. One of our favorite sayings is **opt in to complexity.** We designed Astro to remove as much "required complexity" as possible from the developer experience, especially as you onboard for the first time. You can build a "Hello World" example website in Astro with just HTML and CSS. Then, when you need to build something more powerful, you can incrementally reach for new features and APIs as you go. ### Developer-focused We strongly believe that Astro is only a successful project if people love using it. Astro has everything you need to support you as you build with Astro. Astro invests in developer tools like a great CLI experience from the moment you open your terminal, an official VS Code extension for syntax highlighting, TypeScript and Intellisense, and documentation actively maintained by hundreds of community contributors and available in 14 languages. Our welcoming, respectful, inclusive community on Discord is ready to provide support, motivation, and encouragement. Open a `#support` thread to get help with your project. Visit our dedicated `#showcase` channel for sharing your Astro sites, blog posts, videos, and even work-in-progress for safe feedback and constructive criticism. Participate in regular live events such as our weekly community call, "Talking and Doc'ing," and API/bug bashes. As an open-source project, we welcome contributions of all types and sizes from community members of all experience levels. You are invited to join in roadmap discussions to shape the future of Astro, and we hope you'll contribute fixes and features to the core codebase, compiler, docs, language tools, websites, and other projects. ## [Contribute to Astro](https://docharvest.github.io/docs/astro/contribute/) Contents astro Contribute to Astro Astro Contribute to Astro import ContributorList from '~/components/ContributorList.astro' We welcome contributions of any size and contributors of any skill level. As an open-source project, we believe in giving back to our contributors. We are happy to help with guidance on PRs, technical writing, and turning any feature idea into a reality. :::tip\[Open Issues\] Browse [all of Astro's open GitHub issues tagged `help wanted` and `good first issues`](https://github.com/search?q=org%3Awithastro+label%3A%22good+first+issue%22%2C%22help+wanted%22%2C+state%3Aopen&type=issues) for all the code repositories: Astro code, documentation, Starlight, Astro websites, language tools, GitHub Actions, bots, the compiler... there are lots of places to contribute! ::: Want to get even more involved? See our [Governance doc](https://github.com/withastro/.github/blob/main/GOVERNANCE.md) for detailed descriptions of different roles, maintainer nomination processes, code review processes, and Code of Conduct enforcement. ## Ways to Contribute ### Project repositories There are lots of ways to contribute to the Astro Project! Every Astro repository has a README with a link to a `CONTRIBUTING.md` file in the root of the project. Visit [Astro's GitHub profile](https://github.com/withastro) to find the repositories for: - The [main Astro codebase](https://github.com/withastro/astro), including official integrations and starter templates. - [Astro Docs](https://github.com/withastro/docs), an entire Astro website! Contribute not just written content, but also Astro code addressing a11y, CSS, UI, and UX concerns. We also make our documentation available in several languages, so we need help translating the entire site. - The [Astro compiler](https://github.com/withastro/compiler-rs), written in Rust, distributed as WASM. - Astro's [language tools](https://github.com/withastro/astro/tree/main/packages/language-tools), the editor tooling required for the Astro language (`.astro` files). - [Starlight](https://github.com/withastro/starlight), Astro's official documentation framework. - The [Astro Roadmap](https://github.com/withastro/roadmap) where the future of Astro is shaped! Ideas, suggestions, and formal RFC proposals for the Astro project. ### Types of contributions In addition to contributing your own code or content, you can also make a huge contribution by getting involved by leaving review comments on PRs, adding ideas in existing GitHub Issues and Discussions, and participating in our "Pinned" issue maintenance tasks! Every PR, especially translation PRs, needs reviewers! Reviewing PRs and leaving comments, suggestions, or an approving "LGTM!" ("Looks Good To Me!") is a great way to get started in any repository, and to learn more about Astro. We also have a very active [Discord](https://astro.build/chat) community! We value the contributions of those who welcome new members, answer support questions, and share what they have built with and for Astro! Beyond traditional GitHub contributions, Astro recognizes and supports community members who engage with our community, share Astro in blog posts, videos and conference talks, and help maintain the health of our community. ## Contributing to Docs We have several guides available to assist you with contributing to Astro Docs. Whether it's your very first contribution to open-source, or you need to add docs for the new Astro feature you just built, or you're an experienced translator looking for the next page to translate, or you'd like to learn more about helping as a PR reviewer... we've got you covered! Please visit our dedicated site [_Astro Docs_ Docs](https://contribute.docs.astro.build), where you'll find our documentation to help you contribute to Astro Docs as a typo-fixer, a writer, a translator, a feature-builder, and even as a PR reviewer. ## Our contributors These docs are brought to you by all these helpful people. [Join us on GitHub!](https://github.com/withastro/docs) ## [Develop and build](https://docharvest.github.io/docs/astro/develop-and-build/) Contents astro Develop and build Astro Develop and build Once you have an Astro project, now you're ready to build with Astro! 🚀 ## Edit your project To make changes to your project, open your project folder in your code editor. Working in development mode with the dev server running allows you to see updates to your site as you edit the code. You can also [customize aspects of your development environment](#configure-your-dev-environment) such as configuring TypeScript or installing the official Astro editor extensions. ### Start the Astro dev server Astro comes with a built-in development server that has everything you need for project development. The `astro dev` CLI command will start the local development server so that you can see your new website in action for the very first time. Every starter template comes with a pre-configured script that will run `astro dev` for you. After navigating into your project directory, use your favorite package manager to run this command and start the Astro development server. \`\`\`shell npm run dev \`\`\` \`\`\`shell pnpm run dev \`\`\` \`\`\`shell yarn run dev \`\`\` If all goes well, Astro will now be serving your project on [http://localhost:4321/](http://localhost:4321/). Visit that link in your browser and see your new site! ### Work in development mode Astro will listen for live file changes in your `src/` directory and update your site preview as you build, so you will not need to restart the server as you make changes during development. You will always be able to see an up-to-date version of your site in your browser when the dev server is running. When viewing your site in the browser, you'll have access to the [Astro dev toolbar](/en/guides/dev-toolbar/). As you build, it will help you inspect your [islands](/en/concepts/islands/), spot accessibility issues, and more. If you aren't able to open your project in the browser after starting the dev server, go back to the terminal where you ran the `dev` command and check the message displayed. It should tell you if an error occurred, or if your project is being served at a different URL than [http://localhost:4321/](http://localhost:4321/). ## Build and preview your site To check the version of your site that will be created at build time, quit the dev server (Ctrl + C) and run the appropriate build command for your package manager in your terminal: \`\`\`shell npm run build \`\`\` \`\`\`shell pnpm build \`\`\` \`\`\`shell yarn run build \`\`\` Astro will build a deploy-ready version of your site in a separate folder (`dist/` by default) and you can watch its progress in the terminal. This will alert you to any build errors in your project before you deploy to production. If TypeScript is configured to `strict` or `strictest`, the `build` script will also check your project for type errors. When the build is finished, run the appropriate `preview` command (e.g. `npm run preview`) in your terminal and you can view the built version of your site locally in the same browser preview window. Note that this previews your code as it existed when the build command was last run. This is meant to give you a preview of how your site will look when it is deployed to the web. Any later changes you make to your code after building will **not** be reflected while you preview your site until you run the build command again. Use (Ctrl + C) to quit the preview and run another terminal command, such as restarting the dev server to go back to [working in development mode](#work-in-development-mode) which does update as you edit to show a live preview of your code changes. Read more about [the Astro CLI](/en/reference/cli-reference/) and the terminal commands you will use as you build with Astro. :::tip You may wish to [deploy your new site right away](/en/guides/deploy/), before you begin to add or change too much code. This is helpful to get a minimal, working version of your site published and can save you extra time and effort troubleshooting your deployment later. ::: ## Next Steps Success! You are now ready to start building with Astro! 🥳 Here are a few things that we recommend exploring next. You can read them in any order. You can even leave our documentation for a bit and go play in your new Astro project codebase, coming back here whenever you run into trouble or have a question. ### Configure your dev environment Explore the guides below to customize your development experience. ### Explore Astro's Features ### Take the introductory tutorial Build a fully functional Astro blog starting from a single blank page in our [introductory tutorial](/en/tutorial/0-introduction/). This is a great way to see how Astro works and walks you through the basics of pages, layouts, components, routing, islands, and more. It also includes an optional, beginner-friendly unit for those newer to web development concepts in general, which will guide you through installing the necessary applications on your computer, creating a GitHub account, and deploying your site. ## [Editor setup](https://docharvest.github.io/docs/astro/editor-setup/) Contents astro Editor setup Astro Editor setup See how to [set up TypeScript](/en/guides/typescript/) in your Astro project. ## Zed [Zed](https://zed.dev/) is a high-performance, multiplayer code editor that is optimized for speed and large projects. Their [Astro extension](https://zed.dev/extensions/astro) includes features like syntax highlighting for `.astro` files, code completion, formatting, diagnostics, and go-to-definition. ## JetBrains IDEs [Webstorm](https://www.jetbrains.com/webstorm/) is a JavaScript and TypeScript IDE that added support for the Astro Language Server in version 2024.2. This update brings features like syntax highlighting, code completion, and formatting. Install the official plugin through [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/20959-astro) or by searching for "Astro" in the IDE's Plugins tab. You can toggle the language server in `Settings | Languages & Frameworks | TypeScript | Astro`. For more information on Astro support in Webstorm, check out [the official Webstorm Astro Documentation](https://www.jetbrains.com/help/webstorm/astro.html). ## Other Code Editors Our amazing community maintains several extensions for other popular editors, including: - [VS Code Extension on Open VSX](https://open-vsx.org/extension/astro-build/astro-vscode) \- The official Astro VS Code Extension, available on the Open VSX registry for editors like [Cursor](https://cursor.com) or [VSCodium](https://vscodium.com/). - [Vim Plugin](https://github.com/wuelnerdotexe/vim-astro) \- Provides syntax highlighting, indentation, and code folding support for Astro inside of Vim or Neovim - Neovim [LSP](https://github.com/neovim/nvim-lspconfig/blob/master/doc/configs.md#astro) and [TreeSitter](https://github.com/virchau13/tree-sitter-astro) Plugins \- Provides syntax highlighting, treesitter parsing, and code completion for Astro inside of Neovim - Emacs - See instructions for [Configuring Emacs and Eglot](https://medium.com/@jrmjrm/configuring-emacs-and-eglot-to-work-with-astro-language-server-9408eb709ab0) to work with Astro - [Astro syntax highlighting for Sublime Text](https://packagecontrol.io/packages/Astro) \- The Astro package for Sublime Text, available on the Sublime Text package manager. - [Nova Extension](https://extensions.panic.com/extensions/sciencefidelity/sciencefidelity.astro/) \- Provides syntax highlighting and code completion for Astro inside of Nova ## In-Browser Editors In addition to local editors, Astro also runs well on in-browser hosted editors, including: - [StackBlitz](https://stackblitz.com/) and [CodeSandbox](https://codesandbox.io/) - online editors that run in your browser, with built-in syntax highlighting support for `.astro` files. No installation or configuration required! - [GitHub.dev](https://github.dev/) - allows you to install the Astro VS Code extension as a [web extension](https://code.visualstudio.com/api/extension-guides/web-extensions), which gives you access to only some of the full extension features. Currently, only syntax highlighting is supported. ## Other tools ### ESLint [ESLint](https://eslint.org/) is a popular linter for JavaScript and JSX. For Astro support, [a community maintained plugin](https://github.com/ota-meshi/eslint-plugin-astro) can be installed. See [the project's User Guide](https://ota-meshi.github.io/eslint-plugin-astro/user-guide/) for more information on how to install and set up ESLint for your project. ### Stylelint [Stylelint](https://stylelint.io/) is a popular linter for CSS. [A community maintained Stylelint configuration](https://github.com/ota-meshi/stylelint-config-html) provides Astro support. Installation instructions, editor integration, and additional information can be found in the project's README. ### Biome [Biome](https://biomejs.dev/) is an all-in-one linter and formatter for the web. [Biome currently has experimental support for `.astro` files](https://biomejs.dev/internals/language-support/#html-super-languages-support), and can be used to lint and format the frontmatter in `.astro` files. ### Prettier [Prettier](https://prettier.io/) is a popular formatter for JavaScript, HTML, CSS, and more. If you're using the [Astro VS Code Extension](https://marketplace.visualstudio.com/items?itemName=astro-build.astro-vscode), code formatting with Prettier is included. To add support for formatting `.astro` files outside of the editor (e.g. CLI) or inside editors that don't support our editor tooling, install [the official Astro Prettier plugin](https://github.com/withastro/prettier-plugin-astro). 1\. Install \`prettier\` and \`prettier-plugin-astro\`. ``` ```shell npm install --save-dev --save-exact prettier prettier-plugin-astro ``` ```shell pnpm add --save-dev --save-exact prettier prettier-plugin-astro ``` ```shell yarn add --dev --exact prettier prettier-plugin-astro ``` ``` 2. Create a `.prettierrc` configuration file (or `.prettierrc.json`, `.prettierrc.mjs`, or [other supported formats](https://prettier.io/docs/configuration)) in the root of your project and add `prettier-plugin-astro` to it. In this file, also manually specify the parser for Astro files. ``` { "plugins": ["prettier-plugin-astro"], "overrides": [ { "files": "*.astro", "options": { "parser": "astro" } } ] } ``` 3. Optionally, install other Prettier plugins for your project, and add them to the configuration file. These additional plugins may need to be listed in a specific order. For example, if you use Tailwind, `prettier-plugin-tailwindcss` must be [the last Prettier plugin in the plugins array](https://github.com/tailwindlabs/prettier-plugin-tailwindcss#compatibility-with-other-prettier-plugins). ``` { "plugins": [ "prettier-plugin-astro", "prettier-plugin-tailwindcss" // needs to be last ], "overrides": [ { "files": "*.astro", "options": { "parser": "astro" } } ] } ``` 4. Run the following command in your terminal to format your files. \`\`\`shell npx prettier . --write \`\`\` \`\`\`shell pnpm exec prettier . --write \`\`\` \`\`\`shell yarn exec prettier . --write \`\`\` See the [Prettier plugin's README](https://github.com/withastro/prettier-plugin-astro/blob/main/README.md) for more information about its supported options, how to set up Prettier inside VS Code, and more. ### dprint [dprint](https://dprint.dev/) is a highly-configurable code formatter that supports many languages, including JavaScript, TypeScript, CSS, and more. Support for `.astro` files can be added using the [markup\_fmt plugin](https://github.com/g-plane/markup_fmt). ## [Getting started](https://docharvest.github.io/docs/astro/getting-started/) Contents astro Getting started Astro Getting started import { CardGrid } from '@astrojs/starlight/components' import Card from '/components/Landing/Card.astro' import ListCard from '/components/Landing/ListCard.astro' import SplitCard from '/components/Landing/SplitCard.astro' import Discord from '/components/Landing/Discord.astro' Explore \[Astro starter themes\](https://astro.build/themes/) for blogs, portfolios, docs, landing pages, SaaS, marketing, ecommerce sites, and more! Complete our introductory \[Build a Blog Tutorial\](/en/tutorial/0-introduction/) to learn the basics and create your first Astro site. \`\`\`sh # create a new project with npm npm create astro@latest \`\`\` ``` Our [installation guide](/en/install-and-setup/) has step-by-step instructions for installing Astro using our CLI wizard, creating a new project from an existing Astro GitHub repository, and for installing Astro manually. ``` \- \[Astro’s main features\](/en/concepts/why-astro/) - \[Islands architecture\](/en/concepts/islands/) - \[Astro components\](/en/basics/astro-components/) - \[The Astro template syntax\](/en/reference/astro-syntax/) \- \[Add integrations like React and Partytown\](/en/guides/integrations/) - \[Create type safe content collections\](/en/guides/content-collections/) - \[Enhance navigation with view transitions\](/en/guides/view-transitions/) - \[Connect a headless CMS to your project\](/en/guides/cms/) ## [Actions](https://docharvest.github.io/docs/astro/guides/actions/) Contents astro Actions Astro Actions Astro Actions allow you to define and call backend functions with type-safety. Actions perform data fetching, JSON parsing, and input validation for you. This can greatly reduce the amount of boilerplate needed compared to using an [API endpoint](/en/guides/endpoints/). Use actions instead of API endpoints for seamless communication between your client and server code and to: - Automatically validate JSON and form data inputs using [Zod validation](/en/reference/modules/astro-zod/). - Generate type-safe functions to call your backend from the client and even [from HTML form actions](#call-actions-from-an-html-form-action). No need for manual `fetch()` calls. - Standardize backend errors with the [`ActionError`](/en/reference/modules/astro-actions/#actionerror) object. ## Basic usage Actions are defined in a `server` object exported from `src/actions/index.ts`: ``` } ``` ### Write your first action Follow these steps to define an action and call it in a `script` tag in your Astro page. 1. Create a `src/actions/index.ts` file and export a `server` object. ``` export const server = { // action declarations } ``` 2. Import the `defineAction()` utility from `astro:actions`, and the `z` object from `astro/zod`. ``` import { defineAction } from 'astro:actions'; import { z } from 'astro/zod'; export const server = { // action declarations } ``` 3. Use the `defineAction()` utility to define a `getGreeting` action. The `input` property will be used to validate input parameters with a [Zod schema](/en/reference/modules/astro-zod/#common-data-type-validators) and the `handler()` function includes the backend logic to run on the server. ``` import { defineAction } from 'astro:actions'; import { z } from 'astro/zod'; export const server = { getGreeting: defineAction({ input: z.object({ name: z.string(), }), handler: async (input) => { return `Hello, ${input.name}!` } }) } ``` 4. Create an Astro component with a button that will fetch a greeting using your `getGreeting` action when clicked. ``` --- --- ``` 5. To use your action, import `actions` from `astro:actions` and then call `actions.getGreeting()` in the click handler. The `name` option will be sent to your action’s `handler()` on the server and, if there are no errors, the result will be available as the `data` property. ``` --- --- ``` See the full Actions API documentation for details on [`defineAction()`](/en/reference/modules/astro-actions/#defineaction) and its properties. ## Organizing actions All actions in your project must be exported from the `server` object in the `src/actions/index.ts` file. You can define actions inline or you can move action definitions to separate files and import them. You can even group related functions in nested objects. For example, to colocate all of your user actions, you can create a `src/actions/user.ts` file and nest the definitions of both `getUser` and `createUser` inside a single `user` object. ``` if (error) { // handle error cases return; } // use `data` ``` ### Accessing `data` directly without an error check To skip error handling, for example while prototyping or using a library that will catch errors for you, use the `.orThrow()` property on your action call to throw errors instead of returning an `error`. This will return the action's `data` directly. This example calls a `likePost()` action that returns the updated number of likes as a `number` from the action `handler`: ``` const updatedLikes = await actions.likePost.orThrow({ postId: 'example' }); // ^ type: number ``` ### Handling backend errors in your action You can use the provided `ActionError` to throw an error from your action `handler()`, such as "not found" when a database entry is missing, or "unauthorized" when a user is not logged in. This has two main benefits over returning `undefined`: - You can set a status code like `404 - Not found` or `401 - Unauthorized`. This improves debugging errors in both development and in production by letting you see the status code of each request. - In your application code, all errors are passed to the `error` object on an action result. This avoids the need for `undefined` checks on data, and allows you to display targeted feedback to the user depending on what went wrong. #### Creating an `ActionError` To throw an error, import the [`ActionError()` class](/en/reference/modules/astro-actions/#actionerror) from the `astro:actions` module. Pass it a human-readable status `code` (e.g. `"NOT_FOUND"` or `"BAD_REQUEST"`), and an optional `message` to provide further information about the error. This example throws an error from a `likePost` action when a user is not logged in, after checking a hypothetical "user-session" cookie for authentication: ``` } // Otherwise, like the post }, }), }; ``` #### Handling an `ActionError` To handle this error, you can call the action from your application and check whether an `error` property is present. This property will be of type `ActionError` and will contain your `code` and `message`. In the following example, a `LikeButton.tsx` component calls the `likePost()` action when clicked. If an authentication error occurs, the `error.code` attribute is used to determine whether to display a login link: ``` return ( <> { showLogin && Log in to like a post. } ) } ``` ### Handling client redirects When calling actions from the client, you can integrate with a client-side library like `react-router`, or you can use Astro's [`navigate()` function](/en/guides/view-transitions/#trigger-navigation) to redirect to a new page when an action succeeds. This example navigates to the homepage after a `logout` action returns successfully: ``` if (!error) navigate('/'); }}> Logout ); } ``` ## Accepting form data from an action Actions accept JSON data by default. To accept form data from an HTML form, set `accept: 'form'` in your `defineAction()` call: ### Validating form data Actions will parse submitted form data to an object, using the value of each input’s `name` attribute as the object keys. For example, a form containing `` will be parsed to an object like `{ search: 'user input' }`. Your action's `input` schema will be used to validate this object. To receive the raw `FormData` object in your action handler instead of a parsed object, omit the `input` property in your action definition. The following example shows a validated newsletter registration form that accepts a user's email and requires a "terms of service" agreement checkbox. 1. Create an HTML form component with unique `name` attributes on each input: ```
``` 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()`: ``` import { defineAction } from 'astro:actions'; import { z } from 'astro/zod'; export const server = { newsletter: defineAction({ accept: 'form', input: z.object({ email: z.email(), terms: z.boolean(), }), handler: async ({ email, terms }) => { /* ... */ }, }) } ``` See the [`input` API reference](/en/reference/modules/astro-actions/#input-validator) for all available form validators. 3. Add a ` ``` See [“Call actions from an HTML form action”](#call-actions-from-an-html-form-action) for an alternative way to submit form data. ### Displaying form input errors You can validate form inputs before submission using [native HTML form validation attributes](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation#using_built-in_form_validation) like `required`, `type="email"`, and `pattern`. For more complex `input` validation on the backend, you can use the provided [`isInputError()`](/en/reference/modules/astro-actions/#isinputerror) utility function. To retrieve input errors, use the `isInputError()` utility to check whether an error was caused by invalid input. Input errors contain a `fields` object with messages for each input name that failed to validate. You can use these messages to prompt your user to correct their submission. The following example checks the error with `isInputError()`, then checks whether the error is in the email field, before finally creating a message from the errors. You can use JavaScript DOM manipulation or your preferred UI framework to display this message to users. ``` const form = document.querySelector('form'); const formData = new FormData(form); const { error } = await actions.newsletter(formData); if (isInputError(error)) { // Handle input errors. if (error.fields.email) { const message = error.fields.email.join(', '); } } ``` ## Call actions from an HTML form action :::note Pages must be on-demand rendered when calling actions using a form action. [Ensure prerendering is disabled on the page](/en/guides/on-demand-rendering/#enabling-on-demand-rendering) before using this API. ::: You can enable zero-JS form submissions with standard attributes on any `
` element. Form submissions without client-side JavaScript may be useful both as a fallback for when JavaScript fails to load, or if you prefer to handle forms entirely from the server. Calling [Astro.getActionResult()](/en/reference/api-reference/#getactionresult) on the server returns the result of your form submission (`data` or `error`), and can be used to dynamically redirect, handle form errors, update the UI, and more. To call an action from an HTML form, add `method="POST"` to your ``, then set the form's `action` attribute using your action, for example `action={actions.logout}`. This will set the `action` attribute to use a query string that is handled by the server automatically. For example, this Astro component calls the `logout` action when the button is clicked and reloads the current page: ``` --- ---
``` Additional attributes on the `
` element may be necessary for proper schema validation with Zod. For example, to include file uploads, add `enctype="multipart/form-data"` to ensure that files are sent in a format correctly recognized by `z.instanceof(File)`: ``` --- ---
``` ### Redirect on action success If you need to redirect to a new route on success, you can use an action’s result on the server. A common example is creating a product record and redirecting to the new product's page, e.g. `/products/[id]`. For example, say you have a `createProduct` action that returns the generated product id: ``` return { id: product.id }; }, }) } ``` You can retrieve the action result from your Astro component by calling `Astro.getActionResult()`. This returns an object containing `data` or `error` properties when an action is called, or `undefined` if the action was not called during this request. Use the `data` property to construct a URL to use with `Astro.redirect()`: ``` --- const result = Astro.getActionResult(actions.createProduct); if (result && !result.error) { return Astro.redirect(`/products/${result.data.id}`); } ---
``` ### Handle form action errors Calling `Astro.getActionResult()` in the Astro component containing your form gives you access to the `data` and `error` objects for custom error handling. The following example displays a general failure message when a `newsletter` action fails: ``` --- const result = Astro.getActionResult(actions.newsletter); --- {result?.error && (

Unable to sign up. Please try again later.

)}
``` For more customization, you can [use the `isInputError()` utility](#displaying-form-input-errors) to check whether an error is caused by invalid input. The following example renders an error banner under the `email` input field when an invalid email is submitted: ``` --- const result = Astro.getActionResult(actions.newsletter); const inputErrors = isInputError(result?.error) ? result.error.fields : {}; ---
{inputErrors.email &&

{inputErrors.email.join(',')}

}
``` #### Preserve input values on error Inputs will be cleared whenever a form is submitted. To persist input values, you can [enable view transitions](/en/guides/view-transitions/#enabling-view-transitions-spa-mode) and apply the `transition:persist` directive to each input: ``` ``` ### Update the UI with a form action result To use an action's return value to display a notification to the user on success, pass the action to `Astro.getActionResult()`. Use the returned `data` property to render the UI you want to display. This example uses the `productName` property returned by an `addToCart` action to show a success message. ``` --- const result = Astro.getActionResult(actions.addToCart); --- {result && !result.error && (

Added {result.data.productName} to cart

)} ``` ### Advanced: Persist action results with a session Action results are displayed as a POST submission. This means that the result will be reset to `undefined` when a user closes and revisits the page. The user will also see a "confirm form resubmission?" dialog if they attempt to refresh the page. To customize this behavior, you can add middleware to handle the result of the action manually. You may choose to persist the action result using a cookie or session storage. Start by [creating a middleware file](/en/guides/middleware/) and importing [the `getActionContext()` utility](/en/reference/modules/astro-actions/#getactioncontext) from `astro:actions`. This function returns an `action` object with information about the incoming action request, including the action handler and whether the action was called from an HTML form. `getActionContext()` also returns the `setActionResult()` and `serializeActionResult()` functions to programmatically set the value returned by `Astro.getActionResult()`: ``` if (action?.calledFrom === 'form') { const result = await action.handler(); // ... handle the action result setActionResult(action.name, serializeActionResult(result)); } return next(); }); ``` A common practice to persist HTML form results is the [POST / Redirect / GET pattern](https://en.wikipedia.org/wiki/Post/Redirect/Get). This redirect removes the "confirm form resubmission?" dialog when the page is refreshed, and allows action results to be persisted throughout the user's session. This example applies the POST / Redirect / GET pattern to all form submissions using session storage with the [Netlify server adapter](/en/guides/integrations-guide/netlify/) installed. Action results are written to a session store using [Netlify Blob](https://docs.netlify.com/blobs/overview/), and retrieved after a redirect using a session ID: ``` const { action, setActionResult, serializeActionResult } = getActionContext(context); // Create a Blob store to persist action results with Netlify Blob const actionStore = getStore("action-session"); // If an action result was forwarded as a cookie, set the result // to be accessible from `Astro.getActionResult()` const sessionId = context.cookies.get("action-session-id")?.value; const session = sessionId ? await actionStore.get(sessionId, { type: "json", }) : undefined; if (session) { setActionResult(session.actionName, session.actionResult); // Optional: delete the session after the page is rendered. // Feel free to implement your own persistence strategy await actionStore.delete(sessionId); context.cookies.delete("action-session-id"); return next(); } // If an action was called from an HTML form action, // call the action handler and redirect to the destination page if (action?.calledFrom === "form") { const actionResult = await action.handler(); // Persist the action result using session storage const sessionId = randomUUID(); await actionStore.setJSON(sessionId, { actionName: action.name, actionResult: serializeActionResult(actionResult), }); // Pass the session ID as a cookie // to be retrieved after redirecting to the page context.cookies.set("action-session-id", sessionId); // Redirect back to the previous page on error if (actionResult.error) { const referer = context.request.headers.get("Referer"); if (!referer) { throw new Error( "Internal: Referer unexpectedly missing from Action POST request.", ); } return context.redirect(referer); } // Redirect to the destination page on success return context.redirect(context.originPathname); } return next(); }); ``` ## Security when using actions Actions are accessible as public endpoints based on the name of the action. For example, the action `blog.like()` will be accessible from `/_actions/blog.like`. This is useful for unit testing action results and debugging production errors. However, this means you **must** use same authorization checks that you would consider for API endpoints and on-demand rendered pages. ### Authorize users from an action handler To authorize action requests, add an authentication check to your action handler. You may want to use [an authentication library](/en/guides/authentication/) to handle session management and user information. Actions expose [a subset of the `APIContext` object](/en/reference/modules/astro-actions/#actionapicontext) to access properties passed from middleware using `context.locals`. When a user is not authorized, you can raise an `ActionError` with the `UNAUTHORIZED` code: ``` } return { /* data on success */ }; } }) } ``` ### Gate actions from middleware Astro recommends authorizing user sessions from your action handler to respect permission levels and rate-limiting on a per-action basis. However, you can also gate requests to all actions (or a subset of actions) from middleware. Use the [`getActionContext()` function](/en/reference/modules/astro-actions/#getactioncontext) from your middleware to retrieve information about any inbound action requests. This includes the action name and whether that action was called using a client-side remote procedure call (RPC) function (e.g. `actions.blog.like()`) or an HTML form. The following example rejects all action requests that do not have a valid session token. If the check fails, a "Forbidden" response is returned. Note: this method ensures that actions are only accessible when a session is present, but is _not_ a substitute for secure authorization. ``` // Check if the action was called from a client-side function if (action?.calledFrom === "rpc") { // If so, check for a user session token if (!context.cookies.has("user-session")) { return new Response("Forbidden", { status: 403 }); } } context.cookies.set("user-session", "session-token-value"); return next(); }); ``` ## Call actions from Astro components and server endpoints You can call actions directly from Astro component scripts using the `Astro.callAction()` wrapper (or `context.callAction()` when using a [server endpoint](/en/guides/endpoints/#server-endpoints-api-routes)). This is common to reuse logic from your actions in other server code. Pass the action as the first argument and any input parameters as the second argument. This returns the same `data` and `error` objects you receive when calling actions on the client: ``` --- const searchQuery = Astro.url.searchParams.get('search'); if (searchQuery) { const { data, error } = await Astro.callAction(actions.findProduct, { query: searchQuery }); // handle result } --- ``` ## [Authentication](https://docharvest.github.io/docs/astro/guides/authentication/) ## [Use a backend service with Astro](https://docharvest.github.io/docs/astro/guides/backend/) Contents astro Use a backend service with Astro Astro Use a backend service with Astro **Ready to add features like authentication, monitoring, storage, or data to your Astro project?** Follow one of our guides to integrate a backend service. :::tip Find [community-maintained integrations](https://astro.build/integrations/) for adding popular features to your project in our integrations directory. ::: ## Backend service guides Note that many of these pages are **stubs**: they're collections of resources waiting for your contribution! ## What is a backend service? A backend service is a cloud-based system that helps you build and manage your backend infrastructure. It provides a set of tools and services for managing databases, user authentication, and other server-side functionality. This enables you to focus on building your applications without having to worry about managing the underlying infrastructure. ## Why would I use a backend service? You might want to consider a backend service if your project has complex server-side needs, for example: - user sign-ups and authentication - persistent data storage - user-uploaded asset storage - API generation - realtime communication - application monitoring ## [Appwrite & Astro](https://docharvest.github.io/docs/astro/guides/backend/appwrite/) Contents astro Appwrite & Astro Astro Appwrite & Astro [Appwrite](https://appwrite.io/) is a self-hosted backend-as-a-service platform that provides authentication and account management, user preferences, database and storage persistence, cloud functions, localization, image manipulation, and other server-side utilities. ## Official Resources - [Appwrite Demos for Astro](https://github.com/appwrite/demos-for-astro) ## [Firebase & Astro](https://docharvest.github.io/docs/astro/guides/backend/firebase/) Contents astro Firebase & Astro Astro Firebase & Astro [Firebase](https://firebase.google.com/) is an app development platform that provides a NoSQL database, authentication, realtime subscriptions, functions, and storage. See our separate guide for [deploying to Firebase hosting](/en/guides/deploy/firebase/). ## Initializing Firebase in Astro ### Prerequisites - A [Firebase project with a web app configured](https://firebase.google.com/docs/web/setup). - An Astro project with [`output: 'server'` for on-demand rendering](/en/guides/on-demand-rendering/) enabled. - Firebase credentials: You will need two sets of credentials to connect Astro to Firebase: - Web app credentials: These credentials will be used by the client side of your app. You can find them in the Firebase console under _Project settings > General_. Scroll down to the **Your apps** section and click on the **Web app** icon. - Project credentials: These credentials will be used by the server side of your app. You can generate them in the Firebase console under _Project settings > Service accounts > Firebase Admin SDK > Generate new private key_. ### Adding Firebase credentials To add your Firebase credentials to Astro, create an `.env` file in the root of your project with the following variables: ``` FIREBASE_PRIVATE_KEY_ID=YOUR_PRIVATE_KEY_ID FIREBASE_PRIVATE_KEY=YOUR_PRIVATE_KEY FIREBASE_PROJECT_ID=YOUR_PROJECT_ID FIREBASE_CLIENT_EMAIL=YOUR_CLIENT_EMAIL FIREBASE_CLIENT_ID=YOUR_CLIENT_ID FIREBASE_AUTH_URI=YOUR_AUTH_URI FIREBASE_TOKEN_URI=YOUR_TOKEN_URI FIREBASE_AUTH_CERT_URL=YOUR_AUTH_CERT_URL FIREBASE_CLIENT_CERT_URL=YOUR_CLIENT_CERT_URL ``` Now, these environment variables are available for use in your project. If you would like to have IntelliSense for your Firebase environment variables, edit or create the file `env.d.ts` in your `src/` directory and configure your types: ``` interface ImportMetaEnv { readonly FIREBASE_PRIVATE_KEY_ID: string; readonly FIREBASE_PRIVATE_KEY: string; readonly FIREBASE_PROJECT_ID: string; readonly FIREBASE_CLIENT_EMAIL: string; readonly FIREBASE_CLIENT_ID: string; readonly FIREBASE_AUTH_URI: string; readonly FIREBASE_TOKEN_URI: string; readonly FIREBASE_AUTH_CERT_URL: string readonly FIREBASE_CLIENT_CERT_URL: string; } interface ImportMeta { readonly env: ImportMetaEnv; } ``` :::tip Read more about [environment variables](/en/guides/environment-variables/) and `.env` files in Astro. ::: Your project should now include these new files: \- src/ - \*\*env.d.ts\*\* - \*\*.env\*\* - astro.config.mjs - package.json ### Installing dependencies To connect Astro with Firebase, install the following packages using the single command below for your preferred package manager: - `firebase` - the Firebase SDK for the client side - `firebase-admin` - the Firebase Admin SDK for the server side \`\`\`shell npm install firebase firebase-admin \`\`\` \`\`\`shell pnpm add firebase firebase-admin \`\`\` \`\`\`shell yarn add firebase firebase-admin \`\`\` Next, create a folder named `firebase` in the `src/` directory and add two new files to this folder: `client.ts` and `server.ts`. In `client.ts`, add the following code to initialize Firebase in the client using your web app credentials and the `firebase` package: ``` const firebaseConfig = { apiKey: "my-public-api-key", authDomain: "my-auth-domain", projectId: "my-project-id", storageBucket: "my-storage-bucket", messagingSenderId: "my-sender-id", appId: "my-app-id", }; ``` :::note Remember to replace the `firebaseConfig` object with your own web app credentials. ::: In `server.ts`, add the following code to initialize Firebase in the server using your project credentials and the `firebase-admin` package: ``` const activeApps = getApps(); const serviceAccount = { type: "service_account", project_id: import.meta.env.FIREBASE_PROJECT_ID, private_key_id: import.meta.env.FIREBASE_PRIVATE_KEY_ID, private_key: import.meta.env.FIREBASE_PRIVATE_KEY, client_email: import.meta.env.FIREBASE_CLIENT_EMAIL, client_id: import.meta.env.FIREBASE_CLIENT_ID, auth_uri: import.meta.env.FIREBASE_AUTH_URI, token_uri: import.meta.env.FIREBASE_TOKEN_URI, auth_provider_x509_cert_url: import.meta.env.FIREBASE_AUTH_CERT_URL, client_x509_cert_url: import.meta.env.FIREBASE_CLIENT_CERT_URL, }; const initApp = () => { if (import.meta.env.PROD) { console.info('PROD env detected. Using default service account.') // Use default config in firebase functions. Should be already injected in the server by Firebase. return initializeApp() } console.info('Loading service account from env.') return initializeApp({ credential: cert(serviceAccount as ServiceAccount) }) } ``` :::note Remember to replace the `serviceAccount` object with your own project credentials. ::: Finally, your project should now include these new files: \- src - env.d.ts - firebase - \*\*client.ts\*\* - \*\*server.ts\*\* - .env - astro.config.mjs - package.json ## Adding authentication with Firebase ### Prerequisites - An Astro project [initialized with Firebase](#initializing-firebase-in-astro). - A Firebase project with email/password authentication enabled in the Firebase console under _Authentication > Sign-in_ method. ### Creating auth server endpoints Firebase authentication in Astro requires the following three [Astro server endpoints](/en/guides/endpoints/): - `GET /api/auth/signin` - to sign in a user - `GET /api/auth/signout` - to sign out a user - `POST /api/auth/register` - to register a user Create three endpoints related to authentication in a new directory `src/pages/api/auth/`: `signin.ts`, `signout.ts` and `register.ts`. `signin.ts` contains the code to sign in a user using Firebase: ``` /* Get token from request headers */ const idToken = request.headers.get("Authorization")?.split("Bearer ")[1]; if (!idToken) { return new Response( "No token found", { status: 401 } ); } /* Verify id token */ try { await auth.verifyIdToken(idToken); } catch (error) { return new Response( "Invalid token", { status: 401 } ); } /* Create and set session cookie */ const fiveDays = 60 * 60 * 24 * 5 * 1000; const sessionCookie = await auth.createSessionCookie(idToken, { expiresIn: fiveDays, }); cookies.set("__session", sessionCookie, { path: "/", }); return redirect("/dashboard"); }; ``` :::caution Firebase only allows the use of [one cookie, and it must be named `__session`](https://firebase.google.com/docs/hosting/manage-cache#using_cookies). Any other cookies the client sends will not be visible to your application. ::: :::note This is a basic implementation of the signin endpoint. You can add more logic to this endpoint to suit your needs. ::: `signout.ts` contains the code to log out a user by deleting the session cookie: ``` return redirect("/signin"); }; ``` :::note This is a basic implementation of the signout endpoint. You can add more logic to this endpoint to suit your needs. ::: `register.ts` contains the code to register a user using Firebase: ``` /* Get form data */ const formData = await request.formData(); const email = formData.get("email")?.toString(); const password = formData.get("password")?.toString(); const name = formData.get("name")?.toString(); if (!email || !password || !name) { return new Response( "Missing form data", { status: 400 } ); } /* Create user */ try { await auth.createUser({ email, password, displayName: name, }); } catch (error: any) { return new Response( "Something went wrong", { status: 400 } ); } return redirect("/signin"); }; ``` :::note This is a basic implementation of the register endpoint. You can add more logic to this endpoint to suit your needs. ::: After creating server endpoints for authentication, your project directory should now include these new files: \- src - env.d.ts - firebase - client.ts - server.ts - pages - api - auth - \*\*signin.ts\*\* - \*\*signout.ts\*\* - \*\*register.ts\*\* - .env - astro.config.mjs - package.json ### Creating pages Create the pages that will use the Firebase endpoints: - `src/pages/register` - will contain a form to register a user - `src/pages/signin` - will contain a form to sign in a user - `src/pages/dashboard` - will contain a dashboard that can only be accessed by authenticated users The example `src/pages/register.astro` below includes a form that will send a `POST` request to the `/api/auth/register` endpoint. This endpoint will create a new user using the data from the form and then will redirect the user to the `/signin` page. ``` --- ---

Register

Already have an account? Sign in

``` `src/pages/signin.astro` uses the Firebase server app to verify the user's session cookie. If the user is authenticated, the page will redirect the user to the `/dashboard` page. The example page below contains a form that will send a `POST` request to the `/api/auth/signin` endpoint with the ID token generated by the Firebase client app. The endpoint will verify the ID token and create a new session cookie for the user. Then, the endpoint will redirect the user to the `/dashboard` page. ``` --- /* Check if the user is authenticated */ const auth = getAuth(app); if (Astro.cookies.has("__session")) { const sessionCookie = Astro.cookies.get("__session")!.value; const decodedCookie = await auth.verifySessionCookie(sessionCookie); if (decodedCookie) { return Astro.redirect("/dashboard"); } } ---

Sign in

New here? Create an account

``` `src/pages/dashboard.astro` will verify the user's session cookie using the Firebase server app. If the user is not authenticated, the page will redirect the user to the `/signin` page. The example page below display the user's name and a button to sign out. Clicking the button will send a `GET` request to the `/api/auth/signout` endpoint. The endpoint will delete the user's session cookie and redirect the user to the `/signin` page. ``` --- const auth = getAuth(app); /* Check current session */ if (!Astro.cookies.has("__session")) { return Astro.redirect("/signin"); } const sessionCookie = Astro.cookies.get("__session")!.value; const decodedCookie = await auth.verifySessionCookie(sessionCookie); const user = await auth.getUser(decodedCookie.uid); if (!user) { return Astro.redirect("/signin"); } ---

Welcome {user.displayName}

We are happy to see you here

``` ### Adding OAuth providers To add OAuth providers to your app, you need to enable them in the Firebase console. In the Firebase console, go to the **Authentication** section and click on the **Sign-in method** tab. Then, click on the **Add a new provider** button and enable the providers you want to use. The example below uses the **Google** provider. Edit the `signin.astro` page to add: - a button to sign in with Google underneath the existing form - an event listener on the button to handle the sign in process in the existing ` ``` When clicked, the Google sign in button will open a popup window to sign in with Google. Once the user signs in, it will send a `POST` request to the `/api/auth/signin` endpoint with the ID token generated by OAuth provider. The endpoint will verify the ID token and create a new session cookie for the user. Then, the endpoint will redirect the user to the `/dashboard` page. ## Connecting to Firestore database ### Prerequisites - An Astro project initialized with Firebase as described in the [Initializing Firebase in Astro](#initializing-firebase-in-astro) section. - A Firebase project with a Firestore database. You can follow the [Firebase documentation to create a new project and set up a Firestore database](https://firebase.google.com/docs/firestore/quickstart). In this recipe, the Firestore collection will be called **friends** and will contain documents with the following fields: - `id`: autogenerated by Firestore - `name`: a string field - `age`: a number field - `isBestFriend`: a boolean field ### Creating the server endpoints Create two new files in a new directory `src/pages/api/friends/`: `index.ts` and `[id].ts`. These will create two server endpoints to interact with the Firestore database in the following ways: - `POST /api/friends`: to create a new document in the friends collection. - `POST /api/friends/:id`: to update a document in the friends collection. - `DELETE /api/friends/:id`: to delete a document in the friends collection. `index.ts` will contain the code to create a new document in the friends collection: ``` const name = formData.get("name")?.toString(); const age = formData.get("age")?.toString(); const isBestFriend = formData.get("isBestFriend") === "on"; if (!name || !age) { return new Response("Missing required fields", { status: 400, }); } try { const db = getFirestore(app); const friendsRef = db.collection("friends"); await friendsRef.add({ name, age: parseInt(age), isBestFriend, }); } catch (error) { return new Response("Something went wrong", { status: 500, }); } return redirect("/dashboard"); }; ``` :::note This is a basic implementation of the `friends` endpoint. You can add more logic to this endpoint to suit your needs. ::: `[id].ts` will contain the code to update and delete a document in the friends collection: ``` const db = getFirestore(app); const friendsRef = db.collection("friends"); const name = formData.get("name")?.toString(); const age = formData.get("age")?.toString(); const isBestFriend = formData.get("isBestFriend") === "on"; if (!name || !age) { return new Response("Missing required fields", { status: 400, }); } if (!params.id) { return new Response("Cannot find friend", { status: 404, }); } try { await friendsRef.doc(params.id).update({ name, age: parseInt(age), isBestFriend, }); } catch (error) { return new Response("Something went wrong", { status: 500, }); } return redirect("/dashboard"); }; } try { await friendsRef.doc(params.id).delete(); } catch (error) { return new Response("Something went wrong", { status: 500, }); } return redirect("/dashboard"); }; ``` :::note This is a basic implementation of the `friends/:id` endpoint. You can add more logic to this endpoint to suit your needs. ::: After creating server endpoints for Firestore, your project directory should now include these new files: \- src - env.d.ts - firebase - client.ts - server.ts - pages - api - friends - \*\*index.ts\*\* - \*\*\[id\].ts\*\* - .env - astro.config.mjs - package.json ### Creating pages Create the pages that will use the Firestore endpoints: - `src/pages/add.astro` - will contain a form to add a new friend. - `src/pages/edit/[id].astro` - will contain a form to edit a friend and a button to delete a friend. - `src/pages/friend/[id].astro` - will contain the details of a friend. - `src/pages/dashboard.astro` - will display a list of friends. #### Add a new record The example `src/pages/add.astro` below includes a form that will send a `POST` request to the `/api/friends` endpoint. This endpoint will create a new friend using the data from the form and then will redirect the user to the `/dashboard` page. ``` --- ---

Add a new friend

``` #### Edit or Delete a record `src/pages/edit/[id].astro` will contain a form to edit a friend data and a button to delete a friend. On submit, this page will send a `POST` request to the `/api/friends/:id` endpoint to update a friend data. If the user clicks the delete button, this page will send a `DELETE` request to the `/api/friends/:id` endpoint to delete a friend. ``` --- interface Friend { name: string; age: number; isBestFriend: boolean; } const { id } = Astro.params; if (!id) { return Astro.redirect("/404"); } const db = getFirestore(app); const friendsRef = db.collection("friends"); const friendSnapshot = await friendsRef.doc(id).get(); if (!friendSnapshot.exists) { return Astro.redirect("/404"); } const friend = friendSnapshot.data() as Friend; ---

Edit {friend.name}

Here you can edit or delete your friend's data.

``` #### Display an individual record `src/pages/friend/[id].astro` will display the details of a friend. ``` --- interface Friend { name: string; age: number; isBestFriend: boolean; } const { id } = Astro.params; if (!id) { return Astro.redirect("/404"); } const db = getFirestore(app); const friendsRef = db.collection("friends"); const friendSnapshot = await friendsRef.doc(id).get(); if (!friendSnapshot.exists) { return Astro.redirect("/404"); } const friend = friendSnapshot.data() as Friend; ---

{friend.name}

Age: {friend.age}

Is best friend: {friend.isBestFriend ? "Yes" : "No"}

``` #### Display a list of records with an edit button Finally, `src/pages/dashboard.astro` will display a list of friends. Each friend will have a link to their details page and an edit button that will redirect the user to the edit page. ``` --- interface Friend { id: string; name: string; age: number; isBestFriend: boolean; } const db = getFirestore(app); const friendsRef = db.collection("friends"); const friendsSnapshot = await friendsRef.get(); const friends = friendsSnapshot.docs.map((doc) => ({ id: doc.id, ...doc.data(), })) as Friend[]; ---

Friends

    { friends.map((friend) => (
  • {friend.name} ({friend.age}) {friend.isBestFriend ? "Bestie" : "Friend"} Edit
  • )) }
``` After creating all the pages, you should have the following file structure: \- src - env.d.ts - firebase - client.ts - server.ts - pages - dashboard.astro - add.astro - edit - \[id\].astro - friend - \[id\].astro - api - friends - index.ts - \[id\].ts - .env - astro.config.mjs - package.json ## Community Resources - [Astro and Firebase SSR app example](https://github.com/kevinzunigacuellar/astro-firebase) - [Using Firebase Realtime Database in Astro with Vue: A Step-by-Step Guide](https://www.launchfa.st/blog/vue-astro-firebase-realtime-database) ## [Neon Postgres & Astro](https://docharvest.github.io/docs/astro/guides/backend/neon/) Contents astro Neon Postgres & Astro Astro Neon Postgres & Astro [Neon](https://neon.tech) is a fully managed serverless Postgres database. It separates storage and compute to offer autoscaling, branching, and bottomless storage. ## Adding Neon to your Astro project ### Prerequisites - A [Neon](https://console.neon.tech/signup) account with a created project - Neon database connection string - An Astro project with [on-demand rendering (SSR)](/en/guides/on-demand-rendering/) enabled ### Environment configuration To use Neon with Astro, you will need to set a Neon environment variable. Create or edit the `.env` file in your project root, and add the following code, replacing your own project details: ``` NEON_DATABASE_URL="postgresql://:@.neon.tech:/?sslmode=require" ``` For better TypeScript support, define environment variables in a `src/env.d.ts` file: ``` interface ImportMetaEnv { readonly NEON_DATABASE_URL: string; } interface ImportMeta { readonly env: ImportMetaEnv; } ``` Learn more about [environment variables](/en/guides/environment-variables/) and `.env` files in Astro. ### Installing dependencies Install the `@neondatabase/serverless` package to connect to Neon: ``` npm install @neondatabase/serverless ``` ### Creating a Neon client Create a new file `src/lib/neon.ts` with the following code to initialize your Neon client: ## Querying your Neon database You can now use the Neon client to query your database from any `.astro` component. The following example fetches the current time from the Postgres database: ``` --- const response = await sql`SELECT NOW() as current_time`; const currentTime = response[0].current_time; ---

Current Time

The time is: {currentTime}

``` ## Database branching with Neon Neon's branching feature lets you create copies of your database for development or testing. Use this in your Astro project by creating different environment variables for each branch: ``` NEON_DATABASE_URL=your_development_branch_url ``` ``` NEON_DATABASE_URL=your_production_branch_url ``` ## Resources - [Neon documentation](https://neon.tech/docs/introduction) - [Neon serverless driver GitHub](https://github.com/neondatabase/serverless) - [Connect an Astro site or application to Neon Postgres](https://neon.tech/docs/guides/astro) ## [Prisma Postgres & Astro](https://docharvest.github.io/docs/astro/guides/backend/prisma-postgres/) Contents astro Prisma Postgres & Astro Astro Prisma Postgres & Astro [Prisma Postgres](https://www.prisma.io/) is a fully managed, serverless Postgres database built for modern web apps. ## Connect with Prisma ORM (Recommended) [Prisma ORM](https://www.prisma.io/orm) is the recommended way to connect to your Prisma Postgres database. It provides type-safe queries, migrations, and global performance. ### Prerequisites - An Astro project with an adapter installed to enable [on-demand rendering (SSR)](/en/guides/on-demand-rendering/). ### Install dependencies and initialize Prisma Run the following commands to install the necessary Prisma dependencies: ``` npm install prisma tsx --save-dev npm install @prisma/adapter-pg @prisma/client ``` Once installed, initialize Prisma in your project with the following command: ``` npx prisma init --db --output ./generated ``` You'll need to answer a few questions while setting up your Prisma Postgres database. Select the region closest to your location and a memorable name for your database, like "My Astro Project." This will create: - A `prisma/` directory with a `schema.prisma` file - A `.env` file with a `DATABASE_URL` already set ### Define a Model Even if you don't need any specific data models yet, Prisma requires at least one model in the schema in order to generate a client and apply migrations. The following example defines a `Post` model as a placeholder. Add the model to your schema to get started. You can safely delete or replace it later with models that reflect your actual data. ``` generator client { provider = "prisma-client" output = "./generated" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } model Post { id Int @id @default(autoincrement()) title String content String? published Boolean @default(false) } ``` Learn more about configuring your Prisma ORM setup in the [Prisma schema reference](https://www.prisma.io/docs/concepts/components/prisma-schema). ### Generate client Run the following command to generate the Prisma Client from your schema: ``` npx prisma generate ``` ### Generate migration files Run the following command to create the database tables and generate the Prisma Client from your schema. This will also create a `prisma/migrations/` directory with migration history files. ``` npx prisma migrate dev --name init ``` ### Create a Prisma Client Inside of `/src/lib`, create a `prisma.ts` file. This file will initialize and export your Prisma Client instance so you can query your database throughout your Astro project. ``` const connectionString = import.meta.env.DATABASE_URL; const adapter = new PrismaPg({ connectionString }); const prisma = new PrismaClient({ adapter }); ``` ### Querying and displaying data The following example shows fetching only your published posts with the Prisma Client sorted by `id`, and then displaying titles and post content in your Astro template: ``` --- const posts = await prisma.post.findMany({ where: { published: true }, orderBy: { id: 'desc' } }); --- Published Posts

Published Posts

    {posts.map((post) => (
  • {post.title}

    {post.content &&

    {post.content}

    }
  • ))}
``` It is best practice to handle queries in an API route. For more information on how to use Prisma ORM in your Astro project, see the [Astro + Prisma ORM Guide](https://www.prisma.io/docs/guides/astro). ## Connect with Other ORMs and Libraries You can connect to Prisma Postgres via direct TCP using any other ORM, database library, or tool of your choice. Create a direct connection string in your Prisma Console to get started. ### Prerequisites - An Astro project with an adapter installed to enable [on-demand rendering (SSR)](/en/guides/on-demand-rendering/). - A [Prisma Postgres](https://pris.ly/ppg) database with a TCP enabled connection string ### Install dependencies This example uses [`pg`, a PostgreSQL client for Node.js](https://github.com/brianc/node-postgres) to make a direct TCP connection. Run the following command to install the `pg` package: ``` npm install pg ``` ### Query your database client Provide your connection string to the `pg` client to communicate with your SQL server and fetch data from your database. The following example of creating a table and inserting data can be used to validate your query URL and TCP connection: ``` --- const client = new Client({ connectionString: import.meta.env.DATABASE_URL, ssl: { rejectUnauthorized: false } }); await client.connect(); await client.query(` CREATE TABLE IF NOT EXISTS posts ( id SERIAL PRIMARY KEY, title TEXT UNIQUE, content TEXT ); INSERT INTO posts (title, content) VALUES ('Hello', 'World') ON CONFLICT (title) DO NOTHING; `); const { rows } = await client.query('SELECT * FROM posts'); await client.end(); ---

Posts

{rows[0].title}: {rows[0].content}

``` ## Official Resources - [Astro + Prisma ORM Guide](https://www.prisma.io/docs/guides/astro) ## [Scalekit & Astro](https://docharvest.github.io/docs/astro/guides/backend/scalekit/) Contents astro Scalekit & Astro Astro Scalekit & Astro [Scalekit](https://scalekit.com/) is an authentication platform built for B2B and AI applications. It provides social login, enterprise SSO, magic links, and more — managing the full OAuth 2.0 / OIDC flow so you get back tokens and a user profile without building any login UI. A single Scalekit environment supports multiple applications (for example, `app.yourcompany.com` and `docs.yourcompany.com`), so your users authenticate once and share the same session across all your properties. ## Initializing Scalekit in Astro ### Prerequisites - A Scalekit account and environment. If you don't have one, you can sign up for free at [scalekit.com](https://scalekit.com/) and create a new environment. - An Astro project with [`output: 'server'` for on-demand rendering](/en/guides/on-demand-rendering/) enabled. - Scalekit credentials for your environment. You can find these in the **Settings > API Credentials** section of your Scalekit dashboard. - `SCALEKIT_ENVIRONMENT_URL`: The URL of your Scalekit environment. - `SCALEKIT_CLIENT_ID`: Your Scalekit client ID. - `SCALEKIT_CLIENT_SECRET`: Your Scalekit client secret. - `SCALEKIT_REDIRECT_URI`: The callback URL Scalekit will redirect to after login (e.g. `http://localhost:4321/api/auth/callback`). Register this in your Scalekit dashboard under **Settings > Redirects**. ### Adding Scalekit credentials To add your Scalekit credentials to your Astro project, add the following to your `.env` file: ``` SCALEKIT_ENVIRONMENT_URL=YOUR_SCALEKIT_ENVIRONMENT_URL SCALEKIT_CLIENT_ID=YOUR_SCALEKIT_CLIENT_ID SCALEKIT_CLIENT_SECRET=YOUR_SCALEKIT_CLIENT_SECRET SCALEKIT_REDIRECT_URI=http://localhost:4321/api/auth/callback ``` Now, these environment variables are available in your project. If you would like to have IntelliSense for your environment variables, edit or create the `env.d.ts` in your `src/` directory and add the following: ``` /// interface ImportMetaEnv { readonly SCALEKIT_ENVIRONMENT_URL: string; readonly SCALEKIT_CLIENT_ID: string; readonly SCALEKIT_CLIENT_SECRET: string; readonly SCALEKIT_REDIRECT_URI: string; } interface ImportMeta { readonly env: ImportMetaEnv; } declare namespace App { interface Locals { user?: { sub: string; email?: string; name?: string; }; } } ``` Read more about [environment variables](/en/guides/environment-variables/) and `.env` files in Astro. Your project should now include these files: \- src/ - \*\*env.d.ts\*\* - \*\*.env\*\* - astro.config.mjs - package.json ### Installing dependencies To connect to Scalekit, install `@scalekit-sdk/node` in your project. \`\`\`shell npm install @scalekit-sdk/node \`\`\` \`\`\`shell pnpm add @scalekit-sdk/node \`\`\` \`\`\`shell yarn add @scalekit-sdk/node \`\`\` Next, create a folder named `lib` in your `src/` directory and add a Scalekit client file: Now, your project should include these files: \- src/ - lib/ - \*\*scalekit.ts\*\* - env.d.ts - .env - package.json ## Adding authentication with Scalekit Scalekit handles authentication through a standard OAuth 2.0 / OIDC redirect flow. Your app sends users to Scalekit, they sign in, and Scalekit redirects them back to your callback URL with an authorization code you exchange for tokens. This guide uses the Authorization Code flow with a client secret, which is appropriate for server-side rendered applications. :::tip If you prefer not to use a client secret, Scalekit also supports the [PKCE flow](https://docs.scalekit.com/authenticate/fsa/multiapp/single-page-app/). The [Scalekit developer docs site](https://github.com/scalekit-inc/developer-docs) is an open-source Astro project that demonstrates a full PKCE implementation without an SDK. ::: ### Creating auth server endpoints To add authentication to your project, you will need to create three server endpoints: - `GET /api/auth/login`: redirects users to Scalekit to sign in. - `GET /api/auth/callback`: exchanges the authorization code for tokens and sets session cookies. - `GET /api/auth/logout`: clears session cookies and ends the Scalekit session. Create these endpoints in the `src/pages/api/auth/` directory of your project. Your project should now include these new files: \- src/ - lib/ - scalekit.ts - pages/ - api/ - auth/ - \*\*login.ts\*\* - \*\*callback.ts\*\* - \*\*logout.ts\*\* - env.d.ts - .env - astro.config.mjs - package.json `login.ts` generates an authorization URL and redirects the user to Scalekit to sign in. ``` return Response.redirect(url); }; ``` `callback.ts` receives the authorization code from Scalekit, exchanges it for tokens, and stores them in HttpOnly cookies. ``` const code = url.searchParams.get("code"); if (!code) { return Response.redirect(new URL("/", request.url).origin); } const { user, idToken, accessToken, refreshToken } = await scalekit.authenticateWithCode(code, REDIRECT_URI); const secure = url.protocol === "https:"; const cookieOptions = { httpOnly: true, path: "/", sameSite: "lax" as const, secure }; cookies.set("sk-id-token", idToken, cookieOptions); cookies.set("sk-access-token", accessToken, cookieOptions); cookies.set("sk-refresh-token", refreshToken, cookieOptions); return Response.redirect(new URL("/", request.url).origin); }; ``` `logout.ts` clears the session cookies and redirects to Scalekit's logout endpoint to end the Scalekit session. ``` cookies.delete("sk-id-token", { path: "/" }); cookies.delete("sk-access-token", { path: "/" }); cookies.delete("sk-refresh-token", { path: "/" }); const logoutUrl = scalekit.getLogoutUrl({ idTokenHint: idToken, postLogoutRedirectUri: new URL("/", request.url).origin, }); return Response.redirect(logoutUrl); }; ``` :::note The `postLogoutRedirectUri` passed to `getLogoutUrl()` must be registered as an allowed post-logout redirect URI in your Scalekit dashboard, otherwise Scalekit will reject the logout request. ::: ### Adding session middleware Create a `src/middleware.ts` file to validate the access token on every request and populate `Astro.locals.user` with the authenticated user's profile. When the access token has expired, the middleware automatically refreshes it using the refresh token. ``` if (accessToken) { try { const claims = await scalekit.validateToken(accessToken); context.locals.user = { sub: claims.sub, email: claims.email, name: claims.name, }; } catch { // Access token invalid or expired — try to refresh const refreshToken = context.cookies.get("sk-refresh-token")?.value; if (refreshToken) { try { const { accessToken: newToken } = await scalekit.refreshAccessToken(refreshToken); const secure = new URL(context.request.url).protocol === "https:"; context.cookies.set("sk-access-token", newToken, { httpOnly: true, path: "/", sameSite: "lax", secure, }); const claims = await scalekit.validateToken(newToken); context.locals.user = { sub: claims.sub, email: claims.email, name: claims.name, }; } catch { // Refresh failed — clear session cookies context.cookies.delete("sk-id-token", { path: "/" }); context.cookies.delete("sk-access-token", { path: "/" }); context.cookies.delete("sk-refresh-token", { path: "/" }); } } } } return next(); }); ``` ### Creating an authenticated page Now that your middleware populates `Astro.locals.user`, you can create pages that show different content based on authentication state. `dashboard.astro` is a page that is only accessible to authenticated users. It reads the user from `Astro.locals`, set by the middleware, and redirects to the home page if the user is not signed in. ``` --- const user = Astro.locals.user; if (!user) { return Astro.redirect("/"); } ---

Welcome, {user.name ?? user.email}

You are signed in.

Sign out
``` ## Community Resources - [Scalekit Node.js SDK documentation](https://github.com/scalekit-inc/scalekit-sdk-node) - [Astro blog tutorial with Scalekit authentication (Authorization Code flow)](https://github.com/scalekit-developers/astro-scalekit-auth-example) - [Scalekit developer docs site source (PKCE flow, no SDK)](https://github.com/scalekit-inc/developer-docs) ## [Monitor your Astro Site with Sentry](https://docharvest.github.io/docs/astro/guides/backend/sentry/) Contents astro Monitor your Astro Site with Sentry Astro Monitor your Astro Site with Sentry ```` Once you've configured your `sourceMapsUploadOptions` and added your `dsn`, the SDK will automatically capture and send errors and performance events to Sentry. ## Test your setup Add the following ` ```` To view and resolve the recorded error, log into [sentry.io](https://sentry.io/) and open your project. ## [Supabase & Astro](https://docharvest.github.io/docs/astro/guides/backend/supabase/) Contents astro Supabase & Astro Astro Supabase & Astro [Supabase](https://supabase.com/) is an open source Firebase alternative. It provides a Postgres database, authentication, edge functions, realtime subscriptions, and storage. ## Initializing Supabase in Astro ### Prerequisites - A Supabase project. If you don't have one, you can sign up for free at [supabase.com](https://supabase.com/) and create a new project. - An Astro project with [`output: 'server'` for on-demand rendering](/en/guides/on-demand-rendering/) enabled. - Supabase credentials for your project. You can find these in the **Settings > API** tab of your Supabase project. - `SUPABASE_URL`: The URL of your Supabase project. - `SUPABASE_ANON_KEY`: The anonymous key for your Supabase project. ### Adding Supabase credentials To add your Supabase credentials to your Astro project, add the following to your `.env` file: ``` SUPABASE_URL=YOUR_SUPABASE_URL SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY ``` Now, these environment variables are available in your project. If you would like to have IntelliSense for your environment variables, edit or create the `env.d.ts` in your `src/` directory and add the following: ``` interface ImportMetaEnv { readonly SUPABASE_URL: string readonly SUPABASE_ANON_KEY: string } interface ImportMeta { readonly env: ImportMetaEnv } ``` :::tip Read more about [environment variables](/en/guides/environment-variables/) and `.env` files in Astro. ::: Your project should now include these files: \- src/ - \*\*env.d.ts\*\* - \*\*.env\*\* - astro.config.mjs - package.json ### Installing dependencies To connect to Supabase, you will need to install `@supabase/supabase-js` in your project. \`\`\`shell npm install @supabase/supabase-js \`\`\` \`\`\`shell pnpm add @supabase/supabase-js \`\`\` \`\`\`shell yarn add @supabase/supabase-js \`\`\` Next, create a folder named `lib` in your `src/` directory. This is where you will add your Supabase client. In `supabase.ts`, add the following to initialize your Supabase client: Now, your project should include these files: \- src/ - lib/ - \*\*supabase.ts\*\* - env.d.ts - .env - astro.config.mjs - package.json ## Adding authentication with Supabase Supabase provides authentication out of the box. It supports email/password authentication and OAuth authentication with many providers including GitHub, Google, and several others. ### Prerequisites - An Astro project [initialized with Supabase](#initializing-supabase-in-astro). - A Supabase project with email/password authentication enabled. You can enable this in the **Authentication > Providers** tab of your Supabase project. ### Creating auth server endpoints To add authentication to your project, you will need to create a few server endpoints. These endpoints will be used to register, sign in, and sign out users. - `POST /api/auth/register`: to register a new user. - `POST /api/auth/signin`: to sign in a user. - `GET /api/auth/signout`: to sign out a user. Create these endpoints in the `src/pages/api/auth` directory of your project. If you are using `static` rendering mode, you must specify `export const prerender = false` at the top of each file to render these endpoints on demand. Your project should now include these new files: \- src/ - lib/ - supabase.ts - pages/ - api/ - auth/ - \*\*signin.ts\*\* - \*\*signout.ts\*\* - \*\*register.ts\*\* - env.d.ts - .env - astro.config.mjs - package.json `register.ts` creates a new user in Supabase. It accepts a `POST` request with the an email and password. It then uses the Supabase SDK to create a new user. ``` // With `output: 'static'` configured: // export const prerender = false; const email = formData.get("email")?.toString(); const password = formData.get("password")?.toString(); if (!email || !password) { return new Response("Email and password are required", { status: 400 }); } const { error } = await supabase.auth.signUp({ email, password, }); if (error) { return new Response(error.message, { status: 500 }); } return redirect("/signin"); }; ``` `signin.ts` signs in a user. It accepts a `POST` request with the an email and password. It then uses the Supabase SDK to sign in the user. ``` // With `output: 'static'` configured: // export const prerender = false; const email = formData.get("email")?.toString(); const password = formData.get("password")?.toString(); if (!email || !password) { return new Response("Email and password are required", { status: 400 }); } const { data, error } = await supabase.auth.signInWithPassword({ email, password, }); if (error) { return new Response(error.message, { status: 500 }); } const { access_token, refresh_token } = data.session; cookies.set("sb-access-token", access_token, { path: "/", }); cookies.set("sb-refresh-token", refresh_token, { path: "/", }); return redirect("/dashboard"); }; ``` `signout.ts` signs out a user. It accepts a `GET` request and removes the user's access and refresh tokens. ``` // With `output: 'static'` configured: // export const prerender = false; cookies.delete("sb-refresh-token", { path: "/" }); return redirect("/signin"); }; ``` ### Creating auth pages Now that you have created your server endpoints, create the pages that will use them. - `src/pages/register`: contains a form to register a new user. - `src/pages/signin`: contains a form to sign in a user. - `src/pages/dashboard`: contains a page that is only accessible to authenticated users. Create these pages in the `src/pages` directory. Your project should now include these new files: \- src/ - lib/ - supabase.ts - pages/ - api/ - auth/ - signin.ts - signout.ts - register.ts - \*\*register.astro\*\* - \*\*signin.astro\*\* - \*\*dashboard.astro\*\* - env.d.ts - .env - astro.config.mjs - package.json `register.astro` contains a form to register a new user. It accepts an email and password and sends a `POST` request to `/api/auth/register`. ``` --- ---

Register

Already have an account? Sign in

``` `signin.astro` contains a form to sign in a user. It accepts an email and password and sends a `POST` request to `/api/auth/signin`. It also checks for the presence of the access and refresh tokens. If they are present, it redirects to the dashboard. ``` --- const { cookies, redirect } = Astro; const accessToken = cookies.get("sb-access-token"); const refreshToken = cookies.get("sb-refresh-token"); if (accessToken && refreshToken) { return redirect("/dashboard"); } ---

Sign in

New here? Create an account

``` `dashboard.astro` contains a page that is only accessible to authenticated users. It checks for the presence of the access and refresh tokens. If they are not present or are invalid, it redirects to the sign in page. ``` --- const accessToken = Astro.cookies.get("sb-access-token"); const refreshToken = Astro.cookies.get("sb-refresh-token"); if (!accessToken || !refreshToken) { return Astro.redirect("/signin"); } let session; try { session = await supabase.auth.setSession({ refresh_token: refreshToken.value, access_token: accessToken.value, }); if (session.error) { Astro.cookies.delete("sb-access-token", { path: "/", }); Astro.cookies.delete("sb-refresh-token", { path: "/", }); return Astro.redirect("/signin"); } } catch (error) { Astro.cookies.delete("sb-access-token", { path: "/", }); Astro.cookies.delete("sb-refresh-token", { path: "/", }); return Astro.redirect("/signin"); } const email = session.data.user?.email; ---

Welcome {email}

We are happy to see you here

``` ### Adding OAuth authentication To add OAuth authentication to your project, you will need to edit your Supabase client to enable authentication flow with `"pkce"`. You can read more about authentication flows in the [Supabase documentation](https://supabase.com/docs/guides/auth/server-side-rendering#understanding-the-authentication-flow). Next, in the Supabase dashboard, enable the OAuth provider you would like to use. You can find the list of supported providers in the **Authentication > Providers** tab of your Supabase project. The following example uses GitHub as the OAuth provider. To connect your project to GitHub, follow the steps in the [Supabase documentation](https://supabase.com/docs/guides/auth/social-login/auth-github). Then, create a new server endpoint to handle the OAuth callback at `src/pages/api/auth/callback.ts`. This endpoint will be used to exchange the OAuth code for an access and refresh token. ``` if (!authCode) { return new Response("No code provided", { status: 400 }); } const { data, error } = await supabase.auth.exchangeCodeForSession(authCode); if (error) { return new Response(error.message, { status: 500 }); } const { access_token, refresh_token } = data.session; cookies.set("sb-access-token", access_token, { path: "/", }); cookies.set("sb-refresh-token", refresh_token, { path: "/", }); return redirect("/dashboard"); }; ``` Next, edit the sign in page to include a new button to sign in with the OAuth provider. This button should send a `POST` request to `/api/auth/signin` with the `provider` set to the name of the OAuth provider. ``` --- const { cookies, redirect } = Astro; const accessToken = cookies.get("sb-access-token"); const refreshToken = cookies.get("sb-refresh-token"); if (accessToken && refreshToken) { return redirect("/dashboard"); } ---

Sign in

New here? Create an account

``` Finally, edit the sign in server endpoint to handle the OAuth provider. If the `provider` is present, it will redirect to the OAuth provider. Otherwise, it will sign in the user with the email and password. ``` const email = formData.get("email")?.toString(); const password = formData.get("password")?.toString(); const provider = formData.get("provider")?.toString(); const validProviders = ["google", "github", "discord"]; if (provider && validProviders.includes(provider)) { const { data, error } = await supabase.auth.signInWithOAuth({ provider: provider as Provider, options: { redirectTo: "http://localhost:4321/api/auth/callback" }, }); if (error) { return new Response(error.message, { status: 500 }); } return redirect(data.url); } if (!email || !password) { return new Response("Email and password are required", { status: 400 }); } const { data, error } = await supabase.auth.signInWithPassword({ email, password, }); if (error) { return new Response(error.message, { status: 500 }); } const { access_token, refresh_token } = data.session; cookies.set("sb-access-token", access_token, { path: "/", }); cookies.set("sb-refresh-token", refresh_token, { path: "/", }); return redirect("/dashboard"); }; ``` After creating the OAuth callback endpoint and editing the sign in page and server endpoint, your project should have the following file structure: \- src/ - lib/ - supabase.ts - pages/ - api/ - auth/ - signin.ts - signout.ts - register.ts - callback.ts - register.astro - signin.astro - dashboard.astro - env.d.ts - .env - astro.config.mjs - package.json ## Community Resources - [Getting into the holiday spirit with Astro, React, and Supabase](https://www.aleksandra.codes/astro-supabase) - [Astro and Supabase auth demo](https://github.com/kevinzunigacuellar/astro-supabase) ## [Turso & Astro](https://docharvest.github.io/docs/astro/guides/backend/turso/) Contents astro Turso & Astro Astro Turso & Astro ```` ## Querying your database To access information from your database, import `turso` and [execute a SQL query](https://docs.turso.tech/sdk/ts/reference#simple-query) inside any `.astro` component. The following example fetches all `posts` from your table, then displays a list of titles in a `` component: ```astro title="src/components/BlogIndex.astro" --- import { turso } from '../turso' const { rows } = await turso.execute('SELECT * FROM posts') ---
    {rows.map((post) => (
  • {post.title}
  • ))}
```` ### SQL Placeholders The `execute()` method can take [an object to pass variables to the SQL statement](https://docs.turso.tech/sdk/ts/reference#placeholders), such as `slug`, or pagination. The following example fetches a single entry from the `posts` table `WHERE` the `slug` is the retrieved value from `Astro.params`, then displays the title of the post. ``` --- import { turso } from '../turso' const { slug } = Astro.params const { rows } = await turso.execute({ sql: 'SELECT * FROM posts WHERE slug = ?', args: [slug!] }) ---

{rows[0].title}

``` ## Turso Resources - [Turso Docs](https://docs.turso.tech) - [Turso on GitHub](https://github.com/tursodatabase) - [Using Turso to serve a Server-side Rendered Astro blog's content](https://blog.turso.tech/using-turso-to-serve-a-server-side-rendered-astro-blogs-content-58caa6188bd5) ## [Xata & Astro](https://docharvest.github.io/docs/astro/guides/backend/xata/) Contents astro Xata & Astro Astro Xata & Astro [Xata](https://xata.io) is a **Serverless Data Platform** that combines the features of a relational database, a search engine, and an analytics engine by exposing a single consistent REST API. ## Adding a database with Xata ### Prerequisites - A [Xata](https://app.xata.io/signin) account with a created database. (You can use the sample database from the Web UI.) - An Access Token (`XATA_API_KEY`). - Your Database URL. After you update and initialize the [Xata CLI](https://xata.io/docs/getting-started/installation), you will have your API token in your `.env` file and database URL defined. By the end of the setup, you should have: ``` XATA_API_KEY=hash_key # Xata branch that will be used # if there's not a xata branch with # the same name as your git branch XATA_BRANCH=main ``` And the `databaseURL` defined: ``` { "databaseUrl": "https://your-database-url" } ``` ### Environment configuration To have IntelliSense and type safety for your environment variables, edit or create the file `env.d.ts` in your `src/` directory: ``` interface ImportMetaEnv { readonly XATA_API_KEY: string; readonly XATA_BRANCH?: string; } interface ImportMeta { readonly env: ImportMetaEnv; } ``` :::tip Read more about [environment variables](/en/guides/environment-variables/) and `.env` files in Astro. ::: Using the code generation from the Xata CLI and choosing the TypeScript option, generated an instance of the SDK for you, with types tailored to your database schema. Additionally, `@xata.io/client` was added to your `package.json`. Your Xata environment variables and database url were automatically pulled by the SDK instance, so there's no more setup work needed. Now, your project should have the following structure: \- src/ - \*\*xata.ts\*\* - \*\*env.d.ts\*\* - \*\*.env\*\* - astro.config.mjs - package.json - \*\*.xatarc\*\* ## Create your queries To query your posts, import and use `XataClient` class in a `.astro` file. The example below queries the first 50 posts from Xata's Sample Blog Database. ``` --- const xata = new XataClient({ apiKey: import.meta.env.XATA_API_KEY, branch: import.meta.env.XATA_BRANCH }); const { records } = await xata.db.Posts.getPaginated({ pagination: { size: 50 } }) ---
    {records.map((post) => (
  • {post.title}
  • ))}
``` It's important to note the SDK needs to be regenerated every time your schema changes. So, avoid making changes to the generated files the Xata CLI creates because once schema updates, your changes will be overwritten. ## Official Resources - [Xata Astro Starter](https://github.com/xataio/examples/tree/main/apps/getting-started-astro) - [Xata Docs: Quick Start Guide](https://xata.io/docs/getting-started/quickstart-astro) ## [Building Astro sites with AI tools](https://docharvest.github.io/docs/astro/guides/build-with-ai/) Contents astro Building Astro sites with AI tools Astro Building Astro sites with AI tools AI-powered editors and agentic coding tools generally have good knowledge of Astro's core APIs and concepts. However, some may use older APIs and may not be aware of newer features or recent changes to the framework. This guide covers how to enhance AI tools with up-to-date Astro knowledge and provides best practices for building Astro sites with AI assistance. ## Astro Docs MCP Server You can ensure your AI tools have current Astro knowledge through the Astro Docs MCP (Model Context Protocol) server. This provides real-time access to the latest documentation, helping AI tools avoid outdated recommendations and ensuring they understand current best practices. :::tip\[What is MCP?\] [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) is a standardized way for AI tools to access external tools and data sources. ::: Unlike AI models trained on static data, the MCP server provides access to the latest Astro documentation. The server is free, open-source, and runs remotely with nothing to install locally. The Astro Docs MCP server uses the [kapa.ai](https://www.kapa.ai/) API to maintain an up-to-date index of the Astro documentation. ### Server Details - **Name**: Astro Docs - **URL**: `https://mcp.docs.astro.build/mcp` - **Transport**: Streamable HTTP ### Installation The setup process varies depending on your AI development tool. You may see some tools refer to MCP servers as connectors, adapters, extensions, or plugins. #### Manual setup Many tools support a common JSON configuration format for MCP servers. If there are not specific instructions for your chosen tool, you may be able to add the Astro Docs MCP server by including the following configuration in your tool's MCP settings: \`\`\`json title="MCP Configuration" {3-6} { "mcpServers": { "Astro docs": { "type": "http", "url": "https://mcp.docs.astro.build/mcp" } } } \`\`\` \`\`\`json title="MCP Configuration" {3-7} { "mcpServers": { "Astro docs": { "type": "stdio", "command": "npx", "args": \["-y", "mcp-remote", "https://mcp.docs.astro.build/mcp"\] } } } \`\`\` \#### Claude Code CLI [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) is an agentic coding tool that runs on the command line. Enabling the Astro Docs MCP server allows it to access the latest documentation while generating Astro code. Install using the terminal command: ``` claude mcp add --transport http astro-docs https://mcp.docs.astro.build/mcp ``` [More info on using MCP servers with Claude Code](https://docs.anthropic.com/en/docs/claude-code/mcp) #### Claude Code GitHub Action Claude Code also provides a GitHub Action that can be used to run commands in response to GitHub events. Enabling the Astro Docs MCP server allows it to access the latest documentation while answering questions in comments or generating Astro code. You can configure it to use the Astro Docs MCP server for documentation access by adding the following to the workflow file: ``` # ...rest of your workflow configuration - uses: anthropics/claude-code-action@beta with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} mcp_config: | { "mcpServers": { "astro-docs": { "type": "http", "url": "https://mcp.docs.astro.build/mcp" } } } allowed_tools: "mcp__astro-docs__search_astro_docs" ``` [More info on using MCP servers with the Claude Code GitHub Action](https://github.com/anthropics/claude-code-action?tab=readme-ov-file#using-custom-mcp-configuration) #### Codex CLI Codex CLI is a command-line AI coding tool that can use the Astro Docs MCP server to access documentation while generating Astro code. You can configure MCP servers at the global level in the `~/.codex/config.toml` file, or in a `.codex/config.toml` file in a project root. ``` [mcp_servers.astro-docs] command = "npx" args = ["-y", "mcp-remote", "https://mcp.docs.astro.build/mcp"] ``` [More info on using MCP servers with Codex CLI](https://developers.openai.com/codex/mcp) #### Cursor [Cursor](https://cursor.com) is an AI code editor. Adding the Astro Docs MCP server allows Cursor to access the latest Astro documentation while performing development tasks. Install by clicking the button below: Add to Cursor [More info on using MCP servers with Cursor](https://docs.cursor.com/context/mcp) #### Visual Studio Code [Visual Studio Code](https://code.visualstudio.com) supports MCP servers when using Copilot Chat. Adding the Astro Docs MCP server allows VS Code to access the latest Astro documentation when answering questions or performing coding tasks. Install by clicking the button below: Add to VS Code [More info on using MCP servers with VS Code](https://code.visualstudio.com/docs/copilot/chat/mcp-servers#_add-an-mcp-server) #### Warp [Warp](https://warp.dev) (formerly Warp Terminal) is an agent development environment built for coding with multiple AI agents. Adding the Astro Docs MCP server allows Warp to access the latest Astro documentation when answering questions or performing coding tasks. 1. Open your Warp settings and go to AI > MCP Servers > Manage MCP Servers. 2. Click "Add". 3. Enter the following configuration. You can optionally configure the Astro MCP server to activate on startup using the `start_on_launch` flag: ``` { "mcpServers": { "Astro docs": { "command": "npx", "args": ["-y", "mcp-remote", "https://mcp.docs.astro.build/mcp"], "env": {}, "working_directory": null, "start_on_launch": true } } } ``` 4. Click "Save". [More info on using MCP servers with Warp](https://docs.warp.dev/knowledge-and-collaboration/mcp) #### Claude.ai / Claude Desktop [Claude.ai](https://claude.ai) is a general-purpose AI assistant. Adding the Astro Docs MCP server allows it to access the latest documentation when answering Astro questions or generating Astro code. 1. Navigate to the [Claude.ai connector settings](https://claude.ai/settings/connectors). 2. Click "Add custom connector". You may need to scroll down to find this option. 3. Enter the server URL: `https://mcp.docs.astro.build/mcp`. 4. Set the name to "Astro docs". [More info on using MCP servers with Claude.ai](https://support.anthropic.com/en/articles/10168395-setting-up-integrations-on-claude-ai#h_cda40ecb32) #### Windsurf [Windsurf](https://windsurf.com/) is an AI-powered agentic coding tool, available as editor plugins or a standalone editor. It can use the Astro Docs MCP server to access documentation while performing coding tasks. Windsurf doesn't support streaming HTTP, so it requires a local proxy configuration: 1. Open `~/.codeium/windsurf/mcp_config.json` in your editor. 2. Add the following configuration to your Windsurf MCP settings: ``` { "mcpServers": { "Astro docs": { "command": "npx", "args": ["-y", "mcp-remote", "https://mcp.docs.astro.build/mcp"] } } } ``` 3. Save the configuration and restart Windsurf. [More info on using MCP servers with Windsurf](https://docs.windsurf.com/windsurf/cascade/mcp#mcp-config-json) #### Gemini CLI Gemini CLI is a command-line AI coding tool that can use the Astro Docs MCP server to access documentation while generating Astro code. You can configure MCP servers at the global level in the `~/.gemini/settings.json` file, or in a `.gemini/settings.json` file in a project root. ``` { "mcpServers": { "Astro docs": { "httpUrl": "https://mcp.docs.astro.build/mcp", } } } ``` [More info on using MCP servers with Gemini CLI](https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md) #### Google Antigravity [Google Antigravity](https://antigravity.google/) is an agentic development platform. 1. Open `~/.gemini/antigravity/mcp_config.json` by following the [Connecting Custom MCP Servers guide](https://antigravity.google/docs/mcp#connecting-custom-mcp-servers). 2. Add the following configuration to `mcp_config.json`: ``` { "mcpServers": { "astro-docs": { "serverUrl": "https://mcp.docs.astro.build/mcp" } } } ``` 3. Save the file and click "Refresh" in the "Manage MCPs" tab. #### Zed [Zed](https://zed.dev) supports MCP servers when using its AI capabilities. It can use the Astro Docs MCP server to access documentation while performing coding tasks. 1. Open `~/.config/zed/settings.json` in your editor. 2. Add the following configuration to your Zed MCP settings: ``` { "context_servers": { "Astro docs": { "settings": {}, "enabled": true, "url": "https://mcp.docs.astro.build/mcp" } } } ``` 3. Save the configuration. [More info on using MCP servers with Zed](https://zed.dev/docs/ai/mcp) #### ChatGPT Refer to the [OpenAI MCP documentation](https://platform.openai.com/docs/mcp#test-and-connect-your-mcp-server) for specific setup instructions. #### Raycast [Raycast](https://www.raycast.com/) can connect to MCP servers to enhance its AI capabilities. Adding the Astro Docs MCP server allows Raycast to access the latest Astro documentation while answering questions. Install by clicking the button below: Add to Raycast [More info on using MCP servers with Raycast](https://manual.raycast.com/model-context-protocol) #### Opencode AI [Opencode AI](https://opencode.ai/) is an open-source, terminal-based AI coding tool that can use the Astro Docs MCP server to access documentation while generating Astro code. You can configure MCP servers in your Opencode configuration file, typically named `opencode.json`, located in your project root or your global configuration directory (e.g. `~/.config/opencode/opencode.json`). ``` { "$schema": "https://opencode.ai/config.json", "mcp": { "Astro docs": { "type": "remote", "url": "https://mcp.docs.astro.build/mcp", "enabled": true } } } ``` [More info on using Opencode AI](https://opencode.ai/) #### GitHub Copilot Coding Agent [GitHub Copilot](https://docs.github.com/en/copilot/concepts/agents/coding-agent/about-coding-agent) can be used as a coding agent powered by GitHub Actions. Enabling the Astro Docs MCP server allows it to access the latest Astro documentation when answering questions or performing coding tasks. You can configure it to use the Astro Docs MCP server for documentation access by adding the following to your repository's Copilot coding agent settings available at `https://github.com///settings/copilot/coding_agent`: ``` { "mcpServers": { "astro-docs": { "type": "http", "url": "https://mcp.docs.astro.build/mcp", "tools": ["mcp__astro-docs__search_astro_docs"] } } } ``` Learn more about [extending GitHub Copilot coding agent with MCP servers](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/extend-coding-agent-with-mcp). ### Usage Once configured, you can ask your AI tool questions about Astro, and it will retrieve information directly from the latest docs. Coding agents will be able to consult the latest documentation when performing coding tasks, and chatbots will be able to accurately answer questions about Astro features, APIs, and best practices. :::note\[Remember\] The Astro Docs MCP server provides access to current documentation, but your AI tools are still responsible for interpretation and code generation. AI makes mistakes, so always review generated code carefully and test thoroughly. ::: ### Troubleshooting If you encounter issues: - Verify that your tool supports streamable HTTP transport. - Check that the server URL is correct: `https://mcp.docs.astro.build/mcp`. - Ensure your tool has proper internet access. - Consult your specific tool's MCP integration documentation. If you are still having problems, open an issue in the [Astro Docs MCP Server repository](https://github.com/withastro/docs-mcp/issues). ## Discord AI Support The same technology that powers Astro's MCP server is also available as a chatbot in the [Astro Discord](https://astro.build/chat) for self-serve support. Visit the `#support-ai` channel to ask questions about Astro or your project code in natural language. Your conversation is automatically threaded, and you can ask an unlimited number of follow-up questions. **Conversations with the chatbot are public, and are subject to the same server rules for language and behavior as the rest of our channels**, but they are not actively visited by our volunteer support members. For assistance from the community, please create a thread in our regular `#support` channel. ## Background mode When an AI coding agent is detected, `astro dev` automatically starts the dev server as a detached background process. This prevents the dev server from blocking the agent's terminal and allows it to continue working while the server runs. A lock file (`.astro/dev.json`) is written when the dev server starts, recording the server's URL, port, and PID. This prevents duplicate servers from being started for the same project. If you are not using an AI coding agent, `astro dev` starts in the foreground process and logs to the terminal. To opt out of automatic background mode, set the `ASTRO_DEV_BACKGROUND` environment variable before running `astro dev`: ``` ASTRO_DEV_BACKGROUND=0 astro dev ``` See the [CLI reference](/en/reference/cli-reference/#astro-dev) for the full list of `astro dev` flags and subcommands. ### Health endpoint The dev server exposes a `/_astro/status` endpoint that returns `{"ok": true}` as JSON. This allows agents and other tools to check programmatically whether the dev server is ready to accept requests. This endpoint is only available in the dev server and does not exist in production builds. ## Tips for AI-Powered Astro Development - **Start with templates**: Rather than building from scratch, ask AI tools to start with an existing [Astro template](https://astro.build/themes/) or use `npm create astro@latest` with a template option. - **Use `astro add` for integrations**: Ask AI tools to use `astro add` for official integrations (e.g. `astro add tailwind`, `astro add react`). For other packages, install using the command for your preferred package manager rather than editing `package.json` directly. - **Verify current APIs**: AI tools may use outdated patterns. Ask them to check the latest documentation, especially for newer features like sessions and actions. This is also important for features that have seen significant changes since their initial launch, such as content collections, or previously experimental features that may no longer be experimental. - **Use project rules**: If your AI tool supports it, set up project rules to enforce best practices and coding standards, such as the ones listed above. ## [Route caching](https://docharvest.github.io/docs/astro/guides/caching/) Contents astro Route caching Astro Route caching ```` You can then use [`Astro.cache`](/en/reference/api-reference/#cache) in your `.astro` pages (or `context.cache` for API routes and middleware) to control caching per request. Cache defaults for groups of routes can also be defined declaratively in your config using [`routeRules`](#route-rules). If you deploy to Netlify, Vercel, or Cloudflare, you can use their respective adapters' experimental [CDN cache provider](#adapter-cache-providers) instead of the in-memory provider. ## Adapter cache providers Astro's first-party adapters for Netlify, Vercel, and Cloudflare each provide an experimental CDN cache provider that maps cache directives to the platform's native cache headers and invalidation API. Rather than storing responses in memory, these push your caching directives to the host's edge network, and cache hits are served straight from the CDN without invoking your server function. During the experimental phase these providers need to be manually enabled, as shown below. In a future release, the Netlify and Vercel providers will be enabled automatically by their adapters. The Cloudflare provider requires a feature that is currently in private beta. Each provider automatically tags cached responses with the request path, so [`cache.invalidate({ path })`](/en/reference/api-reference/#cacheinvalidate) works on platforms that only support tag-based purges. ### Netlify

Import `cacheNetlify()` from `@astrojs/netlify/cache` and set it as your cache provider: ```js title="astro.config.mjs" ins={3, 7-9} ```` The provider sets `Netlify-CDN-Cache-Control` and `Netlify-Cache-Tag` headers. Cached responses use Netlify's [durable cache](https://docs.netlify.com/platform/caching/#durable-directive) so they are shared across all edge nodes, reducing function invocations. Both tag-based and path-based invalidation are supported. ### Vercel Import `cacheVercel()` from `@astrojs/vercel/cache` and set it as your cache provider: The provider sets `Vercel-CDN-Cache-Control` and `Vercel-Cache-Tag` headers. Both tag-based and path-based invalidation are supported. Tag invalidation is a soft invalidation: cached responses are marked as stale and revalidated in the background using stale-while-revalidate. ### Cloudflare :::caution\[Private beta\] The Cloudflare provider requires the Cloudflare Workers Cache feature, which is currently in private beta. Without beta access, the headers are ignored and calling `cache.invalidate()` will throw an error. ::: Import `cacheCloudflare()` from `@astrojs/cloudflare/cache` and set it as your cache provider: The provider sets `Cloudflare-CDN-Cache-Control` and `Cache-Tag` headers. The adapter enables Worker caching automatically when a Cloudflare cache provider is configured. Both tag-based and path-based invalidation are supported. ## Interacting with the cache The [`cache` object](/en/reference/api-reference/#cache) provides methods for setting cache options, invalidating entries, and checking the current cache state. This object is available in your `.astro` pages as `Astro.cache`, and in API routes and middleware as `context.cache`. See the [Cache API reference](/en/reference/api-reference/#cache) for more details. ### Checking if caching is enabled When caching is not configured, `cache.set()`, `cache.tags`, and `cache.options` log a warning, and `cache.invalidate()` throws an error. To avoid this, wrap your caching logic in a conditional check using [`cache.enabled`](/en/reference/api-reference/#cacheenabled). Its value is always `false` when no provider is configured or in development mode. ``` --- if (Astro.cache.enabled) { const tags = await getProductTags(Astro.params.id); Astro.cache.set({ maxAge: 3600, tags }); } --- ``` ### Setting cache options Call [`cache.set()`](/en/reference/api-reference/#cacheset) with an options object to enable caching for the current response. The following example caches a page for 2 minutes, serves stale content for 1 minute while revalidating, and tags the response for targeted invalidation: ``` --- --- Cached page ``` In API routes and middleware, use `context.cache`: ``` return Response.json({ ok: true }); } ``` ### Opting out of caching Call [`cache.set()`](/en/reference/api-reference/#cacheset) with `false` to explicitly opt a request out of caching. This is useful when a matched [route rule](#route-rules) would otherwise cache the response: ``` --- if (isPersonalized) { Astro.cache.set(false); } --- ``` ### Reading cache state You can access the current accumulated cache options via [`cache.options`](/en/reference/api-reference/#cacheoptions). This is useful for debugging or when you want to conditionally modify caching based on the current state: ``` const { maxAge, swr, tags } = context.cache.options; ``` ### Invalidating cache entries You can purge cached entries by tag or path using [`cache.invalidate()`](/en/reference/api-reference/#cacheinvalidate). This is useful for programmatically clearing cached content when it becomes stale, such as after a content update or user action. The following example creates an API route that invalidates by tag and by path: ``` // Invalidate a specific path await context.cache.invalidate({ path: '/api/data' }); return Response.json({ purged: true }); } ``` Tag-based invalidation removes all cached entries whose tags include any of the provided tags. Path-based invalidation is exact-match only (no [glob](/en/guides/imports/#glob-patterns) or wildcard patterns). ## Merge behavior Multiple calls to [`cache.set()`](/en/reference/api-reference/#cacheset) within a single request are merged according to the following rules: - **Scalar values** (`maxAge`, `swr`, `etag`): last-write-wins - **`lastModified`**: most recent date wins - **`tags`**: accumulate across all calls Middleware, layouts, content loaders, and page code can each contribute cache directives independently. ## Dev mode behavior In dev mode, the cache API is available so that route code does not need conditional checks, but no actual caching occurs. [`cache.enabled`](/en/reference/api-reference/#cacheenabled) is `false`, and [`cache.set()`](/en/reference/api-reference/#cacheset) and [`cache.invalidate()`](/en/reference/api-reference/#cacheinvalidate) are no-ops. To test your caching locally, build then preview your site. ## Route rules Route rules allow you to define caching behavior for groups of routes declaratively in your config. This is useful for applying caching to large groups of routes at once. The following example caches all API routes with stale-while-revalidate, product pages with a 1-hour freshness window, and blog posts for 5 minutes: The following route patterns are supported: - **Static paths**: `/about`, `/api/health` - **Dynamic parameters**: `/products/[id]`, `/blog/[slug]` - **Rest parameters**: `/docs/[...path]` Patterns use the same syntax, matching, and priority rules as Astro's [file-based routing](/en/guides/routing/#route-priority-order), so more specific patterns take precedence. Glob wildcards such as `*` are not supported; use a `[...rest]` parameter to match a group of routes (for example, `/api/[...path]` to match everything under `/api`). Per-route [`cache.set()`](/en/reference/api-reference/#cacheset) calls merge with config-level route rules. Route code can override or extend the defaults set in config. For example, a route rule might set a default `maxAge` for all product pages, but individual pages can call `cache.set()` to customize or disable caching as needed. ## [Scripts and event handling](https://docharvest.github.io/docs/astro/guides/client-side-scripts/) Contents astro Scripts and event handling Astro Scripts and event handling // Find our component DOM on the page. const buttons = document.querySelectorAll('\[data-confetti-button\]'); // Add event listeners to fire confetti when a button is clicked. buttons.forEach((button) => { button.addEventListener('click', () => confetti()); }); ```` See [when your scripts will not be processed](#unprocessed-scripts) to troubleshoot script behavior, or to learn how to opt-out of this processing intentionally. ## Script processing By default, Astro processes ` ```` ### Unprocessed scripts Astro will not process a ` ``` ### Include JavaScript files on your page You may want to write your scripts as separate `.js`/`.ts` files or need to reference an external script on another server. You can do this by referencing these in a ` ``` #### Load external scripts **When to use this:** when your JavaScript file lives inside of `public/` or on a CDN. To load scripts outside of your project's `src/` folder, include the `is:inline` directive. This approach skips the JavaScript processing, bundling, and optimizations that are provided by Astro when you import scripts as described above. ``` ``` ## Common script patterns ### Handle `onclick` and other events Some UI frameworks use custom syntax for event handling like `onClick={...}` (React/Preact) or `@click="..."` (Vue). Astro follows standard HTML more closely and does not use custom syntax for events. Instead, you can use [`addEventListener`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener) in a ` ``` If you have multiple `` components on a page, Astro will not run the script multiple times. Scripts are bundled and only included once per page. Using `querySelectorAll` ensures that this script attaches the event listener to every button with the `alert` class found on the page. ### Web components with custom elements You can create your own HTML elements with custom behavior using the Web Components standard. Defining a [custom element](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) in a `.astro` component allows you to build interactive components without needing a UI framework library. In this example, we define a new `` HTML element that tracks how many times you click the heart button and updates the `` with the latest count. ``` × 0 ``` There are two advantages to using a custom element here: 1. Instead of searching the whole page using `document.querySelector()`, you can use `this.querySelector()`, which only searches within the current custom element instance. This makes it easier to work with only the children of one component instance at a time. 2. Although a ` ``` Now we can use our component multiple times and be greeted by a different message for each one. ``` --- --- ``` :::tip\[Did you know?\] This is actually what Astro does behind the scenes when you pass props to a component written using a UI framework like React! For components with a `client:*` directive, Astro creates an `` custom element with a `props` attribute that stores your server-side props in the HTML output. ::: ### Combining scripts and UI Frameworks Elements rendered by a UI framework may not be available yet when a ` ``` ## Configuration 1\. Create a static admin folder at \`public/admin/\` 2. Add `config.yml` to `public/admin/`: \- public - admin - config.yml 3. To add support for content collections, configure each schema in `config.yml`. The following example configures a `blog` collection, defining a `label` for each entry's frontmatter property: ``` collections: - name: "blog" # Used in routes, e.g., /admin/collections/blog label: "Blog" # Used in the UI folder: "src/content/blog" # The path to the folder where the documents are stored create: true # Allow users to create new documents in this collection fields: # The fields for each document, usually in frontmatter - { label: "Layout", name: "layout", widget: "hidden", default: "blog" } - { label: "Title", name: "title", widget: "string" } - { label: "Publish Date", name: "date", widget: "datetime" } - { label: "Featured Image", name: "thumbnail", widget: "image" } - { label: "Rating (scale of 1-5)", name: "rating", widget: "number" } - { label: "Body", name: "body", widget: "markdown" } ``` 4. Add the `admin` route for your React app in `src/pages/admin.html`. \- public - admin - config.yml - src - pages - admin.html ``` Content Manager ``` 5. To enable media uploads to a specific folder via the Decap editor, add an appropriate path: ``` media_folder: "src/assets/images" # Location where files will be stored in the repo public_folder: "src/assets/images" # The src attribute for uploaded media ``` See [the Decap CMS configuration documentation](https://decapcms.org/docs/configure-decap-cms/) for full instructions and options. ## Usage Navigate to `yoursite.com/admin/` to use the Decap CMS editor. ## Authentication ### Decap CMS with Netlify Identity Decap CMS was originally developed by Netlify and has first class support for [Netlify Identity](https://docs.netlify.com/security/secure-access-to-sites/identity/). When deploying to Netlify, configure Identity for your project via the Netlify dashboard and include the [Netlify Identity Widget](https://github.com/netlify/netlify-identity-widget) on the `admin` route of your project. Optionally include the Identity Widget on the homepage of your site if you plan to invite new users via email. ### Decap CMS with External OAuth Clients When deploying to hosting providers other than Netlify, you must create your own OAuth routes. In Astro, this can be done with on-demand rendered routes in your project configured with [an adapter](/en/guides/on-demand-rendering/) enabled. See [Decap's OAuth Docs](https://decapcms.org/docs/external-oauth-clients/) for a list of compatible community-maintained OAuth clients. ## Community Resources - Netlify Identity Template: [astro-decap-ssg-netlify](https://github.com/OliverSpeir/astro-decap-ssg-netlify-identity) - On-demand rendering OAuth Routes with Astro Template: [astro-decap-starter-ssr](https://github.com/OliverSpeir/astro-decap-starter-ssr) - Blog Post: [Author your Astro site's content with Git-based CMSs](https://aalam.vercel.app/blog/astro-and-git-cms-netlify) by Aftab Alam - Youtube Tutorial: [Create a Custom Blog with Astro & NetlifyCMS in MINUTES!](https://www.youtube.com/watch?v=3yip2wSRX_4) by Kumail Pirzada ## [Directus & Astro](https://docharvest.github.io/docs/astro/guides/cms/directus/) Contents astro Directus & Astro Astro Directus & Astro [Directus](https://directus.io/) is a backend-as-a-service which can be used to host data and content for your Astro project. ## Official Resources - [Getting Started with Directus and Astro](https://docs.directus.io/blog/getting-started-directus-astro.html). ## Community Resources :::note\[Have a resource to share?\] If you found (or made!) a helpful video or blog post about using Directus with Astro, [add it to this list](https://github.com/withastro/docs/edit/main/src/content/docs/en/guides/cms/directus.mdx)! ::: ## [Drupal & Astro](https://docharvest.github.io/docs/astro/guides/cms/drupal/) Contents astro Drupal & Astro Astro Drupal & Astro [Drupal](https://www.drupal.org/) is an open-source content management tool. ## Prerequisites To get started, you will need to have the following: 1. **An Astro project** - If you don't have an Astro project yet, our [Installation guide](/en/install-and-setup/) will get you up and running in no time. 2. **A Drupal site** - If you haven't set up a Drupal site, you can follow the official guidelines [Installing Drupal](https://www.drupal.org/docs/getting-started/installing-drupal). ## Integrating Drupal with Astro ### Installing the JSON:API Drupal module To be able to get content from Drupal you need to enable the [Drupal JSON:API module](https://www.drupal.org/docs/core-modules-and-themes/core-modules/jsonapi-module). 1. Navigate to the Extend page `admin/modules` via the Manage administrative menu 2. Locate the JSON:API module and check the box next to it 3. Click Install to install the new module Now you can make `GET` requests to your Drupal application through JSON:API. ### Adding the Drupal URL in `.env` To add your Drupal URL to Astro, create a `.env` file in the root of your project (if one does not already exist) and add the following variable: ``` DRUPAL_BASE_URL="https://drupal.ddev.site/" ``` Restart the dev server to use this environment variable in your Astro project. ### Setting up Credentials By default, the Drupal JSON:API endpoint is accessible for external data-fetching requests without requiring authentication. This allows you to fetch data for your Astro project without credentials but it does not permit users to modify your data or site settings. However, if you wish to restrict access and require authentication, Drupal provides [several authentication methods](https://www.drupal.org/docs/contributed-modules/api-authentication) including: - [Basic Authentication](https://www.drupal.org/docs/contributed-modules/api-authentication/setup-basic-authentication) - [API Key-based authentication](https://www.drupal.org/docs/contributed-modules/api-authentication/api-key-authentication) - [Access Token/OAuth-based authentication](https://www.drupal.org/docs/contributed-modules/api-authentication/setup-access-token-oauth-based-authentication) - [JWT Token-based authentication](https://www.drupal.org/docs/contributed-modules/api-authentication/jwt-authentication) - [Third-Party Provider token authentication](https://www.drupal.org/docs/contributed-modules/api-authentication/rest-api-authentication-using-external-identity-provider) You can add your credentials to your `.env` file. ``` DRUPAL_BASIC_USERNAME="editor" DRUPAL_BASIC_PASSWORD="editor" DRUPAL_JWT_TOKEN="abc123" ... ``` Read more about [using environment variables](/en/guides/environment-variables/) and `.env` files in Astro. Your root directory should now include this new files: \- \*\*.env\*\* - astro.config.mjs - package.json ### Installing dependencies JSON:API requests and responses can often be complex and deeply nested. To simplify working with them, you can use two npm packages that streamline both the requests and the handling of responses: - [`JSONA`](https://www.npmjs.com/package/jsona): JSON API v1.0 specification serializer and deserializer for use on the server and in the browser. - [`Drupal JSON-API Params`](https://www.npmjs.com/package/drupal-jsonapi-params): This module provides a helper Class to create the required query. While doing so, it also tries to optimise the query by using the short form, whenever possible. \`\`\`shell npm install jsona drupal-jsonapi-params \`\`\` \`\`\`shell pnpm add jsona drupal-jsonapi-params \`\`\` \`\`\`shell yarn add jsona drupal-jsonapi-params \`\`\` ## Fetching data from Drupal Your content is fetched from a JSON:API URL. ### Drupal JSON:API URL structure The basic URL structure is: `/jsonapi/{entity_type_id}/{bundle_id}` The URL is always prefixed by `jsonapi`. - The `entity_type_id` refers to the Entity Type, such as node, block, user, etc. - The `bundle_id` refers to the Entity Bundles. In the case of a Node entity type, the bundle could be article. - In this case, to get the list of all articles, the URL will be `[DRUPAL_BASE_URL]/jsonapi/node/article`. To retrieve an individual entity, the URL structure will be `/jsonapi/{entity_type_id}/{bundle_id}/{uuid}`, where the uuid is the UUID of the entity. For example the URL to get a specific article will be of the form `/jsonapi/node/article/2ee9f0ef-1b25-4bbe-a00f-8649c68b1f7e`. #### Retrieving only certain fields Retrieve only certain field by adding the Query String field to the request. GET: `/jsonapi/{entity_type_id}/{bundle_id}?field[entity_type]=field_list` Examples: - `/jsonapi/node/article?fields[node--article]=title,created` - `/jsonapi/node/article/2ee9f0ef-1b25-4bbe-a00f-8649c68b1f7e?fields[node--article]=title,created,body` #### Filtering Add a filter to your request by adding the filter Query String. The simplest, most common filter is a key-value filter: GET: `/jsonapi/{entity_type_id}/{bundle_id}?filter[field_name]=value&filter[field_other]=value` Examples: - `/jsonapi/node/article?filter[title]=Testing JSON:API&filter[status]=1` - `/jsonapi/node/article/2ee9f0ef-1b25-4bbe-a00f-8649c68b1f7e?fields[node--article]=title&filter[title]=Testing JSON:API` You can find more query options in the [JSON:API Documentation](https://www.drupal.org/docs/core-modules-and-themes/core-modules/jsonapi-module). ### Building a Drupal query Astro components can fetch data from your Drupal site by using `drupal-jsonapi-params` package to build the query. The following example shows a component with a query for an "article" content type that has a text field for a title and a rich text field for content: ``` --- // Get the Drupal base URL // Generate the JSON:API Query. Get all title and body from published articles. const params: DrupalJsonApiParams = new DrupalJsonApiParams(); params.addFields("node--article", [ "title", "body", ]) .addFilter("status", "1"); // Generates the query string. const path: string = params.getQueryString(); const url: string = baseUrl + '/jsonapi/node/article?' + path; // Get the articles const request: Response = await fetch(url); const json: string | TJsonApiBody = await request.json(); // Initiate Jsona. const dataFormatter: Jsona = new Jsona(); // Deserialise the response. const articles = dataFormatter.deserialize(json); --- {articles?.length ? articles.map((article: any) => (

{article.title}

)):

No Content found

} ``` You can find more querying options in the [Drupal JSON:API Documentation](https://www.drupal.org/docs/core-modules-and-themes/core-modules/jsonapi-module/jsonapi) ## Making a blog with Astro and Drupal With the setup above, you are now able to create a blog that uses Drupal as the CMS. ### Prerequisites 1. **An Astro project** with [`JSONA`](https://www.npmjs.com/package/jsona) and [`Drupal JSON-API Params`](https://www.npmjs.com/package/drupal-jsonapi-params) installed. 2. **A Drupal site with at least one entry** - For this tutorial we recommend starting with a new Drupal site with Standard installation. In the **Content** section of your Drupal site, create a new entry by clicking the **Add** button. Then, choose Article and fill in the fields: - **Title:** `My first article for Astro!` - **Alias:** `/articles/first-article-for astro` - **Description:** `This is my first Astro article! Let's see what it will look like!` Click **Save** to create your first Article. Feel free to add as many articles as you want. ### Displaying a list of articles 1. Create `src/types.ts` if it does not already exist and add two new interfaces called `DrupalNode` and `Path` with the following code. These interfaces will match the fields of your article content type in Drupal and the Path fields. You will use it to type your article entries response. ``` export interface Path { alias: string pid: number langcode: string } export interface DrupalNode extends Record { id: string type: string langcode: string status: boolean drupal_internal__nid: number drupal_internal__vid: number changed: string created: string title: string default_langcode: boolean sticky: boolean path: Path } ``` Your src directory should now include the new file: \- .env - astro.config.mjs - package.json - src/ - \*\*types.ts\*\* 2. Create a new file called `drupal.ts` under `src/api` and add the following code: ``` import {Jsona} from "jsona"; import {DrupalJsonApiParams} from "drupal-jsonapi-params"; import type {DrupalNode} from "../types.ts"; import type {TJsonApiBody} from "jsona/lib/JsonaTypes"; // Get the Drupal Base Url. export const baseUrl: string = import.meta.env.DRUPAL_BASE_URL; ``` This will import the required libraries such as `Jsona` to deserialize the response, `DrupalJsonApiParams` to format the request url and the Node and Jsona types. It will also get the `baseUrl` from the `.env` file. Your src/api directory should now include the new file: \- .env - astro.config.mjs - package.json - src/ - api/ - \*\*drupal.ts\*\* - types.ts 3. In that same file, create the `fetchUrl` function to make the fetch request and deserialize the response. ``` import {Jsona} from "jsona"; import {DrupalJsonApiParams} from "drupal-jsonapi-params"; import type {DrupalNode} from "../types.ts"; import type {TJsonApiBody} from "jsona/lib/JsonaTypes"; // Get the Drupal Base Url. export const baseUrl: string = import.meta.env.DRUPAL_BASE_URL; /** * Fetch url from Drupal. * * @param url * * @return Promise as Promise */ export const fetchUrl = async (url: string): Promise => { const request: Response = await fetch(url); const json: string | TJsonApiBody = await request.json(); const dataFormatter: Jsona = new Jsona(); return dataFormatter.deserialize(json); } ``` 4. Create the `getArticles()` function to get all published articles. ``` import {Jsona} from "jsona"; import {DrupalJsonApiParams} from "drupal-jsonapi-params"; import type {DrupalNode} from "../types.ts"; import type {TJsonApiBody} from "jsona/lib/JsonaTypes"; // Get the Drupal Base Url. export const baseUrl: string = import.meta.env.DRUPAL_BASE_URL; /** * Fetch url from Drupal. * * @param url * * @return Promise as Promise */ export const fetchUrl = async (url: string): Promise => { const request: Response = await fetch(url); const json: string | TJsonApiBody = await request.json(); const dataFormatter: Jsona = new Jsona(); return dataFormatter.deserialize(json); } /** * Get all published articles. * * @return Promise */ export const getArticles = async (): Promise => { const params: DrupalJsonApiParams = new DrupalJsonApiParams(); params .addFields("node--article", [ "title", "path", "body", "created", ]) .addFilter("status", "1"); const path: string = params.getQueryString(); return await fetchUrl(baseUrl + '/jsonapi/node/article?' + path); } ``` Now you can use the function `getArticles()` in an `.astro` component to get all published articles with data for each title, body, path and creation date. 5. Go to the Astro page where you will fetch data from Drupal. The following example creates an articles landing page at `src/pages/articles/index.astro`. Import the necessary dependencies and fetch all the entries from Drupal with a content type of `article` using `getArticles()` while passing the `DrupalNode` interface to type your response. ``` --- import {Jsona} from "jsona"; import {DrupalJsonApiParams} from "drupal-jsonapi-params"; import type {TJsonApiBody} from "jsona/lib/JsonaTypes"; import type { DrupalNode } from "../../types"; import {getArticles} from "../../api/drupal"; // Get all published articles. const articles = await getArticles(); --- ``` This fetch call using getArticles() will return a typed array of entries that can be used in your page template. Your `src/pages/` directory should now include the new file, if you used the same page file: \- .env - astro.config.mjs - package.json - src/ - api/ - drupal.ts - pages/ - articles/ - \*\*index.astro\*\* - types.ts 6. Add content to your page, such as a title. Use `articles.map()` to show your Drupal entries as line items in a list. ``` --- import {Jsona} from "jsona"; import {DrupalJsonApiParams} from "drupal-jsonapi-params"; import type {TJsonApiBody} from "jsona/lib/JsonaTypes"; import type { DrupalNode } from "../types"; import {getArticles} from "../api/drupal"; // Get all published articles. const articles = await getArticles(); --- My news site

My news site

``` ### Generating individual blog posts Use the same method to fetch your data from Drupal as above, but this time, on a page that will create a unique page route for each article. This example uses Astro's default static mode, and creates [a dynamic routing page file](/en/guides/routing/#dynamic-routes) with the `getStaticPaths()` function. This function will be called at build time to generate the list of paths that become pages. 1. Create a new file `src/pages/articles/[path].astro` and import the `DrupalNode` interface and `getArticle()` from `src/api/drupal.ts`. Fetch your data inside a `getStaticPaths()` function to create routes for your blog. ``` --- import {Jsona} from "jsona"; import {DrupalJsonApiParams} from "drupal-jsonapi-params"; import type {TJsonApiBody} from "jsona/lib/JsonaTypes"; import type { DrupalNode } from "../../types"; import {getArticles} from "../../api/drupal"; // Get all published articles. export async function getStaticPaths() { const articles = await getArticles(); } --- ``` Your src/pages/articles directory should now include the new file: \- .env - astro.config.mjs - package.json - src/ - api/ - drupal.ts - pages/ - articles/ - index.astro - \*\*\[path\].astro\*\* - types.ts 2. In the same file, map each Drupal entry to an object with a `params` and `props` property. The `params` property will be used to generate the URL of the page and the `props` values will be passed to the page component as props. ``` --- import {Jsona} from "jsona"; import {DrupalJsonApiParams} from "drupal-jsonapi-params"; import type {TJsonApiBody} from "jsona/lib/JsonaTypes"; import type { DrupalNode } from "../../types"; import {getArticles} from "../../api/drupal"; // Get all published articles. export async function getStaticPaths() { const articles = await getArticles(); return articles.map((article: DrupalNode) => { return { params: { // Choose `path` to match the `[path]` routing value path: article.path.alias.split('/')[2] }, props: { title: article.title, body: article.body, date: new Date(article.created).toLocaleDateString('en-EN', { day: "numeric", month: "long", year: "numeric" }) } } }); } --- ``` The property inside `params` must match the name of the dynamic route. Since the filename is `[path].astro`, the property name passed to `params` must be `path`. In our example, the `props` object passes three properties to the page: - `title`: a string, representing the title of your post. - `body`: a string, representing the content of your entry. - `date`: a timestamp, based on your file creation date. 3. Use the page `props` to display your blog post. ``` --- import {Jsona} from "jsona"; import {DrupalJsonApiParams} from "drupal-jsonapi-params"; import type {TJsonApiBody} from "jsona/lib/JsonaTypes"; import type { DrupalNode } from "../../types"; import {getArticles} from "../../api/drupal"; // Get all published articles. export async function getStaticPaths() { const articles = await getArticles(); return articles.map((article: DrupalNode) => { return { params: { path: article.path.alias.split('/')[2] }, props: { title: article.title, body: article.body, date: new Date(article.created).toLocaleDateString('en-EN', { day: "numeric", month: "long", year: "numeric" }) } } }); } const {title, date, body} = Astro.props; --- {title}

{title}

``` 4. Navigate to your dev server preview and click on one of your posts to make sure your dynamic route is working. ### Publishing your site To deploy your website, visit our [deployment guides](/en/guides/deploy/) and follow the instructions for your preferred hosting provider. ## Community Resources :::note\[Have a resource to share?\] If you found (or made!) a helpful video or blog post about using Drupal with Astro, [add it to this list](https://github.com/withastro/docs/edit/main/src/content/docs/en/guides/cms/drupal.mdx)! ::: ## [Flotiq & Astro](https://docharvest.github.io/docs/astro/guides/cms/flotiq/) Contents astro Flotiq & Astro Astro Flotiq & Astro [Flotiq](https://flotiq.com?utm_campaign=flotiq_at_astro_headless_cms&utm_medium=referral&utm_source=astro) is a headless CMS designed for various frontends, such as static sites, mobile, and web applications. Developers and content creators manage and deliver content through REST and GraphQL-based APIs. ## Integrating with Astro This guide will use the Flotiq headless CMS API with an Astro project to display content on your website. ### Prerequisites To get started, you will need: 1. **An Astro project** - You can create a new project using the `npm create astro@latest` command. 2. **A Flotiq account** - If you don't have an account, [sign up for free](https://editor.flotiq.com/register?utm_campaign=flotiq_at_astro_headless_cms&utm_medium=referral&utm_source=astro). 3. **Flotiq read-only API key** - Find out [how to obtain your key](https://flotiq.com/docs/API/?utm_campaign=flotiq_at_astro_headless_cms&utm_medium=referral&utm_source=astro). ### Setting up the Environment variables Add the read-only API key from your Flotiq account to the `.env` file in the root of your Astro project: ``` FLOTIQ_API_KEY=__YOUR_FLOTIQ_API_KEY__ ``` ### Defining a Content Type in Flotiq First, you need to define an example [Content Type Definition](https://flotiq.com/docs/panel/content-types/?utm_campaign=flotiq_at_astro_headless_cms&utm_medium=referral&utm_source=astro) in Flotiq to store data. Log in to your Flotiq account and create a custom Content Type Definition with the following example `Blog Post` configuration: - **Label**: Blog Post - **API Name**: blogpost - **Fields**: - **Title**: text, required - **Slug**: text, required - **Content**: rich text, required Then, create one or more example [Content Objects](https://flotiq.com/docs/panel/content-objects/?utm_campaign=flotiq_at_astro_headless_cms&utm_medium=referral&utm_source=astro) using this `Blog Post` type. ### Installing the Flotiq TypeScript SDK To connect your project with Flotiq, install the [Flotiq SDK](https://github.com/flotiq/flotiq-api-ts) using the package manager of your choice: \`\`\`sh npm install flotiq-api-ts \`\`\` \`\`\`sh pnpm add flotiq-api-ts \`\`\` \`\`\`sh yarn add flotiq-api-ts \`\`\` Next, configure the SDK using your credentials. Create a new file named `flotiq.ts` inside the `src/lib` directory of your project: This configuration can now be used throughout your project. ### Fetching and Displaying Data from Flotiq 1. Fetch the `Blog Post` data on an Astro page using your content's custom API `BlogpostAPI`: ``` --- import { flotiq } from "../lib/flotiq"; const posts = await flotiq.BlogpostAPI.list(); --- ``` 2. Display the content in your Astro template. You will have access to the `title`, `slug`, and `content` of your posts as well as other `internal` post data: ``` --- import { flotiq } from "../lib/flotiq"; const posts = await flotiq.BlogpostAPI.list(); --- Astro {posts.data?.map((post) => (

{post.title}

{post.internal?.createdAt}
))} ``` 3. Start the dev server and visit your page preview at `http://localhost:4321` to see the list of your blog posts. Each post will link to a page that does not yet exist. These will be created in the next step. ### Generating Individual Pages Astro supports both prerendering all your pages ahead of time, or creating routes on demand when they are requested. Follow the instructions for either [static site generation](#static-site-generation) or [on-demand rendering](#on-demand-rendering) to build the page routes for your blog posts. #### Static Site Generation In static site generation (SSG) mode, use the `getStaticPaths()` method to fetch all possible blog post paths from Flotiq. 1. Create a new file `[slug].astro` in the `/src/pages/posts/` directory. Fetch all blog posts and return them within the `getStaticPaths()` method: ``` --- import type { Blogpost } from "flotiq-api-ts"; import { flotiq } from "../../lib/flotiq"; export async function getStaticPaths() { const posts = await flotiq.BlogpostAPI.list(); return posts.data?.map((post) => ({ params: { slug: post.slug }, props: post })) || [] } --- ``` 2. Add the templating to display an individual post: ``` --- import type { Blogpost } from "flotiq-api-ts"; import { flotiq } from "../../lib/flotiq"; export async function getStaticPaths() { const posts = await flotiq.BlogpostAPI.list(); return posts.data?.map((post) => ({ params: { slug: post.slug }, props: post })) || [] } const post: Blogpost = Astro.props; --- {post.title}

{post.title}

``` 3. Visit `http://localhost:4321` and click on a linked blog post in your list. You will now be able to navigate to the individual post's page. #### On-demand Rendering If you are using [SSR](/en/guides/on-demand-rendering/) mode, you will need to fetch a single post based on its `slug`. 1. Create a new file `[slug].astro` in the `/src/pages/posts/` directory. Fetch the post by its `slug` field, including logic to display a 404 page when the route is not found: ``` --- import type { Blogpost } from "flotiq-api-ts"; import { flotiq } from "../../lib/flotiq"; const { slug } = Astro.params; let post: Blogpost; const blogpostList = await flotiq.BlogpostAPI.list({ filters: JSON.stringify({ slug: { type: 'equals', filter: slug, } }), limit: 1 }); if (blogpostList.data?.[0]) { post = blogpostList.data[0] } else { return Astro.redirect('/404'); } --- ``` 2. Add the templating to display an individual post: ``` --- import type { Blogpost } from "flotiq-api-ts"; import { flotiq } from "../../lib/flotiq"; const { slug } = Astro.params; let post: Blogpost; const blogpostList = await flotiq.BlogpostAPI.list({ filters: JSON.stringify({ slug: { type: 'equals', filter: slug, } }), limit: 1 }); if (blogpostList.data?.[0]) { post = blogpostList.data[0] } else { return Astro.redirect('/404'); } --- {post.title}

{post.title}

``` 3. Visit `http://localhost:4321` and click on a linked blog post in your list. You will now be able to navigate to the individual post's page. ### Refreshing the SDK After Content Type Changes When using the Flotiq TypeScript SDK (`flotiq-api-ts`), all your data types are accurately mapped into the Astro project. If you make changes to the structure of your content types (such as adding a new field or modifying an existing one), you’ll need to refresh the SDK to ensure that your project reflects the latest model updates. To do this, run the rebuild command for your package manager: \`\`\`sh npm rebuild flotiq-api-ts \`\`\` \`\`\`sh pnpm rebuild flotiq-api-ts \`\`\` \`\`\`sh yarn rebuild flotiq-api-ts // for yarn v1 (Classic): // yarn add flotiq-api-ts \`\`\` This will update the SDK, aligning object types, fields, and API methods with your current data model. ## Publishing Your Site To deploy your website, visit Astro's [deployment guides](/en/guides/deploy/) and follow the instructions for your preferred hosting provider. ### Redeploy on Flotiq Changes To update your published site, configure Flotiq to send a webhook your hosting provider to trigger a rebuild whenever your content changes. In Flotiq, you can define which Content Type and events it should trigger on, and configure it accordingly. See the [Flotiq Webhooks documentation](https://flotiq.com/docs/panel/webhooks/async-co-webhook/?utm_campaign=flotiq_at_astro_headless_cms&utm_medium=referral&utm_source=astro) for more details. ## Official Resources - [Flotiq documentation](https://flotiq.com/docs/?utm_campaign=flotiq_at_astro_headless_cms&utm_medium=referral&utm_source=astro) ## Community resources - [Flotiq x Astro](https://maciekpalmowski.dev/blog/flotiq-cms-astro/) by Maciek Palmowski ## [Front Matter CMS & Astro](https://docharvest.github.io/docs/astro/guides/cms/frontmatter-cms/) Contents astro Front Matter CMS & Astro Astro Front Matter CMS & Astro [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 In this section, we'll walk through how to add Front Matter CMS to your Astro project. ### Prerequisites - Visual Studio Code - Use the [Astro Blog template](https://github.com/withastro/astro/tree/main/examples/blog) to provide the base configuration and sample content to start with Front Matter CMS. ### Install the Front Matter CMS extension You can get the extension from the [Visual Studio Code Marketplace - Front Matter](https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter) or by clicking on the following link: [open Front Matter CMS extension in VS Code](vscode:extension/eliostruyf.vscode-front-matter) ### Project initialization Once Front Matter CMS is installed, you will get a new icon in the Activity Bar. It will open the **Front Matter CMS** panel in the primary sidebar when you click on it. Follow the next steps to initialize your project: - Click on the **Initialize project** button in the Front Matter panel - The welcome screen will open, and you can start initializing the project - Click on the first step to **Initialize project** - As Astro is one of the supported frameworks, you can select it from the list - Register your content folders, in this case, the `src/content/blog` folder. :::note Folder registration is required to let Front Matter CMS know where it can find and create your content. You can have multiple types of folders like pages, blog, docs, and many more. ::: - You will be asked to enter the name of the folder. By default, it takes the folder name. :::note The name gets used during the creation process of new content. For example, having multiple folder registrations allows you to choose the type of content you want to create. ::: - Click on **Show the dashboard** to open the content dashboard :::tip Once Front Matter CMS is initialized, you can open the dashboard as follows: - Using the keyboard binding: alt + d (Windows & Linux) or options + d (macOS) - Open the command palette and search for `Front Matter: Open dashboard` - Click on the **Front Matter** icon on the panel's title bar or files. ::: ### Project configuration Once the project is initialized, you will get a `frontmatter.json` configuration file and a `.frontmatter` folder in the root of your project. \- .frontmatter/ - database/ - mediaDb.json - src/ - astro.config.mjs - \*\*frontmatter.json\*\* - package.json #### Content-type configuration Content-types are the way Front Matter CMS manages your content. Each content-type contains a set of fields, which can be defined per type of content you want to use for your website. The fields correspond to the front matter of your page content. You can configure the content-types in the `frontmatter.json` file. - Open the `frontmatter.json` file - Replace the `frontMatter.taxonomy.contentTypes` array with the following content-types configuration: ``` "frontMatter.taxonomy.contentTypes": [ { "name": "default", "pageBundle": false, "previewPath": "'blog'", "filePrefix": null, "fields": [ { "title": "Title", "name": "title", "type": "string", "single": true }, { "title": "Description", "name": "description", "type": "string" }, { "title": "Publishing date", "name": "pubDate", "type": "datetime", "default": "{{now}}", "isPublishDate": true }, { "title": "Content preview", "name": "heroImage", "type": "image", "isPreviewImage": true } ] } ] ``` :::note This configuration ensures that the Front Matter content-type matches the content collection schema from the Astro blog template. ::: :::tip You can find more information on content-types and the supported fields in the [content creation docs section](https://frontmatter.codes/docs/content-creation) from Front Matter CMS. ::: ### Preview your articles in the editor From the **Front Matter CMS** panel, click on the **Start server** button. This action starts the Astro local dev server. Once running, you can open the content dashboard, select one of the articles and click on the **Open preview** button to open the article in the editor. ### Create new articles Open the **Front Matter CMS Dashboard**; you can do this as follows: - Open the Front Matter CMS' content dashboard - Click on the **Create content** button - Front Matter will ask you for the title of the article. Fill it in and press enter - Your new article will be created and opened in the editor. You can start writing your article. ### Using Markdoc with Front Matter CMS To use Markdoc with Front Matter CMS, you must configure this in the `frontMatter.content.supportedFileTypes`. This setting lets the CMS know which types of files it can progress. You can configure the setting as follows: ``` "frontMatter.content.supportedFileTypes": [ "md", "markdown", "mdx", "mdoc" ] ``` To allow your content to be created as Markdoc, specify the `fileType` property on the content-type. ``` "frontMatter.taxonomy.contentTypes": [ { "name": "default", "pageBundle": false, "previewPath": "'blog'", "filePrefix": null, "fileType": "mdoc", "fields": [ { "title": "Title", "name": "title", "type": "string", "single": true }, { "title": "Description", "name": "description", "type": "string" }, { "title": "Publishing date", "name": "pubDate", "type": "datetime", "default": "{{now}}", "isPublishDate": true }, { "title": "Content preview", "name": "heroImage", "type": "image", "isPreviewImage": true } ] } ] ``` ## Official Resources - [Front Matter CMS](https://frontmatter.codes/) - [Front Matter CMS - Documentation](https://frontmatter.codes/docs/) - [Getting started with Astro and Front Matter CMS](https://youtu.be/xb6pZiier_E) ## [Ghost & Astro](https://docharvest.github.io/docs/astro/guides/cms/ghost/) Contents astro Ghost & Astro Astro Ghost & Astro } ``` :::tip Read more about [using environment variables](/en/guides/environment-variables/) and `.env` files in Astro. ::: Your root directory should now include these new files: - src/ - **env.d.ts** - **.env** - astro.config.mjs - package.json ### Installing dependencies To connect with Ghost, install the official content API wrapper [`@tryghost/content-api`](https://www.npmjs.com/package/@tryghost/content-api) using the command below for your preferred package manager, and optionally, a helpful package containing type definitions if you are using TypeScript: ```shell npm install @tryghost/content-api npm install --save @types/tryghost__content-api ``` \`\`\`shell pnpm add @tryghost/content-api pnpm add --save-dev @types/tryghost\_\_content-api \`\`\` \`\`\`shell yarn add @tryghost/content-api yarn add --dev @types/tryghost\_\_content-api \`\`\` ## Making a blog with Astro and Ghost With the setup above, you are now able to create a blog that uses Ghost as the CMS. ### Prerequisites 1. A Ghost blog 2. An Astro project integrated with the [Ghost content API](https://www.npmjs.com/package/@tryghost/content-api) - See [integrating with Astro](/en/guides/cms/ghost/#integrating-with-astro) for more details on how to set up an Astro project with Ghost. This example will create an index page that lists posts with links to dynamically-generated individual post pages. ### Fetching Data You can fetch your site's data with the Ghost content API package. First, create a `ghost.ts` file under a `lib` directory. \- src/ - lib/ - \*\*ghost.ts\*\* - pages/ - index.astro - astro.config.mjs - package.json Initialize an API instance with the Ghost API using the API key from the Ghost dashboard's Integrations page. ``` // Create API instance with site credentials ``` ### Displaying a list of posts The page `src/pages/index.astro` will display a list of posts, each with a description and link to its own page. \- src/ - lib/ - ghost.ts - pages/ - \*\*index.astro\*\* - astro.config.mjs - package.json Import `ghostClient()` in the Astro frontmatter to use the `posts.browse()` method to access blog posts from Ghost. Set `limit: all` to retrieve all posts. ``` --- const posts = await ghostClient.posts .browse({ limit: 'all', }) .catch((err) => { console.error(err); }); --- ``` Fetching via the content API returns an array of objects containing the [properties for each post](https://ghost.org/docs/content-api/#posts) such as: - `title` - the title of the post - `html` - the HTML rendering of the content of the post - `feature_image` - the source URL of the featured image of the post - `slug` - the slug of the post Use the `posts` array returned from the fetch to display a list of blog posts on the page. ``` --- const posts = await ghostClient.posts .browse({ limit: 'all', }) .catch((err) => { console.error(err); }); --- Astro + Ghost 👻 { posts.map((post) => (

{post.title}

)) } ``` ### Generating pages The page `src/pages/post/[slug].astro` [dynamically generates a page](/en/guides/routing/#dynamic-routes) for each post. \- src/ - lib/ - ghost.ts - pages/ - index.astro - post/ - \*\*\[slug\].astro\*\* - astro.config.mjs - package.json Import `ghostClient()` to access blog posts using `posts.browse()` and return a post as props to each of your dynamic routes. ``` --- }); return posts.map((post) => { return { params: { slug: post.slug, }, props: { post: post, }, }; }); } const { post } = Astro.props; --- ``` Create the template for each page using the properties of each `post` object. ``` --- }); return posts.map((post) => { return { params: { slug: post.slug, }, props: { post: post, }, }; }); } const { post } = Astro.props; --- {post.title} {post.title}

{post.title}

{post.reading_time} min read

``` :::note `` is a built-in Astro component which allows you to avoid an unnecessary wrapper element. This can be especially useful when fetching HTML from a CMS (e.g. Ghost or [WordPress](/en/guides/cms/wordpress/)). ::: ### Publishing your site To deploy your site visit our [deployment guide](/en/guides/deploy/) and follow the instructions for your preferred hosting provider. ## Community Resources :::note\[Have a resource to share?\] If you found (or made!) a helpful video or blog post about using Ghost with Astro, [add it to this list](https://github.com/withastro/docs/edit/main/src/content/docs/en/guides/cms/ghost.mdx)! ::: ## [GitCMS & Astro](https://docharvest.github.io/docs/astro/guides/cms/gitcms/) Contents astro GitCMS & Astro Astro GitCMS & Astro [GitCMS](https://gitcms.blog) turns GitHub into a Git-based headless CMS, offering a Notion-like markdown editing experience right in your browser. ## Official Resources - [Introducing GitCMS](https://gitcms.blog/posts/introducing-gitcms/) - [How to Configure GitCMS for an Astro Site](https://gitcms.blog/posts/how-to-configure-gitcms/) - [Install GitCMS Chrome Extension](https://gitcms.blog/extension) ## [Hashnode & Astro](https://docharvest.github.io/docs/astro/guides/cms/hashnode/) Contents astro Hashnode & Astro Astro Hashnode & Astro [Hashnode](https://hashnode.com/) is a hosted CMS that allows you to create a blog or publication. ## Integrating with Astro The [Hashnode Public API](https://apidocs.hashnode.com/) is a GraphQL API that allows you to interact with Hashnode. This guide uses [`graphql-request`](https://github.com/jasonkuhrt/graphql-request), a minimal GraphQL client that works well with Astro, to bring your Hashnode data into your Astro project. ### Prerequisites To get started you will need to have the following: 1. **An Astro project** - If you don't have an Astro project yet, our [Installation guide](/en/install-and-setup/) will get you up and running in no time. 2. **A Hashnode site** - You can create free personal site by visiting [Hashnode](https://hashnode.com/). ### Installing dependencies Install the `graphql-request` package using the package manager of your choice: \`\`\`shell npm install graphql-request \`\`\` \`\`\`shell pnpm add graphql-request \`\`\` \`\`\`shell yarn add graphql-request \`\`\` ## Making a blog with Astro and Hashnode This guide uses [`graphql-request`](https://github.com/jasonkuhrt/graphql-request), a minimal GraphQL client that works well with Astro, to bring your Hashnode data into your Astro project. ### Prerequisites 1. A Hashnode Blog 2. An Astro project integrated with the [graphql-request](https://github.com/jasonkuhrt/graphql-request) package installed. This example will create an index page that lists posts with links to dynamically-generated individual post pages. ### Fetching Data 1\. To fetch your site's data with the \`graphql-request\` package, make a \`src/lib\` directory and create two new files \`client.ts\` & \`schema.ts\`: ``` - src/ - lib/ - **client.ts** - **schema.ts** - pages/ - index.astro - astro.config.mjs - package.json ``` 2. Initialize an API instance with the GraphQLClient using the URL from your Hashnode Website. ``` import { gql, GraphQLClient } from "graphql-request"; import type { AllPostsData, PostData } from "./schema"; export const getClient = () => { return new GraphQLClient("https://gql.hashnode.com") } const myHashnodeURL = "astroplayground.hashnode.dev"; export const getAllPosts = async () => { const client = getClient(); const allPosts = await client.request( gql` query allPosts { publication(host: "${myHashnodeURL}") { id title posts(first: 20) { pageInfo{ hasNextPage endCursor } edges { node { id author{ name profilePicture } title subtitle brief slug coverImage { url } tags { name slug } publishedAt readTimeInMinutes } } } } } ` ); return allPosts; }; export const getPost = async (slug: string) => { const client = getClient(); const data = await client.request( gql` query postDetails($slug: String!) { publication(host: "${myHashnodeURL}") { id post(slug: $slug) { id author{ name profilePicture } publishedAt title subtitle readTimeInMinutes content{ html } tags { name slug } coverImage { url } } } } `, { slug: slug } ); return data.publication.post; }; ``` 3. Configure `schema.ts` to define the shape of the data returned from the Hashnode API. ``` import { z } from "astro/zod"; export const PostSchema = z.object({ id: z.string(), author: z.object({ name: z.string(), profilePicture: z.string(), }), publishedAt: z.string(), title: z.string(), subtitle: z.string(), brief: z.string(), slug: z.string(), readTimeInMinutes: z.number(), content: z.object({ html: z.string(), }), tags: z.array(z.object({ name: z.string(), slug: z.string(), })), coverImage: z.object({ url: z.string(), }), }) export const AllPostsDataSchema = z.object({ id: z.string(), publication: z.object({ title: z.string(), posts: z.object({ pageInfo: z.object({ hasNextPage: z.boolean(), endCursor: z.string(), }), edges: z.array(z.object({ node: PostSchema, })), }), }), }) export const PostDataSchema = z.object({ id: z.string(), publication: z.object({ title: z.string(), post: PostSchema, }), }) export type Post = z.infer export type AllPostsData = z.infer export type PostData = z.infer ``` ### Displaying a list of posts Fetching via `getAllPosts()` returns an array of objects containing the properties for each post such as: - `title` - the title of the post - `brief` - the HTML rendering of the content of the post - `coverImage.url` - the source URL of the featured image of the post - `slug` - the slug of the post Use the `posts` array returned from the fetch to display a list of blog posts on the page. ``` --- const data = await getAllPosts(); const allPosts = data.publication.posts.edges; --- Astro + Hashnode { allPosts.map((post) => (

{post.node.title}

{post.node.brief}

{post.node.title} Read more
)) } ``` ### Generating pages 1\. Create the page \`src/pages/post/\[slug\].astro\` to \[dynamically generate a page\](/en/guides/routing/#dynamic-routes) for each post. ``` - src/ - lib/ - client.ts - schema.ts - pages/ - index.astro - post/ - **[slug].astro** - astro.config.mjs - package.json ``` 2. Import and use `getAllPosts()` and `getPost()` to fetch the data from Hashnode and generate individual page routes for each post. ``` --- import { getAllPosts, getPost } from '../../lib/client'; export async function getStaticPaths() { const data = await getAllPosts(); const allPosts = data.publication.posts.edges; return allPosts.map((post) => { return { params: { slug: post.node.slug }, } }) } const { slug } = Astro.params; const post = await getPost(slug); --- ``` 3. Create the template for each page using the properties of each `post` object. The example below shows the post title and reading time, then the full post content: ``` --- import { getAllPosts, getPost } from '../../lib/client'; export async function getStaticPaths() { const data = await getAllPosts(); const allPosts = data.publication.posts.edges; return allPosts.map((post) => { return { params: { slug: post.node.slug }, } }) } const { slug } = Astro.params; const post = await getPost(slug); --- {post.title} {post.title}

{post.title}

{post.readTimeInMinutes} min read

``` :::note `` is a built-in Astro component which allows you to avoid an unnecessary wrapper element. This can be especially useful when fetching HTML from a CMS (e.g. Hashnode or [WordPress](/en/guides/cms/wordpress/)). ::: ### Publishing your site To deploy your site visit our [deployment guide](/en/guides/deploy/) and follow the instructions for your preferred hosting provider. ## Community Resources - [`astro-hashnode`](https://github.com/matthiesenxyz/astro-hashnode) on GitHub ## [Hygraph & Astro](https://docharvest.github.io/docs/astro/guides/cms/hygraph/) ## [JekyllPad & Astro](https://docharvest.github.io/docs/astro/guides/cms/jekyllpad/) Contents astro JekyllPad & Astro Astro JekyllPad & Astro [JekyllPad](https://www.jekyllpad.com) is a lightweight, browser-based CMS that connects directly to your GitHub repository. It provides a modern WYSIWYG + Markdown editor, commits changes straight to your repo, and runs 100% client‑side, so you can manage content for Astro without setting up servers or learning Git. It is a client-side application that runs completely in user's browser. This means data, oauth and security tokens, all stays in user's browser. No installation required, works with any mobile or desktop browsers. ## Official Resources - [JekyllPad CMS for Astro](https://www.jekyllpad.com/features/astro-headless-cms) ## [Keystatic & Astro](https://docharvest.github.io/docs/astro/guides/cms/keystatic/) Contents astro Keystatic & Astro Astro Keystatic & Astro [Keystatic](https://keystatic.com/) is an open source, headless content-management system that allows you to structure your content and sync it with GitHub. :::tip If you're starting a **new Astro + Keystatic project from scratch**, you can use the [Keystatic CLI](https://keystatic.com/docs/quick-start#keystatic-cli) to generate a new project in seconds: \`\`\`shell npm create @keystatic@latest \`\`\` \`\`\`shell pnpm create @keystatic@latest \`\`\` \`\`\`shell yarn create @keystatic \`\`\` Select the Astro template, and you'll be ready to [deploy](#deploying-keystatic--astro)! ::: ## Prerequisites - An existing Astro project [with an adapter configured](/en/guides/on-demand-rendering/). :::note If you intend to sync Keystatic's data with GitHub, you will also need **a GitHub account with `write` permissions** on the repository for this project. ::: ## Installing dependencies Add both the Markdoc (for content entries) and the React (for the Keystatic Admin UI Dashboard) integrations to your Astro project, using the `astro add` command for your package manager. \`\`\`shell npx astro add react markdoc \`\`\` \`\`\`shell pnpm astro add react markdoc \`\`\` \`\`\`shell yarn astro add react markdoc \`\`\` You will also need two Keystatic packages: \`\`\`shell npm install @keystatic/core @keystatic/astro \`\`\` \`\`\`shell pnpm add @keystatic/core @keystatic/astro \`\`\` \`\`\`shell yarn add @keystatic/core @keystatic/astro \`\`\` ## Adding the Astro integration Add the Astro integration from `@keystatic/astro` in your Astro config file: ``` // astro.config.mjs ``` :::note\[Already using content collections?\] If you are already using [content collections](/en/guides/content-collections/) in your Astro project, then update the schema above to exactly match the collection(s) defined in your existing schema. ::: Keystatic is now configured to manage your content based on your schema. ## Running Keystatic locally To launch your Keystatic Admin UI dashboard, start Astro's dev server: ```` ```bash npm run dev ``` ```` Visit `http://127.0.0.1:4321/keystatic` in the browser to see the Keystatic Admin UI running. ## Creating a new post 1\. In the Keystatic Admin UI dashboard, click on the “Posts” collection. 2. Use the button to create a new post. Add the title "My First Post" and some content, then save the post. 3. This post should now be visible from your "Posts" collection. You can view and edit your individual posts from this dashboard page. 4. Return to view your Astro project files. You will now find a new `.mdoc` file inside the `src/content/posts` directory for this new post: \- src/ - content/ - posts/ - \*\*my-first-post.mdoc\*\* 5. Navigate to that file in your code editor and verify that you can see the Markdown content you entered. For example: ``` --- title: My First Post --- This is my very first post. I am **super** excited! ``` ## Rendering Keystatic content [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 The following example displays a list of each post title, with a link to an individual post page. ``` --- import { getCollection } from 'astro:content' const posts = await getCollection('posts') --- ``` ### Displaying a single entry To display content from an individual post, you can import and use the `` component to [render your content to HTML](/en/guides/content-collections/#rendering-body-content): ``` --- import { getEntry } from 'astro:content' const post = await getEntry('posts', 'my-first-post') const { Content } = await post.render() ---

{post.data.title}

``` For more information on querying, filtering, displaying your collections content and more, see the full content [collections documentation](/en/guides/content-collections/). ## Deploying Keystatic + Astro To deploy your website, visit our [deployment guides](/en/guides/deploy/) and follow the instructions for your preferred hosting provider. You'll also probably want to [connect Keystatic to GitHub](https://keystatic.com/docs/connect-to-github) so you can manage content on the deployed instance of the project. ## Official Resources - Check out [the official Keystatic guide](https://keystatic.com/docs/installation-astro) - [Keystatic starter template](https://github.com/Thinkmill/keystatic/tree/main/templates/astro) ## [KeystoneJS & Astro](https://docharvest.github.io/docs/astro/guides/cms/keystonejs/) Contents astro KeystoneJS & Astro Astro KeystoneJS & Astro [KeystoneJS](https://keystonejs.com/) is an open source, headless content-management system that allows you to describe the structure of your schema. ## [Kontent.ai & Astro](https://docharvest.github.io/docs/astro/guides/cms/kontent-ai/) ## [microCMS & Astro](https://docharvest.github.io/docs/astro/guides/cms/microcms/) Contents astro microCMS & Astro Astro microCMS & Astro [microCMS](https://microcms.io/en) is an API-based headless CMS that lets you define content using schemas, and manage it using the dashboard. ## Official Resources - Check out [the official microCMS document](https://document.microcms.io/tutorial/astro/astro-top) - Blog: [Build a blog with microCMS](https://blog.microcms.io/astro-microcms-introduction/) ## [Optimizely CMS & Astro](https://docharvest.github.io/docs/astro/guides/cms/optimizely/) Contents astro Optimizely CMS & Astro Astro Optimizely CMS & Astro [Optimizely CMS](https://www.optimizely.com/products/content-management/) is available as a headless CMS powered by GraphQL that provides a visual editor. ## Official Resources - The official [Optimizely SaaS CMS documentation](https://docs.developers.optimizely.com/content-management-system/v1.0.0-CMS-SaaS/docs/overview-saas) ## Community Resources - [Build a headless blog with Astro and Optimizely SaaS CMS](https://world.optimizely.com/blogs/jacob-pretorius/dates/2024/5/build-a-headless-blog-with-astro-and-optimizely-saas-cms/) - [Sample Astro + Optimizely Graph starter project templates](https://github.com/jacobpretorius/Opti.SaaS.Astro.Demo/) ## [Pages CMS & Astro](https://docharvest.github.io/docs/astro/guides/cms/pages-cms/) Contents astro Pages CMS & Astro Astro Pages CMS & Astro [Pages CMS](https://pagescms.org/) is an open source, Git-based content management system for GitHub repositories that can be used to manage content in Astro projects. ## Official Resources - [Pages CMS documentation](https://pagescms.org/docs/) - [Pages CMS Astro blog template](https://github.com/pages-cms/astro-blog-template) ## [Payload CMS & Astro](https://docharvest.github.io/docs/astro/guides/cms/payload/) Contents astro Payload CMS & Astro Astro Payload CMS & Astro [PayloadCMS](https://payloadcms.com/) is a headless open-source content management system that can be used to provide content for your Astro project. ## Integrating with Astro ### Prerequisites 1. **An Astro project** - If you don't have an Astro project yet, our [Installation guide](/en/install-and-setup/) will get you up and running in no time. 2. **A MongoDB database** - PayloadCMS will ask you for a MongoDB connection string when creating a new project. You can set one up locally or use [MongoDBAtlas](https://www.mongodb.com/) to host a database on the web for free. 3. **A PayloadCMS REST API** - Create a [PayloadCMS](https://payloadcms.com/docs/getting-started/installation) project and connect it to your MongoDB database during the setup. :::note\[Choosing a template\] During the PayloadCMS installation, you will be asked if you want to use a template. Choosing any of the available templates at this step (such as 'blog') automatically generates additional collections for you to use. Otherwise, you will need to manually create your PayloadCMS collections. ::: ### Configuring Astro for your PayloadCMS collection Your Payload project template will contain a file called Posts.ts in `src/collections/`. If you did not choose a template during installation that created a content collection for you, you can create a new Payload CMS Collection by adding this configuration file manually. The example below shows this file for a collection called `posts` that requires `title`, `content`, and `slug` fields: ``` const Posts: CollectionConfig = { slug: "posts", admin: { useAsTitle: "title", }, access: { read: () => true, }, fields: [ { name: "title", type: "text", required: true, }, { name: "content", type: "text", required: true, }, { name: "slug", type: "text", required: true, }, ], }; ``` 1\. Import and add both \`Users\` (available in all PayloadCMS projects) and any other collections (e.g. \`Posts\`) to the available collections in the \`payload.config.ts\` file. \`\`\`ts title="src/payload.config.ts" ins={4, 5, 12} import { buildConfig } from "payload/config"; import path from "path"; ```` import Users from "./collections/Users"; import Posts from "./collections/Posts"; export default buildConfig({ serverURL: "http://localhost:4321", admin: { user: Users.slug, }, collections: [Users, Posts], typescript: { outputFile: path.resolve(__dirname, "payload-types.ts"), }, graphQL: { schemaOutputFile: path.resolve(__dirname, "generated-schema.graphql"), }, }); ``` This will make a new collection called "Posts" appear in your PayloadCMS Dashboard next to the "Users" collection. ```` 2. Enter the "Posts" collection and create a new post. After saving it, you will notice the API URL appear in the bottom right corner. 3. With the dev server running, open `http://localhost:4321/api/posts` in your browser. You should see a JSON file containing the post you have created as an object. ``` { "docs":[ { "id":"64098b16483b0f06a7e20ed4", "title":"Astro & PayloadCMS Title 🚀", "content":"Astro & PayloadCMS Content", "slug":"astro-payloadcms-slug", "createdAt":"2023-03-09T07:30:30.837Z", "updatedAt":"2023-03-09T07:30:30.837Z" } ], "totalDocs":1, "limit":10, "totalPages":1, "page":1, "pagingCounter":1, "hasPrevPage":false, "hasNextPage":false, "prevPage":null, "nextPage":null } ``` :::tip By default, both Astro and PayloadCMS will use port 4321. You might want to change the PayloadCMS port in the `src/server.ts` file. Don't forget to update the `serverURL` in `src/payload.config.ts` as well. ::: ### Fetching Data Fetch your PayloadCMS data through your site's unique REST API URL and the route for your content. (By default, PayloadCMS will mount all routes through `/api`.) Then, you can render your data properties using Astro's `set:html=""` directive. Together with your post, PayloadCMS will return some top-level metadata. The actual documents are nested within the `docs` array. For example, to display a list of post titles and their content: ``` --- const res = await fetch("http://localhost:5000/api/posts") // http://localhost:4321/api/posts by default const posts = await res.json() --- { posts.docs.map((post) => (

)) } ``` ## Building a blog with PayloadCMS and Astro Create a blog index page `src/pages/index.astro` to list each of your posts with a link to its own page. Fetching via the API returns an array of objects (posts) that include, among others, the following properties: - `title` - `content` - `slug` ``` --- const res = await fetch("http://localhost:5000/api/posts") // http://localhost:4321/api/posts by default const posts = await res.json() ---

Astro + PayloadCMS 🚀

Blog posts list:

``` ### Using the PayloadCMS API to generate pages Create a page `src/pages/posts/[slug].astro` to [dynamically generate a page](/en/guides/routing/#dynamic-routes) for each post. ``` --- }); } ---

``` ### Publishing your site To deploy your site visit our [deployment guide](/en/guides/deploy/) and follow the instructions for your preferred hosting provider. ## Community Resources - Check out the [official Astro Payload CMS integration](https://github.com/payloadcms/payload/tree/main/examples/astro). - Try this [Payload CMS & Astro Template](https://github.com/Lambdo-Labs/payloadcms-astro-template). - Check out [Astroad](https://github.com/mooxl/astroad) for easy development and VPS deployment with Docker. ## [Prepr CMS & Astro](https://docharvest.github.io/docs/astro/guides/cms/preprcms/) ## [Prismic & Astro](https://docharvest.github.io/docs/astro/guides/cms/prismic/) Contents astro Prismic & Astro Astro Prismic & Astro [Prismic](https://prismic.io/) is a headless content management system. ## Community Resources - [Building with Astro & Prismic - w/ Nate Moore](https://www.youtube.com/watch?v=qFUfuDSLdxM) (livestream) and the [repo from the show](https://github.com/natemoo-re/miles-of-code). ## [Sanity & Astro](https://docharvest.github.io/docs/astro/guides/cms/sanity/) Contents astro Sanity & Astro Astro Sanity & Astro [Sanity](https://www.sanity.io) is a headless content management system that focuses on [structured content](https://www.sanity.io/structured-content-platform). ## Official Resources - [Official Sanity integration for Astro](https://www.sanity.io/plugins/sanity-astro) - [Build your blog with Astro and Sanity](https://www.sanity.io/guides/sanity-astro-blog) - [A minimal Astro site with a Sanity Studio](https://www.sanity.io/templates/astro-sanity-clean) ## [Sitecore Experience Manager & Astro](https://docharvest.github.io/docs/astro/guides/cms/sitecore/) ## [Sitepins & Astro](https://docharvest.github.io/docs/astro/guides/cms/sitepins/) Contents astro Sitepins & Astro Astro Sitepins & Astro [Sitepins](https://sitepins.com) is a Git-based, headless CMS for websites built with modern frameworks like Astro. It offers a clean WYSIWYG editor, a version-controlled content workflow, and seamless integration with Astro and other SSGs. ## Getting started 1. [Create a Sitepins account](https://app.sitepins.com/register). 2. Connect your GitHub repository that contains your Astro project. 3. Configure your content, media and config folders and start editing in the visual editor. Once connected, Sitepins will sync your content from the selected folder and provide a visual interface to manage and publish content with full Git version control. ## Official Resources - [Sitepins Website](https://sitepins.com) - [Documentation](https://docs.sitepins.com) - [Live Demo](https://demo.sitepins.com) ## [Spinal & Astro](https://docharvest.github.io/docs/astro/guides/cms/spinal/) ## [Headless Statamic & Astro](https://docharvest.github.io/docs/astro/guides/cms/statamic/) Contents astro Headless Statamic & Astro Astro Headless Statamic & Astro [Statamic](https://statamic.com/) is a modern, flat-file CMS. It allows developers to easily create dynamic websites and applications while offering content editors an intuitive and user-friendly interface for managing content. ## Integrating with Astro Statamic comes with a built-in [REST API](https://statamic.dev/rest-api) and [GraphQL API](https://statamic.dev/graphql) to connect your data to Astro. ### Prerequisites To get started, you will need to have the following: 1. REST API and GraphQL API are only available in a pro version of Statamic. You can try the Pro version free on your [local machine](https://statamic.dev/tips/how-to-enable-statamic-pro#trial-mode). 2. **An Astro project** - If you still need an Astro project, our [Installation guide](/en/install-and-setup/) will get you up and running quickly. 3. **A Statamic site** - If you need a Statamic website, [this guide](https://statamic.dev/quick-start-guide) will help you get started. Remember to [enable REST API](https://statamic.dev/rest-api#enable-the-api) or [GraphQL API](https://statamic.dev/graphql#enable-graphql) by adding `STATAMIC_API_ENABLED=true` or `STATAMIC_GRAPHQL_ENABLED=true` in the `.env` file and enable required resources in the API configuration file. :::caution All the examples assume that your website has a collection called `posts`, that has a blueprint called `post`, and this blueprint has a title field (fieldtype text) and content (fieldtype markdown). ::: ### Fetching Data :::caution If you are using Statamic and Astro on your local machine remember to use `127.0.0.1` instead of `localhost` when fetching the API. When requesting from the Astro server `localhost` doesn't resolve correctly like it does in the browser, and any fetch to either API will fail. ::: #### REST API Fetch your Statamic data from your site's REST API URL. By default, it's `https://[YOUR-SITE]/api/`. Then, you can render your data properties using Astro's `set:html={}` directive. For example, to display a list of titles and their content from a selected [collection](https://statamic.dev/collections): ``` --- const res = await fetch("https://[YOUR-SITE]/api/collections/posts/entries?sort=-date") const posts = await res.json() ---

Astro + Statamic 🚀

{ posts.map((post) => (

)) } ``` #### GraphQL Fetch your Statamic data with your site's GraphQL API URL. By default, it's `https://[YOUR-SITE]/graphql/`. Then, you can render your data properties using Astro's `set:html={}` directive. For example, to display a list of titles and their content from a selected [collection](https://statamic.dev/collections): ``` --- const graphqlQuery = { query: ` query Entries($page: Int, $locale: String) { entries( collection: "posts" sort: "date asc" limit: 20 page: $page filter: { locale: $locale } ) { current_page has_more_pages data { title ... on Entry_Posts_Post { content } } } } `, variables: { page: page, locale: locale, }, }; const res = await fetch("https://[YOUR-SITE]/graphql", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(graphqlQuery), }) const { data } = await res.json(); const entries = data?.entries; ---

Astro + Statamic 🚀

{ entries.data.map((post) => (

)) } ``` ### Publishing your site To deploy your Astro site visit our [deployment guides](/en/guides/deploy/) and follow the instructions for your preferred hosting provider. ## Community Resources - [How to build a static site using Statamic as headless CMS](https://buddy.works/guides/statamic-rest-api) - [Implementing Astro live previews in headless Statamic](https://maciekpalmowski.dev/implementing-live-previews-in-headless-statamic-when-using-astro/) ## [Storyblok & Astro](https://docharvest.github.io/docs/astro/guides/cms/storyblok/) ## [Strapi & Astro](https://docharvest.github.io/docs/astro/guides/cms/strapi/) ## [StudioCMS & Astro](https://docharvest.github.io/docs/astro/guides/cms/studiocms/) Contents astro StudioCMS & Astro Astro StudioCMS & Astro [StudioCMS](https://studiocms.dev/) is a headless CMS for Astro, built with Astro, that provides a user-friendly and configurable dashboard for content management as well as a custom rendering system to display your Astro components. ## Official resources - [StudioCMS documentation](https://docs.studiocms.dev/) - [StudioCMS GitHub repository](https://github.com/withstudiocms/studiocms) - [StudioCMS Discord community](https://chat.studiocms.dev) ## [TinaCMS & Astro](https://docharvest.github.io/docs/astro/guides/cms/tina-cms/) ## [Umbraco & Astro](https://docharvest.github.io/docs/astro/guides/cms/umbraco/) Contents astro Umbraco & Astro Astro Umbraco & Astro [Umbraco CMS](https://umbraco.com/) is an open-source ASP.NET Core CMS. By default, Umbraco uses Razor pages for its front-end, but can be used as a headless CMS. ## Integrating with Astro In this section you will use Umbraco's native [Content Delivery API](https://docs.umbraco.com/umbraco-cms/reference/content-delivery-api) to provide content to your Astro project. ### Prerequisites To get started, you will need to have the following: 1. **An Astro project** - If you don’t have an Astro project yet, our [Installation guide](/en/install-and-setup/) will get you up and running in no time. 2. **An Umbraco (v12+) project** - If you don’t have an Umbraco project yet, please see this [Installation guide](https://docs.umbraco.com/umbraco-cms/fundamentals/setup/install/). ### Setting up the Content Delivery API To enable the Content Delivery API, update your Umbraco project's `appsettings.json` file: ``` { "Umbraco": { "CMS": { "DeliveryApi": { "Enabled": true, "PublicAccess": true } } } } ``` You can configure additional options as needed such as public access, API keys, allowed content types, membership authorisation, and more. See the [Umbraco Content Delivery API documentation](https://docs.umbraco.com/umbraco-cms/reference/content-delivery-api) for more information. ### Fetching Data Use a `fetch()` call to the Content Delivery API to access your content and make it available to your Astro components. The following example displays a list of fetched articles, including properties such as the article date and content. ``` --- const res = await fetch('http://localhost/umbraco/delivery/api/v2/content?filter=contentType:article'); const articles = await res.json(); ---

Astro + Umbraco 🚀

{ articles.items.map((article) => (

{article.name}

{article.properties.articleDate}

)) } ``` Read more about [data fetching in Astro](/en/guides/data-fetching/). ## Building a blog with Umbraco and Astro ### Prerequisites - **An Astro project** - If you don’t have an Astro project yet, our [Installation guide](/en/install-and-setup/) will get you up and running in no time. - **An Umbraco project (v12+)** with the Content Delivery API enabled - Follow this [Installation guide](https://docs.umbraco.com/umbraco-cms/fundamentals/setup/install/) to create a new project. ### Creating blog posts in Umbraco From the [Umbraco backoffice](https://docs.umbraco.com/umbraco-cms/fundamentals/backoffice), create a Document Type for a simple blog article called 'Article'. 1\. Configure your 'Article' Document Type with the following properties: ``` - Title (DataType: Textstring) - Article Date (DataType: Date Picker) - Content (DataType: Richtext Editor) ``` 2. Toggle "Allow as root" to `true` on the 'Article' document type. 3. From the "Content" section in the Umbraco backoffice, [create and publish your first blog post](https://docs.umbraco.com/umbraco-cms/fundamentals/data/defining-content). Repeat the process as many times as you like. For more information, watch a [video guide on creating Document Types](https://www.youtube.com/watch?v=otRuIkN80qM). ### Displaying a list of blog posts in Astro Create a `src/layouts/` folder, then add a new file `Layout.astro` with the following code: ``` --- --- My Blog with Astro and Umbraco
``` Your project should now contain the following files: \- src/ - \*\*layouts/\*\* - \*\*Layout.astro\*\* - pages/ - index.astro To create a list of blog posts, first `fetch` to call the Content Delivery API `content` endpoint and filter by contentType of 'article'. The article objects will include the properties and content set in the CMS. You can then loop through the articles and display a each title with a link to its article. Replace the default contents of `index.astro` with the following code: ``` --- const res = await fetch('http://localhost/umbraco/delivery/api/v2/content?filter=contentType:article'); const articles = await res.json(); ---

Blog Articles

{ articles.items.map((article: any) => (
)) } ``` ### Generating individual blog posts Create the file `[...slug].astro` at the root of the `/pages/` directory. This file will be used to [generate the individual blog pages dynamically](/en/guides/routing/#dynamic-routes). Note that the `params` property, which generates the URL path of the page, contains `article.route.path` from the API fetch. Similarly, the `props` property must include the entire `article` itself so that you can access all the information in your CMS entry. Add the following code to `[...slug].astro` which will create your individual blog post pages: ``` --- let articles = await data.json(); return articles.items.map((article: any) => ({ params: { slug: article.route.path }, props: { article: article }, })); } const { article } = Astro.props; ---

{article.properties.title}

{article.properties.articleDate}

``` Your project should now contain the following files: \- src/ - layouts/ - Layout.astro - pages/ - index.astro - \*\*\[...slug\].astro\*\* With the dev server running, you should now be able to view your Umbraco-created content in your browser preview of your Astro project. Congratulations! 🚀 ## Publishing your site To deploy your site visit our [deployment guides](/en/guides/deploy/) and follow the instructions for your preferred hosting provider. ## Local dev, HTTPS and self-signed SSL certificates If you are using the Umbraco HTTPS endpoint locally, any `fetch` queries will result in `fetch failed` with code `DEPTH_ZERO_SELF_SIGNED_CERT`. This is because Node (upon which Astro is built) won't accept self-signed certificates by default. To avoid this, use the Umbraco HTTP endpoint for local dev. Alternatively, you can set `NODE_TLS_REJECT_UNAUTHORIZED=0` in an `env.development` file and update `astro.config.js` as shown: ``` NODE_TLS_REJECT_UNAUTHORIZED=0 ``` ``` const { NODE_TLS_REJECT_UNAUTHORIZED } = loadEnv(process.env.NODE_ENV, process.cwd(), ""); process.env.NODE_TLS_REJECT_UNAUTHORIZED = NODE_TLS_REJECT_UNAUTHORIZED; // https://astro.build/config ``` This method is described in more detail in this [blog post showing how to configure your project for self-signed certificates](https://kjac.dev/posts/jamstack-for-free-with-azure-and-cloudflare/), with an [accompanying repo](https://github.com/kjac/UmbracoAzureCloudflare). ## Official Documentation - [Content Delivery API - Umbraco Documentation](https://docs.umbraco.com/umbraco-cms/reference/content-delivery-api) ## Community Resources - [Astro-nomically Performant Websites using the Content Delivery API - Louis Richardson](https://24days.in/umbraco-cms/2023/sustainable-performant/astronomically-performant/) - [Generating a TypeScript OpenAPI client from Umbraco's Content Delivery API - Rick Butterfield](https://rickbutterfield.dev/blog/typescript-openapi-umbraco-content-delivery/) - [Jamstack For Free With Azure And CloudFlare - Kenn Jacobsen](https://kjac.dev/posts/jamstack-for-free-with-azure-and-cloudflare/) - [Quick n' dirty blog with Astro and Umbraco - Kenn Jacobsen](https://kjac.dev/posts/quick-n-dirty-blog-with-astro-and-umbraco/) - [Talk: Bake, Don't Fry - Astro & The Content Delivery API - Adam Prendergast](https://www.youtube.com/watch?v=zNxqI25dtx4) ## [Vault CMS & Astro](https://docharvest.github.io/docs/astro/guides/cms/vault-cms/) Contents astro Vault CMS & Astro Astro Vault CMS & Astro [Vault CMS](https://vaultcms.org) is a headless CMS for Astro powered by Obsidian and Git. Write in Markdown with live preview inside a preconfigured Obsidian vault, then deploy to your Astro site. ## Official Resources - [Vault CMS Documentation](https://docs.vaultcms.org/) - [Vault CMS Source Code](https://github.com/davidvkimball/vault-cms) ## [Headless WordPress & Astro](https://docharvest.github.io/docs/astro/guides/cms/wordpress/) Contents astro Headless WordPress & Astro Astro Headless WordPress & Astro [WordPress](https://wordpress.org/) is a content management system that includes its own frontend, but can also be used as a headless CMS to provide content to your Astro project. ## Integrating with Astro WordPress comes with a built-in [WordPress REST API](https://developer.wordpress.org/rest-api/) to connect your WordPress data to Astro. You can optionally install [WPGraphQL](https://wordpress.org/plugins/wp-graphql/) or [Gato GraphQL](https://wordpress.org/plugins/gatographql/) on your site to use GraphQL. ### Prerequisites To get started, you will need to have the following: 1. **An Astro project** - If you don't have an Astro project yet, our [Installation guide](/en/install-and-setup/) will get you up and running in no time. 2. **A WordPress site** - Your site's REST API is `[YOUR_SITE]/wp-json/wp/v2/` and is available by default with any WordPress site. It is also possible to [set up WordPress on a local environment](https://wordpress.org/support/article/installing-wordpress-on-your-own-computer/). ### Setting up Credentials Your WordPress REST API is available to external requests for data fetching without authentication by default. This does not allow users to modify your data or site settings and allows you to use your data in your Astro project without any credentials. You may choose to [require authentication](https://developer.wordpress.org/rest-api/frequently-asked-questions/#require-authentication-for-all-requests) if necessary. ### Fetching Data Fetch your WordPress data through your site's unique REST API URL and the route for your content. (For a blog, this will commonly be `posts`.) Then, you can render your data properties using Astro's `set:html={}` directive. For example, to display a list of post titles and their content: ``` --- const res = await fetch("https://[YOUR-SITE]/wp-json/wp/v2/posts"); const posts = await res.json(); ---

Astro + WordPress 🚀

{ posts.map((post) => (

)) } ``` The WordPress REST API includes [global parameters](https://developer.wordpress.org/rest-api/using-the-rest-api/global-parameters/) such as `_fields` and `_embed`. A large quantity of data is available to you via this API, so you may wish to only fetch certain fields. You can restrict your response by adding the [`_fields`](https://developer.wordpress.org/rest-api/using-the-rest-api/global-parameters/#_fields) parameter to the API URL, for example: `[YOUR-SITE]/wp/v2/posts?_fields=author,id,excerpt,title,link` The API can also return content related to your post, such as a link to the parent post, or to comments on the post. You can add the [`_embed`](https://developer.wordpress.org/rest-api/using-the-rest-api/global-parameters/#_embed) parameter to the API URL (e.g. `[YOUR-SITE]/wp/v2/posts?_embed`) to indicate to the server that the response should include these embedded resources. ## Building a blog with WordPress and Astro This example fetches data from the public WordPress API of [https://norian.studio/dinosaurs/](https://norian.studio/dinosaurs/). This WordPress site stores information about individual dinosaurs under the `dinos` route, just as a blog would store individual blog posts under the `posts` route. This example shows how to reproduce this site structure in Astro: an index page that lists dinosaurs with links to dynamically-generated individual dinosaur pages. :::note To use [Custom Post Types (CPT)](https://learn.wordpress.org/lesson-plan/custom-post-types/) in your WordPress API (not just `post` and `page`), you will have to [configure them in your WordPress dashboard](https://stackoverflow.com/questions/48536646/how-can-i-get-data-from-custom-post-type-using-wp-rest-api) or [add REST API Support For Custom Content Types](https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-rest-api-support-for-custom-content-types/) in WordPress. This example fetches data from a WordPress site whose content types have already been configured and exposed to the REST API. ::: ### Displaying a list of WordPress posts The page `src/pages/index.astro` lists each dinosaur, with a description and link to its own page. \- src/ - pages/ - \*\*index.astro\*\* - dinos/ - \[slug\].astro - astro.config.mjs - package.json Fetching via the API returns an object that includes the properties: - `title.rendered` - Contains the HTML rendering of the title of the post. - `content.rendered` - Contains the HTML rendering of the content of the post. - `slug` - Contains the slug of the post. (This provides the link to the dynamically-generated individual dinosaur pages.) ``` --- let res = await fetch("https://norian.studio/wp-json/wp/v2/dinos"); let posts = await res.json(); ---

List of Dinosaurs

{ posts.map((post) => ( )) }
``` ### Using the WordPress API to generate pages The page `src/pages/dinos/[slug].astro` [dynamically generates a page](/en/guides/routing/#dynamic-routes) for each dinosaur. ``` --- const { slug } = Astro.params; let res = await fetch(`https://norian.studio/wp-json/wp/v2/dinos?slug=${slug}`); let [post] = await res.json(); // The getStaticPaths() is required for static Astro sites. // If using SSR, you will not need this function. let posts = await data.json(); return posts.map((post) => ({ params: { slug: post.slug }, props: { post: post }, })); } ---

``` ### Returning embedded resources The `_embed` query parameter instructs the server to return related (embedded) resources. ``` --- const { slug } = Astro.params; let res = await fetch(`https://norian.studio/wp-json/wp/v2/dinos?slug=${slug}&_embed`); let [post] = await res.json(); --- ``` The `_embedded['wp:featuredmedia']['0'].media_details.sizes.medium.source_url` property is returned, and can be used to display the featured image on each dinosaur page. (Replace `medium` with your desired image size.) ```

``` ### Publishing your site To deploy your site visit our [deployment guides](/en/guides/deploy/) and follow the instructions for your preferred hosting provider. ## Community Resources - [Building An Astro Website With WordPress As A Headless CMS](https://blog.openreplay.com/building-an-astro-website-with-wordpress-as-a-headless-cms/) by Chris Bongers. - [Building with Astro x WordPress](https://www.youtube.com/watch?v=Jstqgklvfnc) on Ben Holmes's stream. - [Building a Headless WordPress Site with Astro](https://developers.wpengine.com/blog/building-a-headless-wordpress-site-with-astro) by Jeff Everhart. - [Astro and WordPress as an API](https://darko.io/posts/wp-as-an-api/) by Darko Bozhinovski. ## Community Resources :::note\[Have a resource to share?\] If you found (or made!) a helpful video or blog post about using headless WordPress with Astro, [add it to this list](https://github.com/withastro/docs/edit/main/src/content/docs/en/guides/cms/wordpress.mdx)! ::: ## [Zero & Astro](https://docharvest.github.io/docs/astro/guides/cms/zero/) Contents astro Zero & Astro Astro Zero & Astro [Zero](https://www.zerocms.io) is an AI-native, Git-based CMS built specifically for Astro, requiring no integration and working directly with existing Zod schemas and Astro components. ## [Configuration overview](https://docharvest.github.io/docs/astro/guides/configuring-astro/) Contents astro Configuration overview Astro Configuration overview ```` 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` is also supported. Read Astro's [configuration reference](/en/reference/configuration-reference/) for a full overview of all supported configuration options. ## The TypeScript config File Every Astro starter project includes a `tsconfig.json` file in your project. Astro's [component script](/en/basics/astro-components/#the-component-script) is TypeScript, which provides Astro's editor tooling and allows you to optionally add syntax to your JavaScript for type checking of your own project code. Use the `tsconfig.json` file to configure the TypeScript template that will perform type checks on your code, configure TypeScript plugins, set import aliases, and more. Read Astro's [TypeScript guide](/en/guides/typescript/) for a full overview of TypeScript options and Astro's built-in utility types. ## Development Experience While you work in development mode, you can take advantage of your code editor and other tools to improve the Astro developer experience. Astro provides its own official VS Code extension and is compatible with several other popular editor tools. Astro also provides a customizable toolbar that displays in your browser preview while the dev server is running. You can install and even build your own toolbar apps for additional functionality. Read Astro's guides to [editor setup options](/en/editor-setup/) and [using the dev toolbar](/en/guides/dev-toolbar/) to learn how to customize your development experience. ## Common new project tasks Here are some first steps you might choose to take with a new Astro project. ### Add your deployment domain For generating your sitemap and creating canonical URLs, configure your deployment URL in the [`site`](/en/reference/configuration-reference/#site) option. If you are deploying to a path (e.g. `www.example.com/docs`), you can also configure a [`base`](/en/reference/configuration-reference/#base) for the root of your project. Additionally, different deployment hosts may have different behavior regarding trailing slashes at the end of your URLs. (e.g. `example.com/about` vs `example.com/about/`). Once your site is deployed, you may need to configure your [`trailingSlash`](/en/reference/configuration-reference/#trailingslash) preference. ```js title="astro.config.mjs" ```` ### Add site metadata Astro does not use its configuration file for common SEO or meta data, only for information required to build your project code and render it to HTML. Instead, this information is added to your page `` using standard HTML `` and `` tags, just as if you were writing plain HTML pages. One common pattern for Astro sites is to create a `` [`.astro` component](/en/basics/astro-components/) that can be added to a common [layout component](/en/basics/layouts/) so it can apply to all your pages. ``` --- const { ...props } = Astro.props; --- ``` Because `Head.astro` is just a regular Astro component, you can import files and receive props passed from other components, such as a specific page title. ``` --- const { title = "My Astro Website", ...props } = Astro.props; --- {title} ``` ## [Content collections](https://docharvest.github.io/docs/astro/guides/content-collections/) Contents astro Content collections Astro Content collections // 2. Import loader(s) // 3. Import Zod // 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) ```` You can then use the dedicated `getCollection()` and `getEntry()` functions to [query your content collections data](#querying-build-time-collections) and render your content. 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. ## Build-time collection loaders 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. 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 `glob()` loader 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} const blog = defineCollection({ loader: glob({ pattern: "**/*.md", base: "./src/data/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. ``` --- title: My Blog Post slug: my-custom-id/supports/slashes --- Your blog post content here. ``` ``` { "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: ``` 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. ``` const dogs = defineCollection({ loader: file("src/data/dogs.json"), }); ``` 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. 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: ``` // Specify an `id` property in each object of an array [ { "id": "poodle", "coat": "curly", "shedding": "low" }, { "id": "afghan", "coat": "short", "shedding": "low" } ] ``` ``` // Each key will be used as the `id` { "poodle": { "coat": "curly", "shedding": "low" }, "afghan": { "coat": "silky", "shedding": "low" } } ``` #### Parsing other data formats Support for parsing single JSON, YAML, and TOML files into collection entries with 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 asynchronous). The following example shows importing a third-party CSV parser then passing a custom `parser` function to the `file()` loader: ``` const cats = defineCollection({ loader: file("src/data/cats.csv", { parser: (text) => parseCsv(text, { columns: true, skipEmptyLines: true }), }), }); ``` ##### Nested `.json` documents The `parser()` argument can be used to load a single collection from a nested JSON document. For example, this JSON file contains multiple collections: ``` {"dogs": [{}], "cats": [{}]} ``` 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: ``` const dogs = defineCollection({ loader: file("src/data/pets.json", { parser: (text) => JSON.parse(text).dogs }) }); const cats = defineCollection({ loader: file("src/data/pets.json", { parser: (text) => JSON.parse(text).cats }) }); ``` ### Custom build-time loaders 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. Then you can import and define your custom loader in your collections config, passing any required values: ``` const blog = defineCollection({ loader: myLoader({ url: "https://api.example.com/posts", apiKey: "my-secret", }), }); ``` :::tip Find community-built and third-party loaders in the [Astro integrations directory](https://astro.build/integrations/?search=&categories%5B%5D=loaders). ::: 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/guides/integrations/) that others can use in their projects. See the full [Content Loader API](/en/reference/content-loader-reference/) for examples of how to build your own loader. ## 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. Schemas also power Astro's automatic TypeScript typings for your content. When you define a schema for your collection, Astro will automatically generate and apply a TypeScript interface to it. The result is full TypeScript support when you query your collection, including property autocompletion and type-checking. :::tip 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. ::: 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): ``` const blog = defineCollection({ loader: glob({ pattern: "**/*.md", base: "./src/data/blog" }), schema: z.object({ title: z.string(), description: z.string(), pubDate: z.coerce.date(), updatedDate: z.coerce.date().optional(), }), }); const dogs = defineCollection({ loader: file("src/data/dogs.json"), schema: z.object({ id: z.string(), breed: z.string(), temperament: z.array(z.string()), }), }); ``` ### 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 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 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 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 Collection entries can also "reference" other related entries. 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: ``` const blog = defineCollection({ 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 `id` relatedPosts: z.array(reference("blog")), }), }); const authors = defineCollection({ loader: glob({ pattern: "**/*.json", base: "./src/data/authors" }), schema: z.object({ name: z.string(), portfolio: z.url(), }), }); ``` This example blog post specifies the `id`s of related posts and the `id` of the post author: ``` --- title: "Welcome to my blog" author: ben-holmes # references `src/data/authors/ben-holmes.json` relatedPosts: - 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](#accessing-referenced-data). ## Querying build-time collections 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. ``` --- // Get all entries from a collection. // Requires the name of the collection as an argument. 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: ``` --- const posts = (await getCollection('blog')).sort( (a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(), ); --- ``` See the full list of properties returned by the [`CollectionEntry` type](/en/reference/modules/astro-content/#collectionentry). ### Using content in Astro templates 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: ``` --- const posts = await getCollection('blog'); ---

My posts

``` ### 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. ``` --- const entry = await getEntry("blog", "post-1"); if (!entry) { throw new Error("Entry not found"); } const { Content } = await render(entry); ---

{entry.data.title}

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

``` When working with MDX entries, you can also [pass your own components to ``](/en/guides/integrations-guide/mdx/#passing-components-to-mdx-content) to replace HTML elements with custom alternatives. #### Passing content as props A component can also pass an entire collection entry as a prop. You can use the [`CollectionEntry`](/en/reference/modules/astro-content/#collectionentry) utility to correctly type your component's props using TypeScript. This utility takes a string argument that matches the name of your collection schema and will inherit all of the properties of that collection's schema. ``` --- interface Props { post: CollectionEntry<'blog'>; } // `post` will match your 'blog' collection schema type const { post } = Astro.props; --- ``` ### Filtering collection queries `getCollection()` takes an optional "filter" callback that allows you to filter your query based on an entry's `id` or `data` properties. 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: ``` --- // Example: Filter out content entries with `draft: true` 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: ``` --- // Example: Filter out content entries with `draft: true` only when building for production 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: ``` --- // Example: Filter entries by sub-directory in the collection const englishDocsEntries = await getCollection('docs', ({ id }) => { return id.startsWith('en/'); }); --- ``` ### Accessing referenced data To access [references defined in your schema](#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. ``` --- // First, query a blog post const blogPost = await getEntry("blog", "Adventures in Space"); // If the blog post doesn't exist, throw an error if (!blogPost) { throw new Error("Blog post not found"); } // 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); // 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); ---

{blogPost.data.title}

Author: {author.data.name}

You might also like:

{relatedPosts.map((post) => {post.data.title})} ``` ## 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 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.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) 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 receives the entire collection entry as a prop for [use in your page template](#using-content-in-astro-templates). ``` --- // 1. Generate a new path for every collection entry return posts.map(post => ({ params: { id: post.id }, props: { post }, })); } // 2. For your template, you can get the entry directly from the prop const { post } = Astro.props; const { Content } = await render(post); ---

{post.data.title}

``` 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. `[...id]`)](/en/guides/routing/#rest-parameters) in the `.astro` filename for this dynamic routing page. ::: ### Building routes on demand at request time 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/#getliveentry) for live collection pages where data is (re)fetched at each request time. ``` --- 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); ---

{post.data.title}

``` :::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 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. ``` // Define live collections for accessing real-time data 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) ``` 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). ::: 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: ``` 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)}`, })), }); ``` When using Zod schemas with live collections, validation errors are automatically caught and returned as `AstroError` objects: ``` --- // 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. ``` --- } // 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: ``` --- } const { entry, error } = await getLiveEntry("articles", Astro.params.id); if (!entry || 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: ``` --- } 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"); } } --- ``` ### Caching live data When live loaders provide [cache hints](/en/reference/content-loader-reference/#live-loaders), `getLiveEntry()` and `getLiveCollection()` return a `cacheHint` object. This allows you to control the [caching behavior](/en/guides/caching/) of your routes without manually setting headers. Cache hints include [`tags`](/en/reference/content-loader-reference/#cachehinttags) for targeted invalidation and [`lastModified`](/en/reference/content-loader-reference/#cachehintlastmodified) to keep cached data fresh. You can also combine the cache hints with your own [cache options](/en/reference/cache-provider-reference/#cacheoptions) to further customize caching behavior. See the \[Content Loader Reference\](/en/reference/content-loader-reference/) for more about implementing cache hints in your live loaders. #### Entry-level cache hints Pass the `cacheHint` returned by `getLiveEntry()` to [`Astro.cache.set()`](/en/reference/api-reference/#cacheset) to apply the loader's recommended caching strategy. The following example passes the loader's cache hint and adds a `maxAge` to control how long the response stays fresh: ``` --- const { entry, error, cacheHint } = await getLiveEntry('products', Astro.params.id); if (error) { return Astro.redirect('/404'); } if (cacheHint) { Astro.cache.set(cacheHint); } Astro.cache.set({ maxAge: 300 }); ---

{entry.data.name}

``` You can also pass a [`LiveDataEntry`](/en/reference/content-loader-reference/#livedataentry) directly to let Astro extract its `cacheHint` automatically: ``` --- const { entry, error } = await getLiveEntry('products', Astro.params.id); if (error) { return Astro.redirect('/404'); } Astro.cache.set(entry); Astro.cache.set({ maxAge: 300, swr: 60 }); ---

{entry.data.name}

``` #### Collection-level cache hints When fetching a full collection with `getLiveCollection()`, Astro merges cache hints from the collection response and all individual entries: tags are accumulated, and the most recent `lastModified` wins. The following example passes the merged cache hint from a collection and sets a 10-minute freshness window: ``` --- const { entries, error, cacheHint } = await getLiveCollection('products'); if (error) { return new Response('Error loading products', { status: 500 }); } if (cacheHint) { Astro.cache.set(cacheHint); } Astro.cache.set({ maxAge: 600 }); ---
    {entries.map((p) =>
  • {p.data.name}
  • )}
``` #### Invalidating by entry You can invalidate cached entries by passing a `LiveDataEntry` to [`cache.invalidate()`](/en/reference/api-reference/#cacheinvalidate). This allows you to target specific cached responses for invalidation based on the entry's cache tags, without needing to clear the entire cache. The following example invalidates the cached response for a specific product entry: ``` if (entry) { await context.cache.invalidate(entry); } return Response.json({ ok: true }); } ``` ## Using JSON Schema files in your editor Astro auto-generates [JSON Schema](https://json-schema.org/) files for collections, which you can use in your editor to get IntelliSense and type-checking for data files. 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 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. In the following example, a data file in `src/data/authors/` uses the schema generated for the `authors` collection: ``` { "$schema": "../../../.astro/collections/authors.schema.json", "name": "Armand", "skills": ["Astro", "Starlight"] } ``` #### Use a schema for a group of JSON files in VS Code 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.schemas": [ { "fileMatch": ["/src/data/authors/**"], "url": "./.astro/collections/authors.schema.json" } ] } ``` ### 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: ``` # yaml-language-server: $schema=../../../.astro/collections/authors.schema.json name: Armand skills: - Astro - Starlight ``` #### Use schemas for a group of YAML files in VS Code 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: ``` { "yaml.schemas": { "./.astro/collections/authors.schema.json": ["/src/content/authors/*.yml"] } } ``` See [“Associating schemas”](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml#associating-schemas) in the Red Hat YAML extension documentation for more details. ## [Data fetching](https://docharvest.github.io/docs/astro/guides/data-fetching/) Contents astro Data fetching Astro Data fetching `.astro` files can fetch remote data to help you generate your pages. ## `fetch()` in Astro All [Astro components](/en/basics/astro-components/) have access to the [global `fetch()` function](https://developer.mozilla.org/en-US/docs/Web/API/fetch) in their component script to make HTTP requests to APIs using the full URL (e.g. `https://example.com/api`). Additionally, you can construct a URL to your project's pages and endpoints that are rendered on demand on the server using [`new URL("/api", Astro.url)`](/en/reference/api-reference/#url). This fetch call will be executed at build time, and the data will be available to the component template for generating dynamic HTML. If [SSR](/en/guides/on-demand-rendering/) mode is enabled, any fetch calls will be executed at runtime. 💡 Take advantage of [**top-level `await`**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await#top_level_await) inside of your Astro component script. 💡 Pass fetched data to both Astro and framework components, as props. ``` --- // src/components/User.astro const response = await fetch("https://randomuser.me/api/"); const data = await response.json(); const randomUser = data.results[0]; ---

User

{randomUser.name.first} {randomUser.name.last}

``` :::note Remember, all data in Astro components is fetched when a component is rendered. Your deployed Astro site will fetch data **once, at build time**. In dev, you will see data fetches on component refreshes. If you need to re-fetch data multiple times client-side, use a [framework component](/en/guides/framework-components/) or a [client-side script](/en/guides/client-side-scripts/) in an Astro component. ::: ## `fetch()` in Framework Components The `fetch()` function is also globally available to any [framework components](/en/guides/framework-components/): ``` const data = await fetch('https://example.com/movies.json').then((response) => response.json()); // Components that are build-time rendered also log to the CLI. // When rendered with a `client:*` directive, they also log to the browser console. console.log(data); const Movies: FunctionalComponent = () => { // Output the result to the page return
{JSON.stringify(data)}
; }; ``` ## GraphQL queries Astro can also use `fetch()` to query a GraphQL server with any valid GraphQL query. ``` --- const response = await fetch( "https://swapi-graphql.netlify.app/.netlify/functions/index", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query: ` query getFilm ($id:ID!) { film(id: $id) { title releaseDate } } `, variables: { id: "ZmlsbXM6MQ==", }, }), } ); const json = await response.json(); const { film } = json.data; ---

Fetching information about Star Wars: A New Hope

Title: {film.title}

Year: {film.releaseDate}

``` ## Fetch from a Headless CMS Astro components can fetch data from your favorite CMS and then render it as your page content. Using [dynamic routes](/en/guides/routing/#dynamic-routes), components can even generate pages based on your CMS content. See our [CMS Guides](/en/guides/cms/) for full details on integrating Astro with headless CMSes including Storyblok, Contentful, and WordPress. ## Community resources - [Creating a fullstack app with Astro + GraphQL](https://robkendal.co.uk/blog/how-to-build-astro-site-with-graphql/) ## [Deploy your Astro Site](https://docharvest.github.io/docs/astro/guides/deploy/) ## [Deploy your Astro Site to AWS](https://docharvest.github.io/docs/astro/guides/deploy/aws/) Contents astro Deploy your Astro Site to AWS Astro Deploy your Astro Site to AWS [AWS](https://aws.amazon.com/) is a full-featured web app hosting platform that can be used to deploy an Astro site. Deploying your project to AWS requires using the [AWS console](https://aws.amazon.com/console/). (Most of these actions can also be done using the [AWS CLI](https://aws.amazon.com/cli/)). This guide will walk you through the steps to deploy your site to AWS using [AWS Amplify](https://aws.amazon.com/amplify/), [S3 static website hosting](https://aws.amazon.com/s3/), and [CloudFront](https://aws.amazon.com/cloudfront/). ## AWS Amplify AWS Amplify is a set of purpose-built tools and features that lets frontend web and mobile developers quickly and easily build full-stack applications on AWS. You can either deploy your Astro project as a static site, or as a server-rendered site. ### Static Site Your Astro project is a static site by default. 1\. Create a new Amplify Hosting project. 2. Connect your repository to Amplify. 3. Modify your build settings to match your project's build process. \`\`\`yaml version: 1 frontend: phases: preBuild: commands: - npm i -g pnpm - pnpm config set store-dir .pnpm-store - pnpm i build: commands: - pnpm run build artifacts: baseDirectory: /dist files: - '\*\*/\*' cache: paths: - .pnpm-store/\*\*/\* \`\`\` \`\`\`yaml version: 1 frontend: phases: preBuild: commands: - npm ci build: commands: - npm run build artifacts: baseDirectory: /dist files: - '\*\*/\*' cache: paths: - node\_modules/\*\*/\* \`\`\` \`\`\`yaml version: 1 frontend: phases: preBuild: commands: - yarn install build: commands: - yarn build artifacts: baseDirectory: /dist files: - '\*\*/\*' cache: paths: - node\_modules/\*\*/\* \`\`\` Amplify will automatically deploy your website and update it when you push a commit to your repository. ### Adapter for on-demand rendering In order to deploy your project as a server-rendered site, you will need to use the third-party, [community-maintained AWS Amplify adapter](https://github.com/alexnguyennz/astro-aws-amplify) and make some changes to your config. First, install the Amplify adapter. \`\`\`shell pnpm add astro-aws-amplify \`\`\` \`\`\`shell npm install astro-aws-amplify \`\`\` \`\`\`shell yarn add astro-aws-amplify \`\`\` Then, in your `astro.config.*` file, add the adapter and set the output to `server`. Once the adapter has been installed, you can set up your Amplify project. 1\. Create a new Amplify Hosting project. 2. Connect your repository to Amplify. 3. Modify your build settings to match the adapter's build process by either editing the build settings in the AWS console, or by adding an `amplify.yaml` in the root of your project. \`\`\`yaml version: 1 frontend: phases: preBuild: commands: - npm i -g pnpm - pnpm config set store-dir .pnpm-store - pnpm i build: commands: - pnpm run build - mv node\_modules ./.amplify-hosting/compute/default artifacts: baseDirectory: .amplify-hosting files: - '\*\*/\*' cache: paths: - .pnpm-store/\*\*/\* \`\`\` \`\`\`yaml version: 1 frontend: phases: preBuild: commands: - npm ci --cache .npm --prefer-offline build: commands: - npm run build - mv node\_modules ./.amplify-hosting/compute/default artifacts: baseDirectory: .amplify-hosting files: - '\*\*/\*' cache: paths: - .npm/\*\*/\* \`\`\` \`\`\`yaml version: 1 frontend: phases: preBuild: commands: - yarn install build: commands: - yarn build - mv node\_modules ./.amplify-hosting/compute/default artifacts: baseDirectory: .amplify-hosting files: - '\*\*/\*' cache: paths: - node\_modules/\*\*/\* \`\`\` Amplify will automatically deploy your website and update it when you push a commit to your repository. See \[AWS's Astro deployment guide\](https://docs.aws.amazon.com/amplify/latest/userguide/get-started-astro.html) for more info. ## S3 static website hosting S3 is the starting point of any application. It is where your project files and other assets are stored. S3 charges for file storage and number of requests. You can find more information about S3 in the [AWS documentation](https://aws.amazon.com/s3/). 1\. Create an S3 bucket with your project's name. ``` :::tip The bucket name should be globally unique. We recommend a combination of your project name and the domain name of your site. ::: ``` 2. Disable **"Block all public access"**. By default, AWS sets all buckets to be private. To make it public, you need to uncheck the "Block public access" checkbox in the bucket's properties. 3. Upload your built files located in `dist` to S3. You can do this manually in the console or use the AWS CLI. If you use the AWS CLI, use the following command after [authenticating with your AWS credentials](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html): ``` aws s3 cp dist/ s3:/// --recursive ``` 4. Update your bucket policy to allow public access. You can find this setting in the bucket's **Permissions > Bucket policy**. ``` { "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::/*" } ] } ``` :::caution Do not forget to replace `` with the name of your bucket. ::: 5. Enable website hosting for your bucket. You can find this setting in the bucket's **Properties > Static website hosting**. Set your index document to `index.html` and your error document to `404.html`. Finally, you can find your new website URL in the bucket's **Properties > Static website hosting**. :::note If you are deploying a single-page application (SPA), set your error document to `index.html`. ::: ## S3 with CloudFront CloudFront is a web service that provides content delivery network (CDN) capabilities. It is used to cache content of a web server and distribute it to end users. CloudFront charges for the amount of data transferred. Adding CloudFront to your S3 bucket is more cost-effective and provides a faster delivery. To connect S3 with CloudFront, create a CloudFront distribution with the following values: - **Origin domain:** Your S3 bucket static website endpoint. You can find your endpoint in your S3 bucket's **Properties > Static website hosting**. Alternative, you can select your s3 bucket and click on the callout to replace your bucket address with your bucket static endpoint. - **Viewer protocol policy:** "Redirect to HTTPS" This configuration will serve your site using the CloudFront CDN network. You can find your CloudFront distribution URL in the bucket's **Distributions > Domain name**. :::note When connecting CloudFront to an S3 static website endpoint, you rely on S3 bucket policies for access control. See [S3 static website hosting](#s3-static-website-hosting) section for more information about bucket policies. ::: ## Continuous deployment with GitHub Actions There are many ways to set up continuous deployment for AWS. One possibility for code hosted on GitHub is to use [GitHub Actions](https://github.com/features/actions) to deploy your website every time you push a commit. 1\. Create a new policy in your AWS account using \[IAM\](https://aws.amazon.com/iam/) with the following permissions. This policy will allow you to upload built files to your S3 bucket and invalidate the CloudFront distribution files when you push a commit. ```` ```json { "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": [ "s3:PutObject", "s3:ListBucket", "s3:DeleteObject", "cloudfront:CreateInvalidation" ], "Resource": [ "", "arn:aws:s3:::/*", "arn:aws:s3:::" ] } ] } ``` :::caution Do not forget to replace `` and ``. You can find the DISTRIBUTION_ARN in **CloudFront > Distributions > Details**. ::: ```` 2. Create a new IAM user and attach the policy to the user. This will provide your `AWS_SECRET_ACCESS_KEY` and `AWS_ACCESS_KEY_ID`. 3. Add this sample workflow to your repository at `.github/workflows/deploy.yml` and push it to GitHub. You will need to add `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `BUCKET_ID`, and `DISTRIBUTION_ID` as “secrets” to your repository on GitHub under **Settings** > **Secrets** > **Actions**. Click New repository secret to add each one. ``` name: Deploy Website on: push: branches: - main jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6 - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-east-1 - name: Install modules run: npm ci - name: Build application run: npm run build - name: Deploy to S3 run: aws s3 sync --delete ./dist/ s3://${{ secrets.BUCKET_ID }} - name: Create CloudFront invalidation run: aws cloudfront create-invalidation --distribution-id ${{ secrets.DISTRIBUTION_ID }} --paths "/*" ``` :::note Your `BUCKET_ID` is the name of your S3 bucket. Your `DISTRIBUTION_ID` is your CloudFront distribution ID. You can find your CloudFront distribution ID in **CloudFront > Distributions > ID** ::: ## Community Resources - [Deploy Astro to AWS Amplify](https://www.launchfa.st/blog/deploy-astro-aws-amplify) - [Deploy Astro to AWS Elastic Beanstalk](https://www.launchfa.st/blog/deploy-astro-aws-elastic-beanstalk) - [Deploy Astro to Amazon ECS on AWS Fargate](https://www.launchfa.st/blog/deploy-astro-aws-fargate) - [Troubleshooting SSR Amplify Deployments](https://docs.aws.amazon.com/amplify/latest/userguide/troubleshooting-ssr-deployment.html) ## [Deploy your Astro Site to AWS with Flightcontrol](https://docharvest.github.io/docs/astro/guides/deploy/aws-via-flightcontrol/) ## [Deploy your Astro Site to AWS with SST](https://docharvest.github.io/docs/astro/guides/deploy/aws-via-sst/) ## [Deploy your Astro Site to Azion](https://docharvest.github.io/docs/astro/guides/deploy/azion/) Contents astro Deploy your Astro Site to Azion Astro Deploy your Astro Site to Azion You can deploy your Astro project on [Azion](https://console.azion.com/), a platform for frontend developers to collaborate and deploy static (JAMstack) and SSR websites. ## Prerequisites To get started, you will need: - An [Azion account](https://www.azion.com/). If you don’t have one, you can sign up for a free account. - Your app code stored in a [GitHub](https://github.com/) repository. - [Azion CLI](https://www.azion.com/en/documentation/products/azion-cli/overview/) installed for faster project setup and deployment. ## How to Deploy through Azion Console Dashboard To start building, follow these steps: 1\. Access \[Azion Console\](https://console.azion.com). 2. On the homepage, click the \*\*\\+ Create\*\* button. - This opens a modal with the options to create new applications and resources. 3. Select the \*\*Import from GitHub\*\* option and click the card. - This action opens the settings page. 4. Connect your Azion account with GitHub. - A pop-up window will appear asking for authorization. 5. Select the repository you want to import from GitHub. 6. Configure the build settings: - \*\*Framework preset:\*\* Select the appropriate framework (e.g., \`Astro\`). - \*\*Root Directory:\*\* This refers to the directory in which your code is located. Your code must be located at the root directory, not a subdirectory. A ./ symbol appears in this field, indicating it’s a root directory. - \*\*Install Command:\*\* the command that compiles your settings to build for production. Build commands are executed through scripts. For example: npm run build or npm install for an NPM package. 7. Click \*\*Save and Deploy\*\*. 8. Monitor the deployment using \*\*Azion Real-Time Metrics\*\* and verify your site is live on the edge. ## How to Deploy a Static Site Using the Azion CLI 1. **Install the Azion CLI:** - Download and install the [Azion CLI](https://www.azion.com/en/documentation/products/azion-cli/overview/) for easier management and deployment. :::caution The Azion CLI does not currently support native Windows environments. However, you can use it on Windows through the Windows Subsystem for Linux (WSL). Follow the [WSL installation guide](https://docs.microsoft.com/en-us/windows/wsl/install) to set up a Linux environment on your Windows machine. ::: 2. **Authenticate the CLI:** - Run the following command to authenticate your CLI with your Azion account. ``` azion login ``` 3. **Set Up Your Application:** - Use the following commands to initialize and configure your project: ``` azion init ``` 4. **Build Your Astro Project:** - Run your build command locally: ``` azion build ``` 5. **Deploy Your Static Files:** - Deploy your static files using the Azion CLI: ``` azion deploy ``` This guide provides an overview of deploying static applications. ## Enabling Local Development Using Azion CLI For the preview to work, you must execute the following command: ``` azion dev ``` Once you've initialized the local development server, the application goes through the `build` process. ``` Building your Edge Application. This process may take a few minutes Running build step command: ... ``` Then, when the build is complete, the access to the application is prompted: ``` [Azion Bundler] [Server] › ✔ success Function running on port http://localhost:3000 ``` ## Troubleshooting ### Node.js runtime APIs A project using an NPM package fails to build with an error message such as `[Error] Could not resolve "XXXX. The package "XXXX" wasn't found on the file system but is built into node.`: This means that a package or import you are using is not compatible with Azion’s runtime APIs. If you are directly importing a Node.js runtime API, please refer to the [Azion Node.js compatibility](https://www.azion.com/en/documentation/products/azion-edge-runtime/compatibility/node/) for further steps on how to resolve this. If you are importing a package that imports a Node.js runtime API, check with the author of the package to see if they support the `node:*` import syntax. If they do not, you may need to find an alternative package. ## [Deploy your Astro Site with Buddy](https://docharvest.github.io/docs/astro/guides/deploy/buddy/) ## [Deploy your Astro Site with Cleavr](https://docharvest.github.io/docs/astro/guides/deploy/cleavr/) ## [Deploy your Astro Site to Clever Cloud](https://docharvest.github.io/docs/astro/guides/deploy/clever-cloud/) Contents astro Deploy your Astro Site to Clever Cloud Astro Deploy your Astro Site to Clever Cloud [Clever Cloud](https://clevercloud.com) is a European cloud platform that provides automated, scalable services. ## Project Configuration You can deploy both fully static and on-demand rendered Astro projects on Clever Cloud. Regardless of your `output` mode (pre-rendered or [on-demand](/en/guides/on-demand-rendering/)), you can choose to deploy as a **static application** which runs using a post-build hook, or as a **Node.js** application, which requires some manual configuration in your `package.json`. ### Scripts If you're running an on-demand Node.js application, update your `start` script to run the Node server. Applications on Clever Cloud listen on port **8080**. ``` "scripts": { "start": "node ./dist/server/entry.mjs --host 0.0.0.0 --port 8080", } ``` ## Deploy Astro from the Console To deploy your Astro project to Clever Cloud, you will need to **create a new application**. The application wizard will walk you through the necessary configuration steps. 1. From the lateral menubar, click **Create** > **An application** 2. Choose how to deploy: - **Create a brand new app**: to deploy from a local repository with Git or - **Select a GitHub repository**: to deploy from GitHub 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.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: \`\`\`shell CC\_NODE\_BUILD\_TOOL="npm" CC\_PRE\_BUILD\_HOOK="npm install && npm run astro telemetry disable && npm run build" \`\`\` \`\`\`shell CC\_NODE\_BUILD\_TOOL="custom" CC\_PRE\_BUILD\_HOOK="npm install -g pnpm && pnpm install" CC\_CUSTOM\_BUILD\_TOOL="pnpm run astro telemetry disable && pnpm build" \`\`\` \`\`\`shell CC\_NODE\_BUILD\_TOOL="yarn" CC\_PRE\_BUILD\_HOOK="yarn && yarn run astro telemetry disable && yarn build" \`\`\` - For a **static** application, add these variables: \`\`\`shell CC\_POST\_BUILD\_HOOK="npm run build" CC\_PRE\_BUILD\_HOOK="npm install && npm run astro telemetry disable" CC\_WEBROOT="/dist" \`\`\` \`\`\`shell CC\_POST\_BUILD\_HOOK="pnpm build" CC\_PRE\_BUILD\_HOOK="npm install -g pnpm && pnpm install && pnpm run astro telemetry disable" CC\_WEBROOT="/dist" \`\`\` \`\`\`shell CC\_POST\_BUILD\_HOOK="yarn build" CC\_PRE\_BUILD\_HOOK="yarn && yarn run astro telemetry disable" CC\_WEBROOT="/dist" \`\`\` 8. **Deploy!** If you're deploying from **GitHub**, your deployment should start automatically. If you're using **Git**, copy the remote and push on the **master** branch. :::tip\[Other Branches\] To deploy from branches other than `master`, use `git push clever :master`. For example, if you want to deploy your local `main` branch without renaming it, use `git push clever main:master`. ::: ## Official Resources - [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/) ## [Deploy your Astro Site to Cloudflare](https://docharvest.github.io/docs/astro/guides/deploy/cloudflare/) ## [Deploy your Astro Site with CloudRay](https://docharvest.github.io/docs/astro/guides/deploy/cloudray/) ## [Deploy your Astro Site with Deno](https://docharvest.github.io/docs/astro/guides/deploy/deno/) Contents astro Deploy your Astro Site with Deno Astro Deploy your Astro Site with Deno ```` import deno from '@deno/astro-adapter'; export default defineConfig({ output: 'server', adapter: deno(), }); ``` ```` 3. Update your `preview` script in `package.json` with the change below. ``` // package.json { "scripts": { "dev": "astro dev", "start": "astro dev", "build": "astro build", "preview": "astro preview" "preview": "deno run --allow-net --allow-read --allow-env ./dist/server/entry.mjs" } } ``` You can now use this command to preview your production Astro site locally with Deno. \`\`\`shell npm run preview \`\`\` \`\`\`shell pnpm run preview \`\`\` \`\`\`shell yarn run preview \`\`\` ## How to deploy You can run your Astro site on your own server, or deploy to Deno Deploy through GitHub Actions or using Deno Deploy’s CLI (command line interface). ### On your own server 1\. Copy your project onto your server. 2. Install the project dependencies using your preferred package manager: \`\`\`shell npm install \`\`\` \`\`\`shell pnpm install \`\`\` \`\`\`shell yarn install \`\`\` 3. Build your Astro site with your preferred package manager: \`\`\`shell npm run build \`\`\` \`\`\`shell pnpm run build \`\`\` \`\`\`shell yarn run build \`\`\` 4. Start your application with the following command: \`\`\`bash deno run -A jsr:@std/http/file-server dist \`\`\` \`\`\`bash deno run -A ./dist/server/entry.mjs \`\`\` ### GitHub Actions Deployment If your project is stored on GitHub, the [Deno Deploy website](https://dash.deno.com/) will guide you through setting up GitHub Actions to deploy your Astro site. 1\. Push your code to a public or private GitHub repository. 2. Sign in on [Deno Deploy](https://dash.deno.com/) with your GitHub account, and click on [New Project](https://dash.deno.com). 3. Select your repository, the branch you want to deploy from, and select **GitHub Action** mode. (Your Astro site requires a build step, and cannot use Automatic mode.) 4. In your Astro project, create a new file at `.github/workflows/deploy.yml` and paste in the YAML below. This is similar to the YAML given by Deno Deploy, with the additional steps needed for your Astro site. \`\`\`yaml --- // .github/workflows/deploy.yml --- name: Deploy on: \[push\] ``` jobs: deploy: name: Deploy runs-on: ubuntu-latest permissions: id-token: write # Needed for auth with Deno Deploy contents: read # Needed to clone the repository steps: - name: Clone repository uses: actions/checkout@v6 # Not using npm? Change `npm ci` to `pnpm i` or `yarn install` - name: Install dependencies run: npm ci # Not using npm? Change `npm run build` to `pnpm run build` or `yarn run build` - name: Build Astro run: npm run build - name: Upload to Deno Deploy uses: denoland/deployctl@v1 with: project: my-deno-project # TODO: replace with Deno Deploy project name entrypoint: jsr:@std/http/file-server root: dist ``` ``` \`\`\`yaml --- // .github/workflows/deploy.yml --- name: Deploy on: \[push\] ``` jobs: deploy: name: Deploy runs-on: ubuntu-latest permissions: id-token: write # Needed for auth with Deno Deploy contents: read # Needed to clone the repository steps: - name: Clone repository uses: actions/checkout@v6 # Not using npm? Change `npm ci` to `pnpm i` or `yarn install` - name: Install dependencies run: npm ci # Not using npm? Change `npm run build` to `pnpm run build` or `yarn run build` - name: Build Astro run: npm run build - name: Upload to Deno Deploy uses: denoland/deployctl@v1 with: project: my-deno-project # TODO: replace with Deno Deploy project name entrypoint: dist/server/entry.mjs ``` ``` 5. After committing this YAML file, and pushing to GitHub on your configured deploy branch, the deploy should begin automatically! You can track the progress using the "Actions" tab on your GitHub repository page, or on [Deno Deploy](https://dash.deno.com). ### CLI Deployment 1\. Install the \[Deno Deploy CLI\](https://docs.deno.com/deploy/manual/deployctl). ```` ```bash deno install -gArf jsr:@deno/deployctl ``` ```` 2. Build your Astro site with your preferred package manager: \`\`\`shell npm run build \`\`\` \`\`\`shell pnpm run build \`\`\` \`\`\`shell yarn run build \`\`\` 3. Run `deployctl` to deploy! \`\`\`bash cd dist && deployctl deploy jsr:@std/http/file-server \`\`\` \`\`\`bash deployctl deploy ./dist/server/entry.mjs \`\`\` You can track all your deploys on [Deno Deploy](https://dash.deno.com). 4. (Optional) To simplify the build and deploy into one command, add a `deploy-deno` script in `package.json`, adapting it for your preferred package manager. \`\`\`json ins={8} // package.json { "scripts": { "dev": "astro dev", "start": "astro dev", "build": "astro build", "preview": "astro preview", "deno-deploy": "npm run build && cd dist && deployctl deploy jsr:@std/http/file-server" } } \`\`\` \`\`\`json ins={8} // package.json { "scripts": { "dev": "astro dev", "start": "astro dev", "build": "astro build", "preview": "deno run --allow-net --allow-read --allow-env ./dist/server/entry.mjs", "deno-deploy": "npm run build && deployctl deploy ./dist/server/entry.mjs" } } \`\`\` Then you can use this command to build and deploy your Astro site in one step. \`\`\`shell npm run deno-deploy \`\`\` \`\`\`shell pnpm run deno-deploy \`\`\` \`\`\`shell yarn run deno-deploy \`\`\` ## [Deploy your Astro Site with DeployHQ](https://docharvest.github.io/docs/astro/guides/deploy/deployhq/) ## [Deploy your Astro Site to EdgeOne Pages](https://docharvest.github.io/docs/astro/guides/deploy/edgeone-pages/) Contents astro Deploy your Astro Site to EdgeOne Pages Astro Deploy your Astro Site to EdgeOne Pages You can deploy your Astro project on [EdgeOne Pages](https://pages.edgeone.ai/), a fullstack development platform with a global edge network that can build your site from a Git connection or from the EdgeOne CLI. ## Official Resources - [EdgeOne Astro Framework Guide](https://pages.edgeone.ai/document/framework-astro) - [EdgeOne Astro template](https://github.com/TencentEdgeOne/astro-template) ## [Deploy your Astro Site to Google’s Firebase Hosting](https://docharvest.github.io/docs/astro/guides/deploy/firebase/) Contents astro Deploy your Astro Site to Google’s Firebase Hosting Astro Deploy your Astro Site to Google’s Firebase Hosting [Firebase Hosting](https://firebase.google.com/products/hosting) is a service provided by Google’s [Firebase](https://firebase.google.com/) app development platform, which can be used to deploy an Astro site. See our separate guide for [adding Firebase backend services](/en/guides/backend/firebase/) such as databases, authentication, and storage. ## Project Configuration Your Astro project can be deployed to Firebase as a static site, or as a server-side rendered site (SSR). ### Static Site Your Astro project is a static site by default. You don’t need any extra configuration to deploy a static Astro site to Firebase. ### Adapter for SSR To enable SSR in your Astro project and deploy on Firebase add the [Node.js adapter](/en/guides/integrations-guide/node/). :::note Deploying an SSR Astro site to Firebase requires the [Blaze plan](https://firebase.google.com/pricing) or higher. ::: ## How to deploy 1\. Install the \[Firebase CLI\](https://github.com/firebase/firebase-tools). This is a command-line tool that allows you to interact with Firebase from the terminal. ``` ```shell npm install firebase-tools ``` ```shell pnpm add firebase-tools ``` ```shell yarn add firebase-tools ``` ``` 2. Authenticate the Firebase CLI with your Google account. This will open a browser window where you can log in to your Google account. \`\`\`shell npx firebase login \`\`\` \`\`\`shell pnpm exec firebase login \`\`\` \`\`\`shell yarn firebase login \`\`\` 3. Enable experimental web frameworks support. This is an experimental feature that allows the Firebase CLI to detect and configure your deployment settings for Astro. \`\`\`shell npx firebase experiments:enable webframeworks \`\`\` \`\`\`shell pnpm exec firebase experiments:enable webframeworks \`\`\` \`\`\`shell yarn firebase experiments:enable webframeworks \`\`\` 4. Initialize Firebase Hosting in your project. This will create a `firebase.json` and `.firebaserc` file in your project root. \`\`\`shell npx firebase init hosting \`\`\` \`\`\`shell pnpm exec firebase init hosting \`\`\` \`\`\`shell yarn firebase init hosting \`\`\` 5. Deploy your site to Firebase Hosting. This will build your Astro site and deploy it to Firebase. \`\`\`shell npx firebase deploy --only hosting \`\`\` \`\`\`shell pnpm exec firebase deploy --only hosting \`\`\` \`\`\`shell yarn firebase deploy --only hosting \`\`\` ## [Deploy your Astro Site to Fleek](https://docharvest.github.io/docs/astro/guides/deploy/fleek/) ## [Deploy your Astro Site to Fly.io](https://docharvest.github.io/docs/astro/guides/deploy/flyio/) Contents astro Deploy your Astro Site to Fly.io Astro Deploy your Astro Site to Fly.io You can deploy your Astro project to [Fly.io](https://fly.io/), a platform for running full stack apps and databases close to your users. ## Project Configuration Your Astro project can be deployed to Fly.io as a static site, or as a server-side rendered site (SSR). ### Static Site Your Astro project is a static site by default. You don’t need any extra configuration to deploy a static Astro site to Fly.io. ### Adapter for SSR To enable on-demand rendering in your Astro project and deploy on Fly.io, add [the Node.js adapter](/en/guides/integrations-guide/node/). ## How to deploy 1\. \[Sign up for Fly.io\](https://fly.io/docs/getting-started/log-in-to-fly/#first-time-or-no-fly-account-sign-up-for-fly) if you haven't already. 2. [Install `flyctl`](https://fly.io/docs/hands-on/install-flyctl/), your Fly.io app command center. 3. Run the following command in your terminal. ``` fly launch ``` `flyctl` will automatically detect Astro, configure the correct settings, build your image, and deploy it to the Fly.io platform. ## Generating your Astro Dockerfile If you don't already have a Dockerfile, `fly launch` will generate one for you, as well as prepare a `fly.toml` file. For pages rendered on demand, this Dockerfile will include the appropriate start command and environment variables. You can instead create your own Dockerfile using [Dockerfile generator](https://www.npmjs.com/package/@flydotio/dockerfile) and then run using the command `npx dockerfile` for Node applications or `bunx dockerfile` for Bun applications. ## Official Resources - Check out [the official Fly.io docs](https://fly.io/docs/js/frameworks/astro/) ## [Deploy your Astro Site to GitHub Pages](https://docharvest.github.io/docs/astro/guides/deploy/github/) Contents astro Deploy your Astro Site to GitHub Pages Astro Deploy your Astro Site to GitHub Pages You can use [GitHub Pages](https://pages.github.com/) to host a static, prerendered Astro website directly from a repository on [GitHub.com](https://github.com/) using [GitHub Actions](https://github.com/features/actions). ## How to deploy Astro maintains an [official Astro GitHub Action to deploy your project to a GitHub Pages](https://github.com/withastro/action) with very little configuration and is the recommended way to deploy to GitHub Pages. Follow the instructions below to use the GitHub Action to deploy your Astro site to GitHub Pages. This will create a website from your repository at a GitHub URL (e.g. `https://.github.io/`). Once deployed, you can optionally [configure a custom domain](#change-your-github-url-to-a-custom-domain) to deploy your GitHub Pages site at your preferred domain (e.g. `https://example.com`). 1\. Create a new file in your project at \`.github/workflows/deploy.yml\` and paste in the YAML below. ```` ```yaml title="deploy.yml" name: Deploy to GitHub Pages on: # Trigger the workflow every time you push to the `main` branch # Using a different branch name? Replace `main` with your branch’s name push: branches: [ main ] # Allows you to run this workflow manually from the Actions tab on GitHub. workflow_dispatch: # Allow this job to clone the repo and create a page deployment permissions: contents: read pages: write id-token: write jobs: build: runs-on: ubuntu-latest steps: - name: Checkout your repository using git uses: actions/checkout@v6 - name: Install, build, and upload your site uses: withastro/action@v6 # with: # path: . # The root location of your Astro project inside the repository. (optional) # node-version: 24 # The specific version of Node that should be used to build your site. Defaults to 24. (optional) # package-manager: pnpm@latest # The Node package manager that should be used to install dependencies and build your site. Automatically detected based on your lockfile. (optional) # build-cmd: pnpm run build # The command to run to build your site. Runs the package build script/task by default. (optional) # env: # PUBLIC_POKEAPI: 'https://pokeapi.co/api/v2' # Use single quotation marks for the variable value. (optional) deploy: needs: build runs-on: ubuntu-latest environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v5 ``` The Astro action can be configured with optional inputs. Provide these by uncommenting the `with:` line and the input you want to use. If your site requires any public environment variables, uncomment the `env:` line and add them there. (See the [GitHub documentation on setting secrets](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#creating-configuration-variables-for-a-repository) for adding private environment variables.) :::caution The official Astro [action](https://github.com/withastro/action) scans for a lockfile to detect your preferred package manager (`npm`, `yarn`, `pnpm`, or `bun`). You should commit your package manager's automatically generated `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, or `bun.lockb` file to your repository. ::: ```` 2. In your Astro config file, set [`site`](/en/reference/configuration-reference/#site) to the GitHub URL of your deployed site. ``` import { defineConfig } from 'astro/config' export default defineConfig({ site: 'https://astronaut.github.io', }) ``` The value for `site` must be one of the following: - The following URL based on your username: `https://.github.io` - The random URL autogenerated for a [GitHub Organization's private page](https://docs.github.com/en/enterprise-cloud@latest/pages/getting-started-with-github-pages/changing-the-visibility-of-your-github-pages-site): `https://.pages.github.io/` 3. In `astro.config.mjs`, configure a value for [`base`](/en/reference/configuration-reference/#base) (usually required). GitHub Pages will publish your website at an address that depends on both your username and your repository name (e.g. `https://.github.io//`). Set a value for `base` that specifies the repository for your website. This is so that Astro understands your website's root is `/my-repo`, rather than the default `/`. You can skip this if your repository name matches the special `.github.io` pattern (e.g. `https://github.com/username/username.github.io/`) Configure `base` as the repository’s name starting with a forward slash ( e.g. `/my-repo`): ``` import { defineConfig } from 'astro/config' export default defineConfig({ site: 'https://astronaut.github.io', base: '/my-repo', }) ``` :::caution\[Internal links with `base` configured\] When this value is configured, all of your internal page links must be prefixed with your `base` value: ``` About ``` See more about [configuring a `base` value](/en/reference/configuration-reference/#base). ::: 4. On GitHub, go to your repository’s **Settings** tab and find the **Pages** section of the settings. 5. Choose **GitHub Actions** as the **Source** of your site. When you push changes to your Astro project’s repository, the GitHub Action will automatically deploy them for you at your GitHub URL. ## Change your GitHub URL to a custom domain Once your Astro project is [deployed to GitHub pages at a GitHub URL](#how-to-deploy) following the previous instructions, you can configure a custom domain. This means that users can visit your site at your custom domain `https://example.com` instead of `https://.github.io`. 1. [Configure DNS for your domain provider](https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain). 2. Add a `./public/CNAME` record to your project. Create the following file in your `public/` folder with a single line of text that specifies your custom domain: ``` sub.example.com ``` This will deploy your site at your custom domain instead of `user.github.io`. 3. In your Astro config, update the value for `site` with your custom domain. Do not set a value for `base`, and remove one if it exists: ``` import { defineConfig } from 'astro/config' export default defineConfig({ site: 'https://example.com', base: '/my-repo' }) ``` 4. If necessary, update all your page internal links to remove the `base` prefix: ``` About ``` ## Examples - [Github Pages Deployment starter template](https://github.com/hkbertoson/github-pages) - [Starlight Flexoki Theme (production site)](https://delucis.github.io/starlight-theme-flexoki/) - [Expressive Code Color Chips (production site)](https://delucis.github.io/expressive-code-color-chips/) - [Starlight Markdown Blocks (production site)](https://delucis.github.io/starlight-markdown-blocks/) ## [Deploy your Astro Site to GitLab Pages](https://docharvest.github.io/docs/astro/guides/deploy/gitlab/) Contents astro Deploy your Astro Site to GitLab Pages Astro Deploy your Astro Site to GitLab Pages You can use [GitLab Pages](https://docs.gitlab.com/ee/user/project/pages/) to host an Astro site for your [GitLab](https://about.gitlab.com/) projects, groups, or user account. :::tip\[Looking for an example?\] Check out [the official GitLab Pages Astro example project](https://gitlab.com/pages/astro)! ::: ## How to deploy You can deploy an Astro site to GitLab Pages by using GitLab CI/CD to automatically build and deploy your site. To do this, your source code must be hosted on GitLab and you need to make the following changes to your Astro project: 1. Set up [`site`](/en/reference/configuration-reference/#site) and [`base`](/en/reference/configuration-reference/#base) options in `astro.config.mjs`. ``` import { defineConfig } from 'astro/config'; export default defineConfig({ site: 'https://.gitlab.io', base: '/', outDir: 'public', publicDir: 'static', }); ``` `site` The value for `site` must be one of the following: - The following URL based on your username: `https://.gitlab.io` - The following URL based on your group name: `https://.gitlab.io` - Your custom domain if you have it configured in your GitLab project’s settings: `https://example.com` For GitLab self-managed instances, replace `gitlab.io` with your instance’s Pages domain. `base` A value for `base` may be required so that Astro will treat your repository name (e.g. `/my-repo`) as the root of your website. :::note Don't set a `base` parameter if your page is served from the root folder. ::: The value for `base` should be your repository’s name starting with a forward slash, for example `/my-blog`. This is so that Astro understands your website's root is `/my-repo`, rather than the default `/`. :::caution When this value is configured, all of your internal page links must be prefixed with your `base` value: ``` About ``` See more about [configuring a `base` value](/en/reference/configuration-reference/#base) ::: 2. Rename the `public/` directory to `static/`. 3. Set `outDir: 'public'` in `astro.config.mjs`. This setting instructs Astro to put the static build output in a folder called `public`, which is the folder required by GitLab Pages for exposed files. If you were using the [`public/` directory](/en/basics/project-structure/#public) as a source of static files in your Astro project, rename it and use that new folder name in `astro.config.mjs` for the value of `publicDir`. For example, here are the correct `astro.config.mjs` settings when the `public/` directory is renamed to `static/`: ``` import { defineConfig } from 'astro/config'; export default defineConfig({ outDir: 'public', publicDir: 'static', }); ``` 4. Change the build output in `.gitignore`. In our example we need to change `dist/` to `public/`: ``` # build output -dist/ +public/ ``` 5. Create a file called `.gitlab-ci.yml` in the root of your project with the content below. This will build and deploy your site whenever you make changes to your content: ``` pages: # The Docker image that will be used to build your app image: node:lts before_script: - npm ci script: # Specify the steps involved to build your app here - npm run build artifacts: paths: # The folder that contains the built files to be published. # This must be called "public". - public only: # Trigger a new build and deploy only when there is a push to the # branch(es) below - main ``` 6. Commit your changes and push them to GitLab. 7. On GitLab, go to your repository’s **Deploy** menu and select **Pages**. Here you will see the full URL of your GitLab Pages website. To make sure you are using the URL format `https://username.gitlab.io/my-repo`, uncheck the **Use unique domain** setting on this page. Your site should now be published! When you push changes to your Astro project’s repository, the GitLab CI/CD pipeline will automatically deploy them for you. ## [Deploy your Astro Site to Google Cloud](https://docharvest.github.io/docs/astro/guides/deploy/google-cloud/) ## [Deploy your Astro Site to Heroku](https://docharvest.github.io/docs/astro/guides/deploy/heroku/) Contents astro Deploy your Astro Site to Heroku Astro Deploy your Astro Site to Heroku [Heroku](https://www.heroku.com/) is a platform-as-a-service for building, running, and managing modern apps in the cloud. You can deploy an Astro site to Heroku using this guide. :::caution The following instructions use [the deprecated `heroku-static-buildpack`](https://github.com/heroku/heroku-buildpack-static#warning-heroku-buildpack-static-is-deprecated). Please see [Heroku's documentation for using `heroku-buildpack-nginx`](https://github.com/dokku/heroku-buildpack-nginx) instead. ::: ## How to deploy 1\. Install the \[Heroku CLI\](https://devcenter.heroku.com/articles/heroku-cli). 2. Create a Heroku account by [signing up](https://signup.heroku.com/). 3. Run `heroku login` and fill in your Heroku credentials: ``` $ heroku login ``` 4. Create a file called `static.json` in the root of your project with the below content: ``` { "root": "./dist" } ``` This is the configuration of your site; read more at [heroku-buildpack-static](https://github.com/heroku/heroku-buildpack-static). 5. Set up your Heroku git remote: ``` # version change $ git init $ git add . $ git commit -m "My site ready for deployment." # creates a new app with a specified name $ heroku apps:create example # set buildpack for static sites $ heroku buildpacks:set https://github.com/heroku/heroku-buildpack-static.git ``` 6. Deploy your site: ``` # publish site $ git push heroku master # opens a browser to view the Dashboard version of Heroku CI $ heroku open ``` ## [Deploy your Astro Site to Hostinger](https://docharvest.github.io/docs/astro/guides/deploy/hostinger/) ## [Deploy your Astro Site to Microsoft Azure](https://docharvest.github.io/docs/astro/guides/deploy/microsoft-azure/) ## [Deploy your Astro Site to Netlify](https://docharvest.github.io/docs/astro/guides/deploy/netlify/) Contents astro Deploy your Astro Site to Netlify Astro Deploy your Astro Site to Netlify [Netlify](https://netlify.com) offers hosting and serverless backend services for web applications and static websites. Any Astro site can be hosted on Netlify! This guide includes instructions for deploying to Netlify through the website UI or Netlify's CLI. ## Project configuration Your Astro project can be deployed to Netlify in three different ways: as a static site, a server-rendered site, or an edge-rendered site. ### Static site Your Astro project is a static site by default. You don’t need any extra configuration to deploy a static Astro site to Netlify. ### Adapter for on-demand rendering Add [the Netlify adapter](/en/guides/integrations-guide/netlify/) to enable on-demand rendering in your Astro project and deploy to Netlify with the following `astro add` command. This will install the adapter and make the appropriate changes to your `astro.config.mjs` file in one step. ``` npx astro add netlify ``` See the [Netlify adapter guide](/en/guides/integrations-guide/netlify/) to install manually instead, or for more configuration options, such as deploying your project’s Astro middleware using Netlify’s Edge Functions. ## How to deploy You can deploy to Netlify through the website UI or using Netlify’s CLI (command line interface). The process is the same for both static and on-demand rendered Astro sites. ### Website UI deployment If your project is stored in GitHub, GitLab, BitBucket, or Azure DevOps, you can use the Netlify website UI to deploy your Astro site. 1\. Click Add a new site in your \[Netlify dashboard\](https://app.netlify.com/) 2. Choose Import an existing project When you import your Astro repository from your Git provider, Netlify should automatically detect and pre-fill the correct configuration settings for you. 3. Make sure that the following settings are entered, then press the Deploy button: - **Build Command:** `astro build` or `npm run build` - **Publish directory:** `dist` After deploying, you will be redirected to the site overview page. There, you can edit the details of your site. Any future changes to your source repository will trigger preview and production deploys based on your deployment configuration. #### `netlify.toml` file You can optionally create a new `netlify.toml` file at the top level of your project repository to configure your build command and publish directory, as well as other project settings including environment variables and redirects. Netlify will read this file and automatically configure your deployment. To configure the default settings, create a `netlify.toml` file with the following contents: ``` [build] command = "npm run build" publish = "dist" ``` More info at [“Deploying an existing Astro Git repository”](https://www.netlify.com/blog/how-to-deploy-astro/#deploy-an-existing-git-repository-to-netlify) on Netlify’s blog ### CLI deployment You can also create a new site on Netlify and link up your Git repository by installing and using the [Netlify CLI](https://cli.netlify.com/). 1\. Install Netlify's CLI globally ```` ```bash npm install --global netlify-cli ``` ```` 2. Run `netlify login` and follow the instructions to log in and authorize Netlify 3. Run `netlify init` and follow the instructions 4. Confirm your build command (`astro build`) The CLI will automatically detect the build settings (`astro build`) and deploy directory (`dist`), and will offer to automatically generate [a `netlify.toml` file](#netlifytoml-file) with those settings. 5. Build and deploy by pushing to Git The CLI will add a deploy key to the repository, which means your site will be automatically rebuilt on Netlify every time you `git push`. More details from Netlify on [Deploy an Astro site using the Netlify CLI](https://www.netlify.com/blog/how-to-deploy-astro/#link-your-astro-project-and-deploy-using-the-netlify-cli) ### 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 `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. - a `NODE_VERSION` environment variable in your site's settings using the Netlify project dashboard. ## Using Netlify Functions No special configuration is required to use Netlify Functions with Astro. Add a `netlify/functions` directory to your project root and follow [the Netlify Functions documentation](https://docs.netlify.com/functions/overview/) to get started! ## Examples - [Deploy An Astro site with Forms, Serverless Functions, and Redirects](https://www.netlify.com/blog/deploy-an-astro-site-with-forms-serverless-functions-and-redirects/) — Netlify Blog - [Deployment Walkthrough Video](https://youtu.be/GrSLYq6ZTes) — Netlify YouTube channel ## [Deploy your Astro Site with Railway](https://docharvest.github.io/docs/astro/guides/deploy/railway/) ## [Deploy your Astro Site to Render](https://docharvest.github.io/docs/astro/guides/deploy/render/) ## [Deploy your Astro Site to Seenode](https://docharvest.github.io/docs/astro/guides/deploy/seenode/) ## [Deploy your Astro Site to Sevalla](https://docharvest.github.io/docs/astro/guides/deploy/sevalla/) ## [Deploy your Astro Site to Stormkit](https://docharvest.github.io/docs/astro/guides/deploy/stormkit/) ## [Deploy your Astro Site to Surge](https://docharvest.github.io/docs/astro/guides/deploy/surge/) Contents astro Deploy your Astro Site to Surge Astro Deploy your Astro Site to Surge You can deploy your Astro project to [Surge](https://surge.sh/), a single-command web publishing platform designed for front-end developers. ## How to deploy 1\. Install \[the Surge CLI\](https://www.npmjs.com/package/surge) globally from the terminal, if you haven't already. ```` ```shell npm install -g surge ``` ```` 2. Build your Astro site from your project’s root directory. ``` npm run build ``` 3. Deploy to Surge using the CLI. ``` surge dist ``` You can [use a custom domain with Surge](http://surge.sh/help/adding-a-custom-domain) when deploying by running `surge dist yourdomain.com`. ## [Deploy your Astro Site to Vercel](https://docharvest.github.io/docs/astro/guides/deploy/vercel/) ## [Deploy your Astro Site to Zeabur](https://docharvest.github.io/docs/astro/guides/deploy/zeabur/) ## [Deploy your Astro Site to Zephyr Cloud](https://docharvest.github.io/docs/astro/guides/deploy/zephyr/) ## [Deploy your Astro Site to Zerops](https://docharvest.github.io/docs/astro/guides/deploy/zerops/) Contents astro Deploy your Astro Site to Zerops Astro Deploy your Astro Site to Zerops [Zerops](https://zerops.io/) is a dev-first cloud platform that can be used to deploy both Static and SSR Astro site. This guide will walk you through setting up and deploying both Static and SSR Astro sites on Zerops. :::tip\[Astro x Zerops Quickrun\] Want to test running Astro on Zerops without installing or setting up anything? Using repositories [Zerops x Astro - Static](https://github.com/zeropsio/recipe-astro-static) or [Zerops x Astro - SSR on Node.js](https://github.com/zeropsio/recipe-astro-nodejs) you can deploy example Astro site with a single click. ::: Running apps on Zerops requires two steps: 1. Creating a project 2. Triggering build & deploy pipeline :::note One Zerops project can contain multiple Astro sites. ::: ## Astro Static site on Zerops ### Creating a project and a service for Astro Static Projects and services can be added either through a [`Project add`](https://app.zerops.io/dashboard/project-add) wizard or imported using a yaml structure: ``` # see https://docs.zerops.io/references/import for full reference project: name: recipe-astro services: - hostname: app type: static ``` This will create a project called `recipe-astro` with a Zerops Static service called `app`. ### Deploying your Astro Static site To tell Zerops how to build and run your site, add a `zerops.yml` to your repository: \`\`\`yaml title="zerops.yml" # see https://docs.zerops.io/zerops-yml/specification for full reference zerops: - setup: app build: base: nodejs@20 buildCommands: - npm i - npm build deployFiles: - dist/~ run: base: static \`\`\` \`\`\`yaml title="zerops.yml" # see https://docs.zerops.io/zerops-yml/specification for full reference zerops: - setup: app build: base: nodejs@20 buildCommands: - pnpm i - pnpm build deployFiles: - dist/~ run: base: static \`\`\` \`\`\`yaml title="zerops.yml" # see https://docs.zerops.io/zerops-yml/specification for full reference zerops: - setup: app build: base: nodejs@20 buildCommands: - yarn - yarn build deployFiles: - dist/~ run: base: static \`\`\` Now you can [trigger the build & deploy pipeline using the Zerops CLI](#trigger-the-pipeline-using-zerops-cli-zcli) or by connecting the `app` service with your [GitHub](https://docs.zerops.io/references/github-integration/) / [GitLab](https://docs.zerops.io/references/gitlab-integration) repository from inside the service detail. ## Astro SSR site on Zerops ### Update scripts Update your `start` script to run the server output from the Node adapter. ``` "scripts": { "start": "node ./dist/server/entry.mjs", } ``` ### Creating a project and a service for Astro SSR (Node.js) Projects and services can be added either through a [`Project add`](https://app.zerops.io/dashboard/project-add) wizard or imported using a yaml structure: ``` # see https://docs.zerops.io/references/import for full reference project: name: recipe-astro services: - hostname: app type: nodejs@20 ``` This will create a project called `recipe-astro` with Zerops Node.js service called `app`. ### Deploying your Astro SSR site To tell Zerops how to build and run your site using the official [Astro Node.js adapter](/en/guides/integrations-guide/node/) in `standalone` mode, add a `zerops.yml` file to your repository: \`\`\`yaml title="zerops.yml" # see https://docs.zerops.io/zerops-yml/specification for full reference zerops: - setup: app build: base: nodejs@20 buildCommands: - npm i - npm run build deployFiles: - dist - package.json - node\_modules run: base: nodejs@20 ports: - port: 3000 httpSupport: true envVariables: PORT: 3000 HOST: 0.0.0.0 start: npm start \`\`\` \`\`\`yaml title="zerops.yml" # see https://docs.zerops.io/zerops-yml/specification for full reference zerops: - setup: app build: base: nodejs@20 buildCommands: - pnpm i - pnpm run build deployFiles: - dist - package.json - node\_modules run: base: nodejs@20 ports: - port: 3000 httpSupport: true envVariables: PORT: 3000 HOST: 0.0.0.0 start: pnpm start \`\`\` \`\`\`yaml title="zerops.yml" # see https://docs.zerops.io/zerops-yml/specification for full reference zerops: - setup: app build: base: nodejs@20 buildCommands: - yarn - yarn build deployFiles: - dist - package.json - node\_modules run: base: nodejs@20 ports: - port: 3000 httpSupport: true envVariables: PORT: 3000 HOST: 0.0.0.0 start: yarn start \`\`\` Now you can [trigger the build & deploy pipeline using the Zerops CLI](#trigger-the-pipeline-using-zerops-cli-zcli) or by connecting the `app` service with your [GitHub](https://docs.zerops.io/references/github-integration/) / [GitLab](https://docs.zerops.io/references/gitlab-integration) repository from inside the service detail. ## Trigger the pipeline using Zerops CLI (zcli) 1\. Install the Zerops CLI. \`\`\`shell # To download the zcli binary directly, # use https://github.com/zeropsio/zcli/releases npm i -g @zerops/zcli \`\`\` 2. Open [`Settings > Access Token Management`](https://app.zerops.io/settings/token-management) in the Zerops app and generate a new access token. 3. Log in using your access token with the following command: `shell zcli login ` 4. Navigate to the root of your app (where `zerops.yml` is located) and run the following command to trigger the deploy: `shell zcli push` ## Resources ### Official - [Create Zerops account](https://app.zerops.io/registration) - [Zerops Documentation](https://docs.zerops.io) - [Zerops Astro recipe](https://app.zerops.io/recipe/astro) ### Community - [Deploying Astro to Zerops in 3 mins](https://medium.com/@arjunaditya/how-to-deploy-astro-to-zerops-4230816a62b4) - [Deploying Astro SSG with Node.js on Zerops with One Click Deploy](https://youtu.be/-4KTa4VWtBE) - [Deploying Astro SSR with Node.js on Zerops with One Click Deploy](https://youtu.be/eR6b_JnDH6g) ## [Dev toolbar](https://docharvest.github.io/docs/astro/guides/dev-toolbar/) Contents astro Dev toolbar Astro Dev toolbar While the dev server is running, Astro includes a dev toolbar at the bottom of every page in your local browser preview. This toolbar includes a number of useful tools for debugging and inspecting your site during development and can be [extended with more dev toolbar apps](#extending-the-dev-toolbar) found in the integrations directory. You can even [build your own toolbar apps](/en/recipes/making-toolbar-apps/) using the [Dev Toolbar API](/en/reference/dev-toolbar-app-reference/)! This toolbar is enabled by default and appears when you hover over the bottom of the page. It is a development tool only and will not appear on your published site. ## Built-in apps ### Astro Menu The Astro Menu app provides easy access to various information about the current project and links to extra resources. Notably, it provides one-click access to the Astro documentation, GitHub repository, and Discord server. This app also includes a "Copy debug info" button which will run the [`astro info`](/en/reference/cli-reference/#astro-info) command and copy the output to your clipboard. This can be useful when asking for help or reporting issues. ### Inspect The Inspect app provides information about any [islands](/en/concepts/islands/) on the current page. This will show you the properties passed to each island, and the client directive that is being used to render them. ### Audit The Audit app automatically runs a series of audits on the current page, checking for the most common performance and accessibility issues. When an issue is found, a red dot will appear in the toolbar. Clicking on the app will pop up a list of results from the audit and will highlight the related elements directly in the page. :::note The basic performance and accessibility audits performed by the dev toolbar are not a replacement for dedicated tools like [Pa11y](https://pa11y.org/) or [Lighthouse](https://developers.google.com/web/tools/lighthouse), or even better, humans! The dev toolbar aims to provide a quick and easy way to catch common issues during development, without needing to context-switch to a different tool. ::: ### Settings The Settings app allows you to configure options for the dev toolbar, such as verbose logging, disabling notifications, and adjusting its placement on your screen. ## Extending the dev toolbar Astro integrations can add new apps to the dev toolbar, allowing you to extend it with custom tools that are specific to your project. You can find [more dev tool apps to install in the integrations directory](https://astro.build/integrations/?search=&categories%5B%5D=toolbar) or using the [Astro Menu](#astro-menu). Install additional dev toolbar app integrations in your project just like any other [Astro integration](/en/guides/integrations/) according to its own installation instructions. ## Disabling the dev toolbar The dev toolbar is enabled by default for every site. You can choose to disable it for individual projects and/or users as needed. ### Per-project To disable the dev toolbar for everyone working on a project, set `devToolbar: false` in the [Astro config file](/en/reference/configuration-reference/#devtoolbarenabled). To enable the dev toolbar again, remove these lines from your configuration, or set `enabled: true`. ### Per-user To disable the dev toolbar for yourself on a specific project, run the [`astro preferences`](/en/reference/cli-reference/#astro-preferences) command. ``` astro preferences disable devToolbar ``` To disable the dev toolbar in all Astro projects for a user on the current machine, add the `--global` flag when running `astro-preferences`: ``` astro preferences disable --global devToolbar ``` The dev toolbar can later be enabled with: ``` astro preferences enable devToolbar ``` ## [E-commerce](https://docharvest.github.io/docs/astro/guides/ecommerce/) ## [Endpoints](https://docharvest.github.io/docs/astro/guides/endpoints/) Contents astro Endpoints Astro Endpoints Astro lets you create custom endpoints to serve any kind of data. You can use this to generate images, expose an RSS document, or use them as API Routes to build a full API for your site. In statically-generated sites, your custom endpoints are called at build time to produce static files. If you opt in to [SSR](/en/guides/on-demand-rendering/) mode, custom endpoints turn into live server endpoints that are called on request. Static and SSR endpoints are defined similarly, but SSR endpoints support additional features. ## Static File Endpoints To create a custom endpoint, add a `.js` or `.ts` file to the `/pages` directory. The `.js` or `.ts` extension will be removed during the build process, so the name of the file should include the extension of the data you want to create. For example, `src/pages/data.json.ts` will build a `/data.json` endpoint. Endpoints export a `GET` function (optionally `async`) that receives a [context object](/en/reference/api-reference/) with properties similar to the `Astro` global. Here, it returns a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object with a `name` and `url`, and Astro will call this at build time and use the contents of the body to generate the file. ``` // Outputs: /builtwith.json } ``` Since Astro v3.0, the returned `Response` object doesn't have to include the `encoding` property anymore. For example, to produce a binary `.png` image: ``` return new Response(await response.arrayBuffer()); } ``` You can also get type safety in your endpoint functions using the `APIRoute` type with the `satisfies` operator: 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: ``` const usernames = ["Sarah", "Chris", "Yan", "Elian"]; return new Response( JSON.stringify({ name: usernames[id], }), ); }) satisfies APIRoute; } ``` This will generate four JSON endpoints at build time: `/api/0.json`, `/api/1.json`, `/api/2.json` and `/api/3.json`. Dynamic routing with endpoints works the same as it does with pages. In static mode, you can [pass props to the endpoint using `getStaticPaths()`](/en/reference/routing-reference/#data-passing-with-props). However, with on-demand rendering, since the endpoint is a function and not a component, passing props is not supported. ### `request` All endpoints receive a `request` property, but in static mode, you only have access to `request.url`. This returns the full URL of the current endpoint and works the same as [Astro.request.url](/en/reference/api-reference/#request) does for pages. ``` }) satisfies APIRoute; ``` ## Server Endpoints (API Routes) Everything described in the static file endpoints section can also be used in SSR mode: files can export a `GET` function which receives a [context object](/en/reference/api-reference/) with properties similar to the `Astro` global. But, unlike in `static` mode, when you enable on-demand rendering for a route, the endpoint will be built when it is requested. This unlocks new features that are unavailable at build time, and allows you to build API routes that listen for requests and securely execute code on the server at runtime. Your routes will be rendered on demand by default in `server` mode. In `static` mode, you must opt out of prerendering for each custom endpoint with `export const prerender = false`. :::note Be sure to [enable an on-demand rendering mode](/en/guides/on-demand-rendering/) before trying these examples, and opt out of prerendering in `static` mode. ::: Server endpoints can access `params` without exporting `getStaticPaths`, and they can return a `Response` object, allowing you to set status codes and headers: ``` const product = await getProduct(id); if (!product) { return new Response(null, { status: 404, statusText: "Not found", }); } return new Response(JSON.stringify(product), { status: 200, headers: { "Content-Type": "application/json", }, }); }) satisfies APIRoute; ``` This will respond to any request that matches the dynamic route. For example, if we navigate to `/helmet.json`, `params.id` will be set to `helmet`. If `helmet` exists in the mock product database, the endpoint will use a `Response` object to respond with JSON and return a successful [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/API/Response/status). If not, it will use a `Response` object to respond with a `404`. In SSR mode, certain providers require the `Content-Type` header to return an image. In this case, use a `Response` object to specify a `headers` property. For example, to produce a binary `.png` image: ``` const buffer = Buffer.from(await response.arrayBuffer()); // requires `@types/node` in your dependencies for type safety return new Response(buffer, { headers: { "Content-Type": "image/png" }, }); }) satisfies APIRoute; ``` ### HTTP methods In addition to the `GET` function, you can export a function with the name of any [HTTP method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods). When a request comes in, Astro will check the method and call the corresponding function. You can also export an `ALL` function to match any method that doesn't have a corresponding exported function. If there is a request with no matching method, it will redirect to your site's [404 page](/en/basics/astro-pages/#custom-404-error-page). ``` }) satisfies APIRoute; }) satisfies APIRoute; }) satisfies APIRoute; }) satisfies APIRoute; ``` If you define a `GET` function but no `HEAD` function, Astro will automatically handle `HEAD` requests by calling the `GET` function and stripping the body from the response. ### `request` In SSR mode, the `request` property returns a fully usable [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) object that refers to the current request. This allows you to accept data and check headers: ``` const name = body.name; return new Response( JSON.stringify({ message: "Your name was: " + name, }), { status: 200, }, ); } return new Response(null, { status: 400 }); }) satisfies APIRoute; ``` ### Redirects The endpoint context exports a `redirect()` utility similar to `Astro.redirect`: ``` const link = await getLinkUrl(id); if (!link) { return new Response(null, { status: 404, statusText: "Not found", }); } return redirect(link, 307); }) satisfies APIRoute; ``` ## [Using environment variables](https://docharvest.github.io/docs/astro/guides/environment-variables/) Contents astro Using environment variables Astro Using environment variables Astro gives you access to [Vite's built-in environment variables support](#vites-built-in-support) and includes some [default environment variables for your project](#default-environment-variables) that allow you to access configuration values for your current project (e.g. `site`, `base`), whether your project is running in development or production, and more. Astro also provides a way to [use and organize your environment variables with type safety](#type-safe-environment-variables). It is available for use inside the Astro context (e.g. Astro components, routes and endpoints, UI framework components, middleware), and managed with [a schema in your Astro configuration](/en/reference/configuration-reference/#env). ## Vite's built-in support Astro uses Vite's built-in support for environment variables, which are statically replaced at build time, and lets you [use any of its methods](https://vite.dev/guide/env-and-mode.html) to work with them. Note that while _all_ environment variables are available in server-side code, only environment variables prefixed with `PUBLIC_` are available in client-side code for security purposes. ``` SECRET_PASSWORD=password123 PUBLIC_ANYBODY=there ``` In this example, `PUBLIC_ANYBODY` (accessible via `import.meta.env.PUBLIC_ANYBODY`) will be available in server or client code, while `SECRET_PASSWORD` (accessible via `import.meta.env.SECRET_PASSWORD`) will be server-side only. :::caution `.env` files are not loaded inside [configuration files](#in-the-astro-config-file). ::: ### IntelliSense for TypeScript By default, Astro provides a type definition for `import.meta.env` in `astro/client.d.ts`. While you can define more custom env variables in `.env.[mode]` files, you may want to get TypeScript IntelliSense for user-defined env variables which are prefixed with `PUBLIC_`. To achieve this, you can create an `env.d.ts` in `src/` to [extend the global types](/en/guides/typescript/#extending-global-types) and configure `ImportMetaEnv` like this: ``` interface ImportMetaEnv { readonly DB_PASSWORD: string; readonly PUBLIC_POKEAPI: string; // more env variables... } interface ImportMeta { readonly env: ImportMetaEnv; } ``` ## Default environment variables Astro includes a few environment variables out of the box: - `import.meta.env.MODE`: The mode your site is running in. This is `development` when running `astro dev` and `production` when running `astro build`. - `import.meta.env.PROD`: `true` if your site is running in production; `false` otherwise. - `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`. Use them like any other environment variable. ``` const isProd = import.meta.env.PROD; const isDev = import.meta.env.DEV; ``` ## Setting environment variables ### `.env` files Environment variables can be loaded from `.env` files in your project directory. Just create a `.env` file in the project directory and add some variables to it. ``` # This will only be available when run on the server! DB_PASSWORD="foobar" # This will be available everywhere! PUBLIC_POKEAPI="https://pokeapi.co/api/v2" ``` You can also add `.production`, `.development` or a custom mode name to the filename itself (e.g `.env.testing`, `.env.staging`). This allows you to use different sets of environment variables at different times. The `astro dev` and `astro build` commands default to `"development"` and `"production"` modes, respectively. You can run these commands with the [`--mode` flag](/en/reference/cli-reference/#--mode-string) to pass a different value for `mode` and load the matching `.env` file. This allows you to run the dev server or build your site connecting to different APIs: \`\`\`shell # Run the dev server connected to a "staging" API npm run astro dev -- --mode staging ```` # Build a site that connects to a "production" API with additional debug information npm run astro build -- --devOutput # Build a site that connects to a "testing" API npm run astro build -- --mode testing ``` ```` \`\`\`shell # Run the dev server connected to a "staging" API pnpm astro dev --mode staging ```` # Build a site that connects to a "production" API with additional debug information pnpm astro build --devOutput # Build a site that connects to a "testing" API pnpm astro build --mode testing ``` ```` \`\`\`shell # Run the dev server connected to a "staging" API yarn astro dev --mode staging ```` # Build a site that connects to a "production" API with additional debug information yarn astro build --devOutput # Build a site that connects to a "testing" API yarn astro build --mode testing ``` ```` For more on `.env` files, [see the Vite documentation](https://vite.dev/guide/env-and-mode.html#env-files). ### In the Astro config file Astro evaluates configuration files before it loads your other files. This means that you cannot use `import.meta.env` in `astro.config.mjs` to access environment variables that were set in `.env` files. You can use `process.env` in a configuration file to access other environment variables, like those [set by the CLI](#using-the-cli). You can also use [Vite's `loadEnv` helper](https://main.vite.dev/config/#using-environment-variables-in-config) to manually load `.env` files. ``` const { SECRET_PASSWORD } = loadEnv(process.env.NODE_ENV, process.cwd(), ""); ``` :::note `pnpm` does not allow you to import modules that are not directly installed in your project. If you are using `pnpm`, you will need to install `vite` to use the `loadEnv` helper. ``` pnpm add -D vite ``` ::: ### Using the CLI You can also add environment variables as you run your project: \`\`\`shell PUBLIC\_POKEAPI=https://pokeapi.co/api/v2 yarn run dev \`\`\` \`\`\`shell PUBLIC\_POKEAPI=https://pokeapi.co/api/v2 npm run dev \`\`\` \`\`\`shell PUBLIC\_POKEAPI=https://pokeapi.co/api/v2 pnpm run dev \`\`\` ## Getting environment variables Environment variables in Astro are accessed with `import.meta.env`, using the [`import.meta` feature added in ES2020](https://tc39.es/ecma262/2020/#prod-ImportMeta), instead of `process.env`. For example, use `import.meta.env.PUBLIC_POKEAPI` to get the `PUBLIC_POKEAPI` environment variable. ``` // When import.meta.env.SSR === true const data = await db(import.meta.env.DB_PASSWORD); // When import.meta.env.SSR === false const data = fetch(`${import.meta.env.PUBLIC_POKEAPI}/pokemon/squirtle`); ``` When using SSR, environment variables can be accessed at runtime based on the SSR adapter being used. With most adapters you can access environment variables with `process.env`, but some adapters work differently. For the Deno adapter, you will use `Deno.env.get()`. See how to [access the Cloudflare runtime](/en/guides/integrations-guide/cloudflare/#cloudflare-runtime) to handle environment variables when using the Cloudflare adapter. Astro will first check the server environment for variables, and if they don't exist, Astro will look for them in `.env` files. ## Type safe environment variables The `astro:env` API lets you configure a type-safe schema for [environment variables you have set](#setting-environment-variables). This allows you to indicate whether they should be available on the server or the client, and define their data type and additional properties. Developing an adapter? See how to [make an adapter compatible with `astro:env`](/en/reference/adapter-reference/#envgetsecret). ### Basic Usage #### Define your schema To configure a schema, add the `env.schema` option to your Astro config: ``` fetch(`${API_URL}/ping`) ``` ### Variable types There are three kinds of environment variables, determined by the combination of `context` (`"client"` or `"server"`) and `access` (`"secret"` or `"public"`) settings defined in your schema: - **Public client variables**: These variables end up in both your final client and server bundles, and can be accessed from both client and server through the `astro:env/client` module: ``` import { API_URL } from "astro:env/client"; ``` - **Public server variables**: These variables end up in your final server bundle and can be accessed on the server through the `astro:env/server` module: ``` import { PORT } from "astro:env/server"; ``` - **Secret server variables**: These variables are not part of your final bundle and can be accessed on the server through the `astro:env/server` module: ``` import { API_SECRET } from "astro:env/server"; ``` By default, all secrets are validated whenever anything is imported from the `astro:env/server` module. This means, secrets may be validated even when they are not imported. You may need to [pass dummy environment variables](#setting-environment-variables) to satisfy this validation during the build. You can also enable validating secrets on start by [configuring `validateSecrets: true`](/en/reference/configuration-reference/#envvalidatesecrets). :::note **Secret client variables** are not supported because there is no safe way to send this data to the client. Therefore, it is not possible to configure both `context: "client"` and `access: "secret"` in your schema. ::: ### Data types There are currently four data types supported: strings, numbers, enums, and booleans: ``` envField.string({ // context & access optional: true, default: "foo", }) envField.number({ // context & access optional: true, default: 15, }) envField.boolean({ // context & access optional: true, default: true, }) envField.enum({ // context & access values: ["foo", "bar", "baz"], optional: true, default: "baz", }) ``` For a complete list of validation fields, see the [`envField` API reference](/en/reference/modules/astro-config/#envfield). ### Retrieving secrets dynamically Despite defining your schema, you may want to retrieve the raw value of a given secret or to retrieve secrets not defined in your schema. In this case, you can use `getSecret()` exported from `astro:env/server`: ``` getSecret("FOO"); // string | undefined ``` Learn more in [the API reference](/en/reference/modules/astro-env/#getsecret). ### Limitations `astro:env` is a virtual module which means it can only be used inside the Astro context. For example, you can use it in: - Middlewares - Astro routes and endpoints - Astro components - Framework components - Modules You cannot use it in the following and will have to resort to `process.env`: - `astro.config.mjs` - Scripts ## [Using custom fonts](https://docharvest.github.io/docs/astro/guides/fonts/) Contents astro Using custom fonts Astro Using custom fonts 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. 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. 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). 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 inside the [`src/` directory](/en/basics/project-structure/#src), for example `src/assets/fonts/`. 2. Create a new font family in your Astro config file using the [local font provider](/en/reference/font-provider-reference/#local) and specify the variants to be included: ``` import { defineConfig, fontProviders } from "astro/config"; export default defineConfig({ fonts: [{ provider: fontProviders.local(), name: "DistantGalaxy", cssVariable: "--font-distant-galaxy", options: { variants: [{ src: ['./src/assets/fonts/DistantGalaxy.woff2'], weight: 'normal', style: 'normal' }] } }] }); ``` 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 Astro supports [several font providers](/en/reference/font-provider-reference/#built-in-providers) out of the box, including support for [Fontsource](https://fontsource.org/) that simplifies using Google Fonts and other open-source fonts. The following example will use Fontsource to add custom font support, but the process is similar for any of Astro's built-in font providers (e.g. [Adobe](https://fonts.adobe.com/), [Bunny](https://fonts.bunny.net/)). 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): ``` 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. ## Applying custom fonts 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. 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: ``` --- import { Font } from "astro:assets"; --- ``` 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. In the following example, the `

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

` will not. ``` --- import Layout from "../layouts/Layout.astro"; ---

In a galaxy far, far away...

Custom fonts make my headings much cooler!

``` ## Preloading fonts Font preloading should be done sparingly, as it can block the loading of other important resources or download fonts that are unnecessary for the current page. Consider preloading only the most essential fonts, necessary for displaying content visible above the fold. To preload a font, pass the [`preload` property](/en/reference/modules/astro-assets/#preload) to the corresponding `` component. If multiple files correspond to a font, you can also specify which one to preload by passing an array. ``` --- --- ``` ## Register fonts in Tailwind If you are using [Tailwind](/en/guides/styling/#tailwind) for styling, you will not apply your styles with the `font-face` CSS property. 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: ```` ```css title="src/styles/global.css" ins={4} ins="inline" @import "tailwindcss"; @theme inline { --font-sans: var(--font-roboto); } ``` ```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-a-custom-value) for more information. ## Using variable fonts To use [variable fonts](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_fonts/Variable_fonts_guide) in your project, specify the available weight range instead of individual weights in your provider's configuration. When [using a local font file](#using-a-local-font-file), you can specify that the font is variable by setting the [`weight` property of the variant](/en/reference/font-provider-reference/#weight) to a string corresponding to the exact weight range available for the font. The following example configures Inter as a local variable font with the available weight range: When using [other providers (e.g. Fontsource)](#using-fontsource), that support variable fonts, you can request the variable version of a font by setting the [`weights` property](/en/reference/configuration-reference/#fontweights) with an array containing the exact range of weights available for the font. The following example downloads [Fira Code from Fontsource](https://fontsource.org/fonts/fira-code) as a variable font with the available weight range: ## Customizing font fallbacks Fallback fonts are used when the primary font has not yet loaded, contains missing characters, or cannot be loaded for any reason. When the fallback font differs significantly from the primary font, layout shifts may occur during page loading. To avoid this, Astro automatically tries to generate optimized fallback fonts from the last [defined fallback](/en/reference/configuration-reference/#fontfallbacks) if it is a [generic font family](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/font-family#generic-name). It uses `sans-serif` by default, but it may not match the desired appearance of your primary font. You can adjust it in your font configuration: You can also opt out of the default optimization by setting [`font.optimizedFallbacks`](/en/reference/configuration-reference/#fontoptimizedfallbacks) to `false` in your font configuration. Astro will then use the fallback fonts specified in your configuration without any additional automatic processing. ## Accessing font data programmatically Astro exposes low-level APIs for accessing data programmatically: - Font family data through the [`fontData`](/en/reference/modules/astro-assets/#fontdata) object - Font file URLs with the [`experimental_getFontFileURL()`](/en/reference/modules/astro-assets/#experimental_getfontfileurl) function. This can be useful for advanced use cases where you need direct access to font files, such as generating OpenGraph images with [Satori](https://github.com/vercel/satori) in an [API Route](/en/guides/endpoints/#server-endpoints-api-routes). The `fontData` object gives you access to all font files downloaded by Astro for your project, along with their metadata. This means that you are responsible for filtering font files to find the specific file you need, and for fetching data after resolving URLs. The following example generates an OpenGraph image in a static file endpoint, assuming that only [one font and its format have been configured](/en/reference/configuration-reference/#fontformats) with a [format supported by Satori](https://github.com/vercel/satori?tab=readme-ov-file#fonts): ``` if (fontPath === undefined) { throw new Error("Cannot find the font path."); } const url = experimental_getFontFileURL(fontPath, context.url); const data = await fetch(url).then((res) => res.arrayBuffer()); const svg = await satori( html`
hello, world
`, { width: 600, height: 400, fonts: [ { name: "Roboto", data, weight: 400, style: "normal", }, ], }, ); const pngBuffer = await sharp(Buffer.from(svg)) // requires `@types/node` in your dependencies for type safety .resize(600, 400) .png() .toBuffer(); return new Response(new Uint8Array(pngBuffer), { headers: { "Content-Type": "image/png", }, }); }; ``` ## 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`: ## 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. ## [Front-end frameworks](https://docharvest.github.io/docs/astro/guides/framework-components/) Contents astro Front-end frameworks Astro Front-end frameworks * * * # Use React components directly in Astro! \`\`\` By default, your framework components will only render on the server, as static HTML. This is useful for templating components that are not interactive and avoids sending any unnecessary JavaScript to the client. ## Hydrating interactive components A framework component can be made interactive (hydrated) using a [`client:*` directive](/en/reference/directives-reference/#client-directives). These are component attributes that determine when your component's JavaScript should be sent to the browser. With all client directives except `client:only`, your component will first render on the server to generate static HTML. Component JavaScript will be sent to the browser according to the directive you chose. The component will then hydrate and become interactive. ``` --- // Example: hydrating framework components in the browser. --- ``` The JavaScript framework (React, Svelte, etc.) needed to render the component will be sent to the browser along with the component's own JavaScript. If two or more components on a page use the same framework, the framework will only be sent once. :::note\[Accessibility\] Most framework-specific accessibility patterns should work the same when these components are used in Astro. Be sure to choose a client directive that will ensure any accessibility-related JavaScript is properly loaded and executed at the appropriate time! ::: ### Available hydration directives There are several hydration directives available for UI framework components: `client:load`, `client:idle`, `client:visible`, `client:media={QUERY}` and `client:only={FRAMEWORK}`. See our [directives reference](/en/reference/directives-reference/#client-directives) page for a full description of these hydration directives, and their usage. ## Mixing frameworks You can import and render components from multiple frameworks in the same Astro component. ``` --- // Example: Mixing multiple framework components on the same page. ---
``` Astro will recognize and render your component based on its file extension. To distinguish between frameworks that use the same file extension, [additional configuration when rendering multiple JSX frameworks](/en/guides/integrations-guide/react/#combining-multiple-jsx-frameworks) (e.g. React and Preact) is required. :::caution Only **Astro** components (`.astro`) can contain components from multiple frameworks. ::: ## Passing props to framework components You can pass props from Astro components to framework components: ``` --- ---
``` Props that are passed to interactive framework components [using a `client:*` directive](/en/reference/directives-reference/#client-directives) must be [serialized](https://developer.mozilla.org/en-US/docs/Glossary/Serialization): translated into a format suitable for transfer over a network, or storage. However, Astro does not serialize every type of data structure. Therefore, there are some limitations on what can be passed as props to hydrated components. The following prop types are supported: plain object, `number`, `string`, `Array`, `Map`, `Set`, `RegExp`, `Date`, `BigInt`, `URL`, `Uint8Array`, `Uint16Array`, `Uint32Array`, and `Infinity` Non-supported data structures passed to components, such as functions, can only be used during the component's server rendering and cannot be used to provide interactivity. For example, passing functions to hydrated components is not supported because Astro cannot pass functions from the server in a way that makes them executable on the client. ## Passing children to framework components Inside of an Astro component, you **can** pass children to framework components. Each framework has its own patterns for how to reference these children: React, Preact, and Solid all use a special prop named `children`, while Svelte and Vue use the `` element. ``` --- ---

Here is a sidebar with some text and a button.

``` Additionally, you can use [Named Slots](/en/basics/astro-components/#named-slots) to group specific children together. For React, Preact, and Solid, these slots will be converted to a top-level prop. Slot names using `kebab-case` will be converted to `camelCase`. ``` --- ---

Menu

Here is a sidebar with some text and a button.

``` ``` // src/components/MySidebar.jsx export default function MySidebar(props) { return ( ) } ``` For Svelte and Vue these slots can be referenced using a `` element with the `name` attribute. Slot names using `kebab-case` will be preserved. ``` // src/components/MySidebar.svelte ``` ## Nesting framework components 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. ``` --- ---

Here is a sidebar with some text and a button.

``` :::caution Remember: framework component files themselves (e.g. `.jsx`, `.svelte`) cannot mix multiple frameworks. ::: This allows you to build entire "apps" in your preferred JavaScript framework and render them, via a parent component, to an Astro page. :::note Astro components are always rendered to static HTML, even when they include framework components that are hydrated. This means that you can only pass props that don't do any HTML rendering. Passing React's "render props" to framework components from an Astro component will not work, because Astro components can’t provide the client runtime behavior that this pattern requires. Instead, use named slots. ::: ## Can I use Astro components inside my framework components? Any UI framework component becomes an "island" of that framework. These components must be written entirely as valid code for that framework, using only its own imports and packages. You cannot import `.astro` components in a UI framework component (e.g. `.jsx` or `.svelte`). You can, however, use [the Astro `` pattern](/en/basics/astro-components/#slots) to pass static content generated by Astro components as children to your framework components **inside an `.astro` component**. ``` --- --- ``` ## Can I hydrate Astro components? If you try to hydrate an Astro component with a `client:` modifier, you will get an error. [Astro components](/en/basics/astro-components/) are HTML-only templating components with no client-side runtime. But, you can use a ` A starry night sky. ``` Astro components (e.g. ``, ``, SVG components) are unavailable inside UI framework components because [a client island must contain only valid code for its own framework](/en/guides/framework-components/#can-i-use-astro-components-inside-my-framework-components). But, you can pass the static content generated by these components to a framework component inside a `.astro` file [as children](/en/guides/framework-components/#passing-children-to-framework-components) or using a [named ``](/en/guides/framework-components/#can-i-use-astro-components-inside-my-framework-components): ``` --- --- A starry night sky. ``` ## Astro components for images Astro provides two built-in Astro components for images (`` and ``) and also allows you to import SVG files and use them as Astro components. These components may be used in any files that can import and render `.astro` components. ### `` Use the built-in `` Astro component to display optimized versions of: - your local images located within the `src/` folder - [configured remote images](#authorizing-remote-images) from authorized sources `` can transform a local or authorized remote image's dimensions, file type, and quality for control over your displayed image. This transformation happens at build time for prerendered pages. When your page is rendered on demand, this transformation will occur on the fly when the page is viewed. The resulting `` tag includes `alt`, `loading`, and `decoding` attributes and infers image dimensions to avoid Cumulative Layout Shift (CLS). :::note\[What is Cumulative Layout Shift?\] [Cumulative Layout Shift (CLS)](https://web.dev/cls/) is a Core Web Vital metric for measuring how much content shifted on your page during loading. The `` component optimizes for CLS by automatically setting the correct `width` and `height` for your images. ::: ``` --- // import the Image component and the image --- ``` ``` ``` :::tip You can also use the `` component for images in the `public/` folder, or remote images not specifically configured in your project, even though these images will not be optimized or processed. The resulting image will be the same as using the HTML ``. However, using the image component for all images provides a consistent authoring experience and prevents Cumulative Layout Shift (CLS) even for your unoptimized images. ::: ### `` Use the built-in `` Astro component to generate a `` tag with multiple formats and/or sizes of your image. This allows you to specify preferred file formats to display and at the same time, provide a fallback format. Like the [`` component](#image-), images will be processed at build time for prerendered pages. When your page is rendered on demand, processing will occur on the fly when the page is viewed. The following example uses the `` component to transform a local `.png` file into a web-friendly `avif` and `webp` format as well as the `.png` `` that can be displayed as a fallback when needed: ``` --- --- A description of my image. ``` This `` component will generate the following HTML output on a prerendered page: ``` A description of my image ``` #### Responsive image styles Setting [`image.responsiveStyles: true`](/en/reference/configuration-reference/#imageresponsivestyles) applies a small number of global styles to ensure that your images resize correctly. In most cases, you will want to enable these as a default; your images will not be responsive without additional styles. However, if you prefer to handle responsive image styling yourself, or need to [override these defaults when using Tailwind 4](#responsive-images-with-tailwind-4), leave the default `false` value configured. The global styles applied by Astro will depend on the layout type, and are designed to produce the best result for the generated `srcset` and `sizes` attributes. These are the default styles: ``` :where([data-astro-image]) { object-fit: var(--fit); object-position: var(--pos); } :where([data-astro-image='full-width']) { width: 100%; } :where([data-astro-image='constrained']) { max-width: 100%; } ``` 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. #### Responsive images with Tailwind 4 Tailwind 4 is compatible with Astro's default responsive styles. However, Tailwind uses [cascade layers](https://developer.mozilla.org/en-US/docs/Web/CSS/@layer), meaning that its rules are always lower specificity than rules that don't use layers, including Astro's responsive styles. Therefore, Astro's styling will take precedence over Tailwind styling. To use Tailwind rules instead of Astro's default styling, do not enable [Astro's default responsive styles](/en/reference/configuration-reference/#imageresponsivestyles). ### SVG components Astro allows you to import SVG files and use them as Astro components. Astro will inline the SVG content into your HTML output. Reference the default import of any local `.svg` file. Since this import is treated as an Astro component, you must use the same conventions (e.g. capitalization) as when [using dynamic tags](/en/reference/astro-syntax/#dynamic-tags). ``` --- --- ``` Your SVG component, like `` or any other Astro component, is unavailable inside UI framework components, but can [be passed to a framework component](#images-in-ui-framework-components) inside a `.astro` component. #### SVG component attributes You can pass props such as `width`, `height`, `fill`, `stroke`, and any other attribute accepted by the [native `` element](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg). These attributes will automatically be applied to the underlying `` element. If a property is present in the original `.svg` file and is passed to the component, the value passed to the component will override the original value. ``` --- --- ``` #### `SvgComponent` Type You can also enforce type safety for your `.svg` assets using the `SvgComponent` type: ``` --- interface Link { url: string; text: string; icon: SvgComponent; } const links: Link[] = [ { url: "/", text: "Home", icon: HomeIcon, }, ]; --- ``` ### Creating custom image components You can create a custom, reusable image component by wrapping the `` or `` component in another Astro component. This allows you to set default attributes and styles only once. For example, you could create a component for your blog post images that receives attributes as props and applies consistent styles to each image: ``` --- const { alt, src, ...attrs } = Astro.props; --- ``` ## Display unprocessed images with the HTML `` tag The [Astro template syntax](/en/reference/astro-syntax/) also supports writing an `` tag directly, with full control over its final output. These images will not be processed and optimized. It accepts all HTML `` tag properties, and the only required property is `src`. However, it is strongly recommended to include [the `alt` property for accessibility](#alt-text). ### images in `src/` Local images must be imported from the relative path from the existing `.astro` file, or you can configure and use an [import alias](/en/guides/imports/#aliases). Then, you can access the image's `src` and other properties to use in the `` tag. Imported image assets match the [`ImageMetadata` type](/en/reference/modules/astro-assets/#imagemetadata-1) and have the following signature: ``` interface ImageMetadata { src: string; width: number; height: number; format: string; } ``` The following example uses the image's own `height` and `width` properties to avoid Cumulative Layout Shift (CLS) and improve Core Web Vitals: ``` --- // import local images --- // access the image properties A barking dog. ``` ### Images in `public/` For images located within `public/` use the image's file path relative to the public folder as the `src` value: ``` A sleeping cat. ``` ### Remote images For remote images, use the image's full URL as the `src` value: ``` A sleeping cat. ``` ### Choosing `` vs `` The `` component optimizes your image and infers width and height (for images it can process) based on the original aspect ratio to avoid CLS. It is the preferred way to use images in `.astro` files whenever possible. Use the HTML `` element when you cannot use the `` component, for example: - for unsupported image formats - when you do not want your image optimized by Astro - to access and change the `src` attribute dynamically client-side ## Using Images from a CMS or CDN Image CDNs work with [all Astro image options](#images-in-astro-files). Use an image's full URL as the `src` attribute in the `` component, an `` tag, or in Markdown notation. For image optimization with remote images, also [configure your authorized domains or URL patterns](#authorizing-remote-images). Alternatively, the CDN may provide its own SDKs to more easily integrate in an Astro project. For example: - Cloudinary supports an [Astro SDK](https://astro.cloudinary.dev/) which allows you to easily drop in images with their `CldImage` component or a [Node.js SDK](https://cloudinary.com/documentation/node_integration) that can generate URLs to use with an `` tag in a Node.js environment. - ImageKit provides an [Astro integration](https://github.com/imagekit-developer/imagekit-astro) that registers an image service. This means Astro's built-in `` and `` components, along with Markdown `![]()` and MDX images, are all routed through ImageKit automatically. It adds CDN delivery, automatic AVIF/WebP conversion, responsive `srcset`, and on-the-fly transformations without changing your existing image syntax. See the full API reference for the [``](/en/reference/modules/astro-assets/#image-) and [``](/en/reference/modules/astro-assets/#picture-) components. ## Authorizing remote images You can configure lists of authorized image source URL domains and patterns for image optimization using [`image.domains`](/en/reference/configuration-reference/#imagedomains) and [`image.remotePatterns`](/en/reference/configuration-reference/#imageremotepatterns). This configuration is an extra layer of safety to protect your site when showing images from an external source. Remote images from other sources will not be optimized, but using the `` component for these images will prevent Cumulative Layout Shift (CLS). For example, the following configuration will only allow remote images from `astro.build` to be optimized: ``` // astro.config.mjs ``` The following configuration will only allow remote images from HTTPS hosts: ``` // astro.config.mjs ``` ## Images in content collections You can declare an associated image for a content collections entry, such as a blog post's cover image, in your frontmatter using its path relative to the current folder: ``` --- title: "My first blog post" cover: "./firstpostcover.jpeg" # will resolve to "src/content/blog/firstpostcover.jpeg" coverAlt: "A photograph of a sunset behind a mountain range." --- This is a blog post ``` The `image` helper for the content collections schema lets you validate and import the image. ``` const blogCollection = defineCollection({ schema: ({ image }) => z.object({ title: z.string(), cover: image(), coverAlt: z.string(), }), }); ``` The image will be imported and transformed into metadata, allowing you to pass it as a `src` to ``, ``, or `getImage()` in an Astro component. The example below shows a blog index page that renders the cover photo and title of each blog post from the previous schema: ``` --- const allBlogPosts = await getCollection("blog"); --- { allBlogPosts.map((post) => ( )) } ``` ## Generating images with `getImage()` 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: ``` --- const optimizedBackground = await getImage({ src: myBackground, format: "avif" }); ---
``` See more in the [`getImage()` reference](/en/reference/modules/astro-assets/#getimage). ## Alt Text Not all users can see images in the same way, so accessibility is an especially important concern when using images. Use the `alt` attribute to provide [descriptive alt text](https://www.w3.org/WAI/tutorials/images/) for images. This attribute is required for both the `` and `` components. If no alt text is provided, a helpful error message will be provided reminding you to include the `alt` attribute. If the image is merely decorative (i.e. doesn’t contribute to the understanding of the page), set `alt=""` so that screen readers know to ignore the image. ## Default image service [Sharp](https://github.com/lovell/sharp) is the default image service used for `astro:assets`. You can further configure the image service using the [`image.service`](/en/reference/configuration-reference/#imageservice) option. :::note When using a [strict package manager](https://pnpm.io/pnpm-vs-npm#npms-flat-tree) like `pnpm`, you may need to manually install Sharp into your project even though it is an Astro dependency: ``` 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. 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: ## Asset Caching Astro stores processed image assets in a cache directory during site builds for both local and [remote images from authorized sources](#authorizing-remote-images). By preserving the cache directory between builds, processed assets are reused, improving build time and bandwidth usage. The default cache directory is `./node_modules/.astro`, however this can be changed using the [`cacheDir`](/en/reference/configuration-reference/#cachedir) configuration setting. ### Remote Images Remote images in the asset cache are managed based on [HTTP Caching](https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching), and respect the [Cache-Control header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) returned by the remote server. Images are cached if the Cache-Control header allows, and will be used until they are no longer [fresh](https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching#fresh_and_stale_based_on_age). #### Revalidation [Revalidation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching#validation) reduces bandwidth usage and build time by checking with the remote server whether an expired cached image is still up-to-date. If the server indicates that the image is still fresh, the cached version is reused, otherwise the image is redownloaded. Revalidation requires that the remote server send [Last-Modified](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified) and/or [Etag (entity tag)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) headers with its responses. This feature is available for remote servers that support the [If-Modified-Since](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Modified-Since) and [If-None-Match](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match) headers. ## Community Integrations There are several third-party [community image integrations](https://astro.build/integrations?search=images) for optimizing and working with images in your Astro project. ## [Imports reference](https://docharvest.github.io/docs/astro/guides/imports/) Contents astro Imports reference Astro Imports reference ```` JavaScript can be imported using normal ESM `import` & `export` syntax. :::note[Importing JSX files] An appropriate [UI framework](/en/guides/framework-components/) ([React](/en/guides/integrations-guide/react/), [Preact](/en/guides/integrations-guide/preact/), or [Solid](/en/guides/integrations-guide/solid-js/)) is required to render JSX/TSX files. Use `.jsx`/`.tsx` extensions where appropriate, as Astro does not support JSX in `.js`/`.ts` files. ::: ### TypeScript ```js ```` Astro includes built-in support for [TypeScript](https://www.typescriptlang.org/). You can import `.ts` and `.tsx` files directly in your Astro project, and even write TypeScript code directly inside your [Astro component script](/en/basics/astro-components/#the-component-script) and any [script tags](/en/guides/client-side-scripts/). **Astro doesn't perform any type checking itself.** Type checking should be taken care of outside of Astro, either by your IDE or through a separate script. For type checking Astro files, the [`astro check` command](/en/reference/cli-reference/#astro-check) is provided. :::note\[TypeScript and file extensions\] Per [TypeScript's module resolution rules](https://www.typescriptlang.org/docs/handbook/module-resolution.html), `.ts` and `.tsx` file extensions should not be used when importing TypeScript files. Instead, either use `.js`/`.jsx` file extensions or completely omit the file extension. ``` --- ``` If a package was published using a legacy format, Astro will try to convert the package to ESM so that `import` statements work. In some cases, you may need to adjust your [`vite` config](/en/reference/configuration-reference/#vite) for it to work. :::caution Some packages rely on a browser environment. Astro components runs on the server, so importing these packages in the frontmatter may [lead to errors](/en/guides/troubleshooting/#document-or-window-is-not-defined). ::: ### JSON ``` // Load the JSON object via the default export ``` Astro supports importing JSON files directly into your application. Imported files return the full JSON object in the default import. ### CSS ``` // Load and inject 'style.css' onto the page ``` Astro supports importing CSS files directly into your application. Imported styles expose no exports, but importing one will automatically add those styles to the page. This works for all CSS files by default, and can support compile-to-CSS languages like Sass & Less via plugins. Read more about advanced CSS import use cases such as a direct URL reference for a CSS file, or importing CSS as a string in the [Styling guide](/en/guides/styling/#advanced). ### CSS Modules ``` // 1. Converts './style.module.css' classnames to unique, scoped values. // 2. Returns an object mapping the original classnames to their final, scoped value. // This example uses JSX, but you can use CSS Modules with any framework. return
Your Error Message
; ``` Astro supports CSS Modules using the `[name].module.css` naming convention. Like any CSS file, importing one will automatically apply that CSS to the page. However, CSS Modules export a special default `styles` object that maps your original classnames to unique identifiers. CSS Modules help you enforce component scoping & isolation on the frontend with uniquely-generated class names for your stylesheets. ### Other Assets ``` // Returns an object with `src` and other properties // HTML or UI Framework components use this to render the image image description; // The Astro `` and `` components access `src` by default image description ``` All other assets not explicitly mentioned above can be imported via ESM `import` and will return a URL reference to the final built asset (e.g. `/_astro/my-video.C7vXpQtF.mp4`) instead of an object. This can be useful for referencing non-JS assets by URL, like creating a video element with a `src` attribute pointing to that image. It can also be useful to place images and other assets in the `public/` folder as explained on the [project-structure page](/en/basics/project-structure/#public). Read more about appending Vite import parameters (e.g. `?url`, `?raw`) in [Vite's static asset handling guide](https://vite.dev/guide/assets.html). :::note Adding **alt text** to `` tags is encouraged for accessibility! Don't forget to add an `alt="a helpful description"` attribute to your image elements. You can just leave the attribute empty if the image is purely decorative. ::: ## Aliases An **alias** is a way to create shortcuts for your imports. Aliases can help improve the development experience in codebases with many directories or relative imports. ``` --- --- ``` In this example, a developer would need to understand the tree relationship between `src/pages/about/company.astro`, `src/components/controls/Button.astro`, and `src/assets/logo.png`. And then, if the `company.astro` file were to be moved, these imports would also need to be updated. You can add import aliases in `tsconfig.json`. ``` { "compilerOptions": { "paths": { "@components/*": ["./src/components/*"], "@assets/*": ["./src/assets/*"] } } } ``` The development server will automatically restart after this configuration change. You can now import using the aliases anywhere in your project: ``` --- --- ``` ## `import.meta.glob()` [Vite's `import.meta.glob()`](https://vite.dev/guide/features.html#glob-import) is a way to import many files at once using glob patterns to find matching file paths. `import.meta.glob()` takes a relative [glob pattern](#glob-patterns) matching the local files you'd like to import as a parameter. It returns an array of each matching file's exports. To load all matched modules up front, pass `{ eager: true }` as the second argument: ``` --- // imports all files that end with `.md` in `./src/pages/post/` const matches = import.meta.glob('../pages/post/*.md', { eager: true }); const posts = Object.values(matches); ---
{posts.slice(0, 4).map((post) => (

{post.frontmatter.title}

{post.frontmatter.description}

Read more
))}
``` Astro components imported using `import.meta.glob` are of type [`AstroInstance`](#astro-files). You can render each component instance using its `default` property: ``` --- // imports all files that end with `.astro` in `./src/components/` const components = Object.values(import.meta.glob('../components/*.astro', { eager: true })); --- {components.map((component) => (
))} ``` ### Supported Values Vite's `import.meta.glob()` function only supports static string literals. It does not support dynamic variables and string interpolation. A common workaround is to instead import a larger set of files that includes all the files you need, then filter them: ``` --- // src/components/featured.astro const { postSlug } = Astro.props; const pathToMyFeaturedPost = `src/pages/blog/${postSlug}.md`; const posts = Object.values(import.meta.glob("../pages/blog/*.md", { eager: true })); const myFeaturedPost = posts.find(post => post.file.includes(pathToMyFeaturedPost)); ---

Take a look at my favorite post, {myFeaturedPost.frontmatter.title}!

``` ### Import type utilities #### Markdown files Markdown files loaded with `import.meta.glob()` return the following `MarkdownInstance` interface: ``` /* The absolute file path of this file */ file: string; /* The rendered path of this file */ url: string | undefined; /* Astro Component that renders the contents of this file */ Content: AstroComponentFactory; /** (Markdown only) Raw Markdown file content, excluding layout HTML and YAML/TOML frontmatter */ rawContent(): string; /** (Markdown only) Markdown file compiled to HTML, excluding layout HTML */ compiledContent(): string; /* Function that returns an array of the h1...h6 elements in this file */ getHeadings(): Promise<{ depth: number; slug: string; text: string }[]>; default: AstroComponentFactory; } ``` You can optionally provide a type for the `frontmatter` variable using a TypeScript generic. ``` --- interface Frontmatter { title: string; description?: string; } const posts = Object.values(import.meta.glob>('./posts/**/*.md', { eager: true })); ---
    {posts.map(post =>
  • {post.frontmatter.title}
  • )}
``` #### Astro files Astro files have the following interface: ``` /* The URL for this file (if it is in the pages directory) */ url: string | undefined; default: AstroComponentFactory; } ``` #### Other files Other files may have various different interfaces, but `import.meta.glob()` accepts a TypeScript generic if you know exactly what an unrecognized file type contains. ``` --- interface CustomDataFile { default: Record; } const data = import.meta.glob('../data/**/*.js'); --- ``` ### Glob Patterns A glob pattern is a file path that supports special wildcard characters. This is used to reference multiple files in your project at once. For example, the glob pattern `./pages/**/*.{md,mdx}` starts within the pages subdirectory, looks through all of its subdirectories (`/**`), and matches any filename (`/*`) that ends in either `.md` or `.mdx` (`.{md,mdx}`). #### Glob Patterns in Astro To use with `import.meta.glob()`, the glob pattern must be a string literal and cannot contain any variables. Additionally, glob patterns must begin with one of the following: - `./` (to start in the current directory) - `../` (to start in the parent directory) - `/` (to start at the root of the project) [Read more about the glob pattern syntax](https://github.com/micromatch/picomatch#globbing-features). ### `import.meta.glob()` vs `getCollection()` [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 ``` // Loads and initializes the requested WASM file const wasm = await WebAssembly.instantiateStreaming(fetch('/example.wasm')); ``` Astro supports loading WASM files directly into your application using the browser’s [`WebAssembly`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly) API. ## Node Builtins Astro supports Node.js built-ins, with some limitations, using Node’s newer `node:` prefix. There may be differences between development and production, and some features may be incompatible with on-demand rendering. Some [adapters](/en/guides/on-demand-rendering/) may also be incompatible with these built-ins modules or require configuration to support a subset (e.g., [Cloudflare Workers](/en/guides/integrations-guide/cloudflare/) or [Deno](https://github.com/denoland/deno-astro-adapter)). The following example imports the `util` module from Node to parse a media type (MIME): ``` --- // Example: import the "util" built-in from Node.js export interface Props { mimeType: string, } const mime = new util.MIMEType(Astro.props.mimeType) --- Type: {mime.type} SubType: {mime.subtype} ``` ## Extending file type support With **Vite** and compatible **Rollup** plugins, you can import file types which aren't natively supported by Astro. Learn where to find the plugins you need in the [Finding Plugins](https://vite.dev/guide/using-plugins.html#finding-plugins) section of the Vite Documentation. :::note\[Plugin configuration\] Refer to your plugin's documentation for configuration options, and how to correctly install it. ::: ## [Working with integrations](https://docharvest.github.io/docs/astro/guides/integrations/) ## [@astrojs/alpinejs](https://docharvest.github.io/docs/astro/guides/integrations-guide/alpinejs/) Contents astro @astrojs/alpinejs Astro @astrojs/alpinejs This **[Astro integration](/en/guides/integrations/)** adds [Alpine.js](https://alpinejs.dev/) to your project so that you can use Alpine.js anywhere on your page. ## 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. To install `@astrojs/alpinejs`, run the following from your project directory and follow the prompts: \`\`\`sh npx astro add alpinejs \`\`\` \`\`\`sh pnpm astro add alpinejs \`\`\` \`\`\`sh yarn astro add alpinejs \`\`\` If you run into any issues, [feel free to report them to us on GitHub](https://github.com/withastro/astro/issues) and try the manual installation steps below. ### Manual Install First, install the `@astrojs/alpinejs` package. \`\`\`sh npm install @astrojs/alpinejs \`\`\` \`\`\`sh pnpm add @astrojs/alpinejs \`\`\` \`\`\`sh yarn add @astrojs/alpinejs \`\`\` Most package managers will install associated peer dependencies as well. However, if you see a `Cannot find package 'alpinejs'` (or similar) warning when you start up Astro, you'll need to manually install Alpine.js yourself: \`\`\`sh npm install alpinejs @types/alpinejs \`\`\` \`\`\`sh pnpm add alpinejs @types/alpinejs \`\`\` \`\`\`sh yarn add alpinejs @types/alpinejs \`\`\` Then, apply the integration to your `astro.config.*` file using the `integrations` property: ## Configuration Options ### `entrypoint` **Type:** `string` You can extend Alpine by setting the `entrypoint` option to a root-relative import specifier (e.g. `entrypoint: "/src/entrypoint"`). The default export of this file should be a function that accepts an Alpine instance prior to starting. This allows the use of custom directives, plugins and other customizations for advanced use cases. ``` } ``` ## Examples - The [Astro Alpine.js example](https://github.com/withastro/astro/tree/main/examples/framework-alpine) shows how to use Alpine.js in an Astro project. ## [@astrojs/cloudflare](https://docharvest.github.io/docs/astro/guides/integrations-guide/cloudflare/) Contents astro @astrojs/cloudflare Astro @astrojs/cloudflare This adapter allows Astro to deploy your [on-demand rendered routes and features](/en/guides/on-demand-rendering/) to [Cloudflare](https://www.cloudflare.com/), including [server islands](/en/guides/server-islands/), [actions](/en/guides/actions/), and [sessions](/en/guides/sessions/). 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. Add the Cloudflare adapter to enable server-rendering in your Astro project with the `astro add` command. This will install `@astrojs/cloudflare` and make the appropriate changes to your `astro.config.mjs` file in one step. \`\`\`sh npx astro add cloudflare \`\`\` \`\`\`sh pnpm astro add cloudflare \`\`\` \`\`\`sh yarn astro add cloudflare \`\`\` Now, you can enable [on-demand rendering per page](/en/guides/on-demand-rendering/#enabling-on-demand-rendering), or set your build output configuration to `output: 'server'` to [server-render all your pages by default](/en/guides/on-demand-rendering/#server-mode). ### Manual Install 1. Add the `@astrojs/cloudflare` adapter to your project's dependencies using your preferred package manager. \`\`\`sh npm install @astrojs/cloudflare \`\`\` \`\`\`sh pnpm add @astrojs/cloudflare \`\`\` \`\`\`sh yarn add @astrojs/cloudflare \`\`\` 2. Add the adapter to your `astro.config.mjs` file: ``` import { defineConfig } from 'astro/config'; import cloudflare from '@astrojs/cloudflare'; export default defineConfig({ adapter: cloudflare(), }); ``` 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: ``` { "name": "my-astro-app", // Add your bindings here, e.g.: // "kv_namespaces": [{ "binding": "MY_KV", "id": "" }] } ``` ## Options The Cloudflare adapter accepts the following options from [`@cloudflare/vite-plugin`](https://developers.cloudflare.com/workers/vite-plugin/): - `auxiliaryWorkers` - `configPath` - `inspectorPort` - `persistState` - `remoteBindings` - `experimental.headersAndRedirectsDevModeSupport` It also accepts the following: ### `imageService` \*\*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 `cloudflare-binding` 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. - **`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 the [configured image service](/en/reference/configuration-reference/#imageservice) or falls back to 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`:** Uses the [configured image service](/en/reference/configuration-reference/#imageservice) (defaults to Sharp) to process assets at build time for prerendered routes. Bundles the image service for runtime image handling, without checking its compatibility with Cloudflare's `workerd` runtime. 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'`: ### `sessionKVBindingName` \*\*Type:\*\* \`string\` \*\*Default:\*\* \`SESSION\` 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. ``` { "kv_namespaces": [ { "binding": "MY_SESSION_BINDING", } ] } ``` ### `imagesBindingName` \*\*Type:\*\* \`string\` \*\*Default:\*\* \`IMAGES\` 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: ``` { "images": { "binding": "MY_IMAGES" } } ``` ### `prerenderEnvironment` \*\*Type:\*\* \`'workerd' | 'node'\` \*\*Default:\*\* \`'workerd'\` Controls which runtime is used for [prerendering](/en/guides/on-demand-rendering/) static pages at build time and during development. By default, prerendered pages are built using Cloudflare's `workerd` runtime to match the production environment as closely as possible. Set this option to `'node'` when your prerendered pages depend on Node.js APIs or NPM packages that are not compatible with `workerd`: For example, if a prerendered page reads from the file system using `node:fs`, set `prerenderEnvironment` to `'node'`. On-demand rendered pages are unaffected by this option and always run in `workerd`. ## Cloudflare runtime The Cloudflare runtime gives you access to environment variables, bindings to Cloudflare resources, and other Cloudflare-specific APIs. ### Environment variables and bindings Environment variables and bindings are defined in your `wrangler.jsonc` configuration file. 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`: ``` { "vars": { "MY_VARIABLE": "test", }, } ``` [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. To define `secrets`, add them through the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/) rather than in your Wrangler config file: ``` npx wrangler secret put ``` To set secrets for local development, add a `.dev.vars` file to the root of the Astro project: ``` DB_PASSWORD=myPassword ``` Cloudflare environment variables and secrets can be imported from `"cloudflare:workers"`: ``` --- const myVariable = env.MY_VARIABLE; const myKVNamespace = env.MY_KV; --- ``` They are also compatible with the [`astro:env` API](/en/guides/environment-variables/#type-safe-environment-variables): See the [list of all supported bindings](https://developers.cloudflare.com/workers/wrangler/api/#supported-bindings) in the Cloudflare documentation. ### The `cf` object 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: ``` --- const cf = Astro.request.cf; const country = cf?.country; --- ``` ### Execution context 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. ``` --- const cfContext = Astro.locals.cfContext; cfContext.exports.Greeter.greet('Astro'); cfContext.waitUntil(someAsyncOperation()); --- ``` ### Typing `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. Run `wrangler types` every time you change your configuration files (e.g. `wrangler.jsonc`, `.dev.vars`). :::note The following example shows a script configuration to run `wrangler types` automatically before other commands: ``` { "scripts": { "dev": "wrangler types && astro dev", "start": "wrangler types && astro dev", "build": "wrangler types && astro check && astro build", "preview": "wrangler types && astro preview", "astro": "astro" } } ``` ::: ## Cloudflare Platform ### Headers 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 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 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/). ## 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. 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. ``` --- --- 🛒 {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: - `.wasm` or `.wasm?module`: exports a [`WebAssembly.Module`](https://developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/Module) that can then be instantiated - `.bin`: exports an [`ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) of the raw binary contents of the file - `.txt`: exports a string of the file contents All module types export a single default value. Modules can be imported both from server-side rendered pages, or from prerendered pages for static site generation. The following is an example of importing a Wasm module that then responds to requests by adding the request's number parameters together. ``` // Import the WebAssembly module // Instantiate first in order to use it const addModule: any = new WebAssembly.Instance(mod); const b = Number.parseInt(context.params.b); return new Response(`${addModule.exports.add(a, b)}`); } ``` While this example is trivial, Wasm can be used to accelerate computationally intensive operations which do not involve significant I/O such as embedding an image processing library, or embedding a small pre-indexed database for search over a read-only dataset. ## Node.js compatibility 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 enable Node.js compatibility, add the `nodejs_compat` flag to your Wrangler configuration: ``` { "compatibility_flags": ["nodejs_compat"], } ``` Then use the `node:*` import syntax in your server-side code: ## Using advanced routing The Cloudflare adapter provides companion handlers that apply Cloudflare-specific setup to your [advanced routing pipeline](/en/guides/routing/#advanced-routing). These handlers configure session KV binding injection, static asset serving via the `ASSETS` binding, `Astro.locals.cfContext`, client address from the `cf-connecting-ip` header, `waitUntil`, and prerendered error page fetching. You can use the [`astro/fetch`](/en/reference/modules/astro-fetch/) and [`astro/hono`](/en/reference/modules/astro-hono/) APIs from `src/fetch.ts` on Cloudflare without these handlers. The adapter's default entrypoint takes care of Cloudflare-specific setup for you. These companion handlers are useful when you already have a [custom worker entrypoint](https://developers.cloudflare.com/workers/wrangler/configuration/#inheritable-keys) (`src/worker.ts`), for example, to export a Durable Object, and want to use the advanced routing APIs directly from that file instead. When using these handlers in your worker entrypoint, they replace the functionality of the adapter's default handler, so you should not use both at the same time. Place the Cloudflare handler before other Astro handlers so that bindings and locals are available to the rest of the pipeline. ### With the Fetch API For use with [`astro/fetch`](/en/reference/modules/astro-fetch/). The `cf()` function imported from `@astrojs/cloudflare/fetch` receives a [`FetchState`](/en/reference/modules/astro-fetch/#fetchstate), the Cloudflare `env`, and the `ExecutionContext`. It returns a `Response` for static asset hits, or `undefined` when the request should continue to Astro rendering: ``` const asset = await cf(state, env, ctx); if (asset) return asset; return astro(state); }, }; ``` ### With Hono For use with [`astro/hono`](/en/reference/modules/astro-hono/). The `cf()` function imported from `@astrojs/cloudflare/hono` returns a Hono middleware that reads `env` and `executionCtx` from the Hono context automatically: ``` const app = new Hono<{ Bindings: Env }>(); app.use(cf()); app.use(actions()); app.use(middleware()); app.use(pages()); app.use(i18n()); ``` ## 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. ### New: `prerenderEnvironment` option In Astro 6, prerendered pages now run in Cloudflare's `workerd` runtime by default during development and build. Previously, these pages always ran in Node.js. If your prerendered pages depend on Node.js APIs (for example `node:fs`) or NPM packages that are not compatible with `workerd`, set `prerenderEnvironment: 'node'` in your Cloudflare adapter config to restore the previous behavior for prerendering. On-demand rendered pages are not affected by this option and continue to run in `workerd`. See [`prerenderEnvironment`](#prerenderenvironment) for configuration details. ### Some dependencies might need to be pre-compiled The new workerd environment does not support CommonJS syntax, including Node.js specific syntax such as `require` and `module.exports`. This means that some of your project dependencies may throw errors in the development server or during the build. If you have control over the dependency, you can create a Vite plugin and pre-compile the dependency using the `optimizeDeps.include` option. For example, you can create a Vite plugin to pre-compile the dependency `postcss` in order to use the Expressive Code syntax highlighter: ``` function noExternalPlugin() { return { name: "optimize-dependencies", configEnvironment(environment) { // We're only interested in server environments if (environment !== 'client') { return { optimizeDeps: { include: [ "postcss" // Or you can use this syntax if you don't depend directly on a dependency // "expressive-code > postcss" ] } } } } } } ``` ### 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: ``` { "main": "dist/_worker.js/index.js", "main": "@astrojs/cloudflare/entrypoints/server", "name": "my-astro-app", // ... rest of config } ``` This single entrypoint handles both `astro dev` and production deployments. ### 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: ``` const { env } = Astro.locals.runtime; ``` **Accessing the `cf` object:** Previously, the `cf` object was accessed through `Astro.locals.runtime.cf`. Now access it directly from the request: ``` const { cf } = Astro.locals.runtime; const cf = Astro.request.cf; ``` **Accessing the caches API:** Previously, the caches API was accessed through `Astro.locals.runtime.caches`. Now use the global `caches` object directly: ``` 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`: ``` 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: ``` { "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: ``` { "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: ``` { "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: ``` 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: ### 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. ### Removed: Cloudflare Pages support The Astro Cloudflare adapter no longer supports deployment on Cloudflare Pages. For the best experience and feature support, you should migrate to Cloudflare Workers. 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. ### 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. ### Changed: Deploy to Cloudflare Environment In Astro 5.x, you could build your Astro project once and deploy it to a specific Cloudflare environment with `wrangler deploy --env some-env`. Since Astro 6.0, the integration relies on the Cloudflare Vite plugin and this behavior has changed. The environment is now determined during the build phase. Therefore, you must build your project separately for each environment. To deploy to a specific Cloudflare environment, prefix your command with the `CLOUDFLARE_ENV` variable. For example, the command `CLOUDFLARE_ENV=some-env astro build && wrangler deploy` will build your Astro project and deploy it with Wrangler using the `some-env` environment. Learn how to update your [Cloudflare environments](https://developers.cloudflare.com/workers/vite-plugin/reference/cloudflare-environments/) in the [Migrate from wrangler dev guide](https://developers.cloudflare.com/workers/vite-plugin/reference/migrating-from-wrangler-dev/#cloudflare-environments). ## [@astrojs/db](https://docharvest.github.io/docs/astro/guides/integrations-guide/db/) Contents astro @astrojs/db Astro @astrojs/db :::caution\[Removed\] The Astro DB integration was deprecated in v6.4 and has been removed since Astro v7.0. If you were using this integration in your project, we recommend migrating to a third-party library for database functionality. See the [Astro v7.0 migration guide](/en/guides/upgrade-to/v7/#removed-astrojsdb) for more details and recommendations on how to update your project. ::: ## [@deno/astro-adapter](https://docharvest.github.io/docs/astro/guides/integrations-guide/deno/) Contents astro @deno/astro-adapter Astro @deno/astro-adapter The Deno adapter allows Astro to deploy your SSR site to Deno targets including Deno Deploy. The Deno adapter was previously maintained by Astro but now is maintained by Deno directly. Usage is now documented [in the Deno adapter repository](https://github.com/denoland/deno-astro-adapter). If you are currently using this Astro adapter, you will need to migrate to the new Deno version or to [add another adapter](/en/guides/on-demand-rendering/) to continue using SSR in your project. ## [Lit](https://docharvest.github.io/docs/astro/guides/integrations-guide/lit/) Contents astro Lit Astro Lit :::caution\[Deprecated\] This Astro integration to enable on-demand rendering and client-side hydration for your [Lit](https://lit.dev/) custom elements was deprecated in Astro 5.0. ::: You can continue to use Lit for client components by adding a client-side script tag. For example: ``` ... ``` If you're interested in maintaining a Lit integration yourself, you may wish to use the [last published version of `@astrojs/lit`](https://github.com/withastro/astro/tree/astro%404.13.0/packages/integrations/lit) as a starting point and upgrade the relevant packages. ## [@astrojs/markdoc](https://docharvest.github.io/docs/astro/guides/integrations-guide/markdoc/) Contents astro @astrojs/markdoc Astro @astrojs/markdoc This **[Astro integration](/en/guides/integrations/)** enables the usage of [Markdoc](https://markdoc.dev/) to create components, pages, and content collection entries. ## Why Markdoc? Markdoc allows you to enhance your Markdown with [Astro components](/en/basics/astro-components/). If you have existing content authored in Markdoc, this integration allows you to bring those files to your Astro project using content collections. ## 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. Run one of the following commands in a new terminal window. \`\`\`sh npx astro add markdoc \`\`\` \`\`\`sh pnpm astro add markdoc \`\`\` \`\`\`sh yarn astro add markdoc \`\`\` If you run into any issues, [feel free to report them to us on GitHub](https://github.com/withastro/astro/issues) and try the manual installation steps below. ### Manual Install First, install the `@astrojs/markdoc` package: \`\`\`sh npm install @astrojs/markdoc \`\`\` \`\`\`sh pnpm add @astrojs/markdoc \`\`\` \`\`\`sh yarn add @astrojs/markdoc \`\`\` Then, apply the integration to your `astro.config.*` file using the `integrations` property: ### VS Code Editor Integration If you are using VS Code, there is an official [Markdoc language extension](https://marketplace.visualstudio.com/items?itemName=Stripe.markdoc-language-support) that includes syntax highlighting and autocomplete for configured tags. [See the language server on GitHub](https://github.com/markdoc/language-server.git) for more information. To set up the extension, create a `markdoc.config.json` file in the project root with following content: ``` [ { "id": "my-site", "path": "src/content", "schema": { "path": "markdoc.config.mjs", "type": "esm", "property": "default", "watch": true } } ] ``` Set `markdoc.config.mjs` as your configuration file with the `schema` object, and define where your Markdoc files are stored using the `path` property. Since Markdoc is specific to content collections, you can use `src/content`. ## Usage Markdoc files can only be used within content collections. Add entries to any content collection using the `.mdoc` extension: \- src/ - content/ - docs/ - why-markdoc.mdoc - quick-start.mdoc Then, [query and display your posts and collections](/en/guides/content-collections/#querying-build-time-collections): ``` --- const entry = await getEntry('docs', 'why-markdoc'); const { Content } = await render(entry); ---

{entry.data.title}

``` See the [Astro Content Collection docs](/en/guides/content-collections/) for more information. ## Pass Markdoc variables You may need to pass [variables](https://markdoc.dev/docs/variables) to your content. This is useful when passing SSR parameters like A/B tests. Variables can be passed as props via the `Content` component: ``` --- const entry = await getEntry('docs', 'why-markdoc'); const { Content } = await render(entry); --- ``` Now, `abTestGroup` is available as a variable in `docs/why-markdoc.mdoc`: ``` {% if $abTestGroup === 'image-optimization-lover' %} Let me tell you about image optimization... {% /if %} ``` To make a variable global to all Markdoc files, you can use the `variables` attribute from your `markdoc.config.mjs|ts`: ### Access frontmatter from your Markdoc content To access frontmatter, you can pass the entry `data` property as a variable where you render your content: ``` --- const entry = await getEntry('docs', 'why-markdoc'); const { Content } = await render(entry); --- ``` This can now be accessed as `$frontmatter` in your Markdoc. ## Render components `@astrojs/markdoc` offers configuration options to use all of Markdoc's features and connect UI components to your content. ### Use Astro components as Markdoc tags You can configure [Markdoc tags](https://markdoc.dev/docs/tags) that map to `.astro` components. You can add a new tag by creating a `markdoc.config.mjs|ts` file at the root of your project and configuring the `tag` attribute. This example renders an `Aside` component, and allows a `type` prop to be passed as a string: This component can now be used in your Markdoc files with the `{% aside %}` tag. Children will be passed to your component's default slot: ``` # Welcome to Markdoc 👋 {% aside type="tip" %} Use tags like this fancy "aside" to add some _flair_ to your docs. {% /aside %} ``` ### Use client-side UI components Tags and nodes are restricted to `.astro` files. To embed client-side UI components in Markdoc, [use a wrapper `.astro` component that renders a framework component](/en/guides/framework-components/#nesting-framework-components) with your desired `client:` directive. This example wraps a React `Aside.tsx` component with a `ClientAside.astro` component: ``` --- ---