Skip to content

Instantly share code, notes, and snippets.

@ckng
Created May 3, 2025 17:23
Show Gist options
  • Save ckng/8e054e03c79447559c218775c5902f00 to your computer and use it in GitHub Desktop.
Save ckng/8e054e03c79447559c218775c5902f00 to your computer and use it in GitHub Desktop.
Astro 5 docs llms.txt
This file has been truncated, but you can view the full file.
Directory Structure:
└── ./
├── src
│ └── content
│ └── docs
│ └── en
│ ├── basics
│ │ ├── astro-components.mdx
│ │ ├── astro-pages.mdx
│ │ ├── layouts.mdx
│ │ └── project-structure.mdx
│ ├── concepts
│ │ ├── islands.mdx
│ │ └── why-astro.mdx
│ ├── guides
│ │ ├── backend
│ │ │ ├── appwriteio.mdx
│ │ │ ├── google-firebase.mdx
│ │ │ ├── index.mdx
│ │ │ ├── neon.mdx
│ │ │ ├── sentry.mdx
│ │ │ ├── supabase.mdx
│ │ │ ├── turso.mdx
│ │ │ └── xata.mdx
│ │ ├── cms
│ │ │ ├── apostrophecms.mdx
│ │ │ ├── builderio.mdx
│ │ │ ├── buttercms.mdx
│ │ │ ├── caisy.mdx
│ │ │ ├── cloudcannon.mdx
│ │ │ ├── contentful.mdx
│ │ │ ├── cosmic.mdx
│ │ │ ├── craft-cms.mdx
│ │ │ ├── crystallize.mdx
│ │ │ ├── datocms.mdx
│ │ │ ├── decap-cms.mdx
│ │ │ ├── directus.mdx
│ │ │ ├── drupal.mdx
│ │ │ ├── flotiq.mdx
│ │ │ ├── frontmatter-cms.mdx
│ │ │ ├── ghost.mdx
│ │ │ ├── gitcms.mdx
│ │ │ ├── hashnode.mdx
│ │ │ ├── hygraph.mdx
│ │ │ ├── index.mdx
│ │ │ ├── keystatic.mdx
│ │ │ ├── keystonejs.mdx
│ │ │ ├── kontent-ai.mdx
│ │ │ ├── microcms.mdx
│ │ │ ├── payload.mdx
│ │ │ ├── preprcms.mdx
│ │ │ ├── prismic.mdx
│ │ │ ├── sanity.mdx
│ │ │ ├── sitecore.mdx
│ │ │ ├── spinal.mdx
│ │ │ ├── statamic.mdx
│ │ │ ├── storyblok.mdx
│ │ │ ├── strapi.mdx
│ │ │ ├── studiocms.mdx
│ │ │ ├── tina-cms.mdx
│ │ │ ├── umbraco.mdx
│ │ │ └── wordpress.mdx
│ │ ├── deploy
│ │ │ ├── aws.mdx
│ │ │ ├── azion.mdx
│ │ │ ├── buddy.mdx
│ │ │ ├── cleavr.mdx
│ │ │ ├── clever-cloud.mdx
│ │ │ ├── cloudflare.mdx
│ │ │ ├── deno.mdx
│ │ │ ├── edgio.mdx
│ │ │ ├── fleek.mdx
│ │ │ ├── flightcontrol.mdx
│ │ │ ├── flyio.mdx
│ │ │ ├── github.mdx
│ │ │ ├── gitlab.mdx
│ │ │ ├── google-cloud.mdx
│ │ │ ├── google-firebase.mdx
│ │ │ ├── heroku.mdx
│ │ │ ├── index.mdx
│ │ │ ├── kinsta.mdx
│ │ │ ├── microsoft-azure.mdx
│ │ │ ├── netlify.mdx
│ │ │ ├── render.mdx
│ │ │ ├── sst.mdx
│ │ │ ├── stormkit.mdx
│ │ │ ├── surge.mdx
│ │ │ ├── vercel.mdx
│ │ │ ├── zeabur.mdx
│ │ │ └── zerops.mdx
│ │ ├── integrations-guide
│ │ │ ├── alpinejs.mdx
│ │ │ ├── cloudflare.mdx
│ │ │ ├── db.mdx
│ │ │ ├── deno.mdx
│ │ │ ├── index.mdx
│ │ │ ├── lit.mdx
│ │ │ ├── markdoc.mdx
│ │ │ ├── mdx.mdx
│ │ │ ├── netlify.mdx
│ │ │ ├── node.mdx
│ │ │ ├── partytown.mdx
│ │ │ ├── preact.mdx
│ │ │ ├── prefetch.mdx
│ │ │ ├── react.mdx
│ │ │ ├── sitemap.mdx
│ │ │ ├── solid-js.mdx
│ │ │ ├── svelte.mdx
│ │ │ ├── tailwind.mdx
│ │ │ ├── vercel.mdx
│ │ │ └── vue.mdx
│ │ ├── media
│ │ │ ├── cloudinary.mdx
│ │ │ └── index.mdx
│ │ ├── migrate-to-astro
│ │ │ ├── from-create-react-app.mdx
│ │ │ ├── from-docusaurus.mdx
│ │ │ ├── from-eleventy.mdx
│ │ │ ├── from-gatsby.mdx
│ │ │ ├── from-gitbook.mdx
│ │ │ ├── from-gridsome.mdx
│ │ │ ├── from-hugo.mdx
│ │ │ ├── from-jekyll.mdx
│ │ │ ├── from-nextjs.mdx
│ │ │ ├── from-nuxtjs.mdx
│ │ │ ├── from-pelican.mdx
│ │ │ ├── from-sveltekit.mdx
│ │ │ ├── from-vuepress.mdx
│ │ │ ├── from-wordpress.mdx
│ │ │ └── index.mdx
│ │ ├── upgrade-to
│ │ │ ├── v1.mdx
│ │ │ ├── v2.mdx
│ │ │ ├── v3.mdx
│ │ │ ├── v4.mdx
│ │ │ └── v5.mdx
│ │ ├── actions.mdx
│ │ ├── astro-db.mdx
│ │ ├── authentication.mdx
│ │ ├── client-side-scripts.mdx
│ │ ├── configuring-astro.mdx
│ │ ├── content-collections.mdx
│ │ ├── data-fetching.mdx
│ │ ├── dev-toolbar.mdx
│ │ ├── ecommerce.mdx
│ │ ├── endpoints.mdx
│ │ ├── environment-variables.mdx
│ │ ├── fonts.mdx
│ │ ├── framework-components.mdx
│ │ ├── images.mdx
│ │ ├── imports.mdx
│ │ ├── internationalization.mdx
│ │ ├── markdown-content.mdx
│ │ ├── middleware.mdx
│ │ ├── on-demand-rendering.mdx
│ │ ├── prefetch.mdx
│ │ ├── routing.mdx
│ │ ├── server-islands.mdx
│ │ ├── sessions.mdx
│ │ ├── styling.mdx
│ │ ├── syntax-highlighting.mdx
│ │ ├── testing.mdx
│ │ ├── troubleshooting.mdx
│ │ ├── typescript.mdx
│ │ └── view-transitions.mdx
│ ├── recipes
│ │ ├── add-yaml-support.mdx
│ │ ├── analyze-bundle-size.mdx
│ │ ├── build-custom-img-component.mdx
│ │ ├── build-forms-api.mdx
│ │ ├── build-forms.mdx
│ │ ├── bun.mdx
│ │ ├── call-endpoints.mdx
│ │ ├── captcha.mdx
│ │ ├── docker.mdx
│ │ ├── dynamically-importing-images.mdx
│ │ ├── external-links.mdx
│ │ ├── i18n.mdx
│ │ ├── index.mdx
│ │ ├── making-toolbar-apps.mdx
│ │ ├── modified-time.mdx
│ │ ├── reading-time.mdx
│ │ ├── rss.mdx
│ │ ├── sharing-state-islands.mdx
│ │ ├── sharing-state.mdx
│ │ ├── streaming-improve-page-performance.mdx
│ │ └── tailwind-rendered-markdown.mdx
│ ├── reference
│ │ ├── errors
│ │ │ ├── action-called-from-server-error.mdx
│ │ │ ├── action-not-found-error.mdx
│ │ │ ├── action-query-string-invalid-error.mdx
│ │ │ ├── actions-cant-be-loaded.mdx
│ │ │ ├── actions-returned-invalid-data-error.mdx
│ │ │ ├── actions-used-with-for-get-error.mdx
│ │ │ ├── actions-without-server-output-error.mdx
│ │ │ ├── adapter-support-output-mismatch.mdx
│ │ │ ├── astro-glob-no-match.mdx
│ │ │ ├── astro-glob-used-outside.mdx
│ │ │ ├── astro-response-headers-reassigned.mdx
│ │ │ ├── cannot-extract-font-type.mdx
│ │ │ ├── cannot-fetch-font-file.mdx
│ │ │ ├── cannot-load-font-provider.mdx
│ │ │ ├── cant-render-page.mdx
│ │ │ ├── cant-use-astro-config-module-error.mdx
│ │ │ ├── client-address-not-available.mdx
│ │ │ ├── collection-does-not-exist-error.mdx
│ │ │ ├── config-legacy-key.mdx
│ │ │ ├── config-not-found.mdx
│ │ │ ├── content-collection-type-mismatch-error.mdx
│ │ │ ├── content-entry-data-error.mdx
│ │ │ ├── content-loader-invalid-data-error.mdx
│ │ │ ├── content-loader-returns-invalid-id.mdx
│ │ │ ├── content-schema-contains-slug-error.mdx
│ │ │ ├── could-not-transform-image.mdx
│ │ │ ├── csssyntax-error.mdx
│ │ │ ├── data-collection-entry-parse-error.mdx
│ │ │ ├── duplicate-content-entry-slug-error.mdx
│ │ │ ├── endpoint-did-not-return-aresponse.mdx
│ │ │ ├── env-invalid-variable.mdx
│ │ │ ├── env-invalid-variables.mdx
│ │ │ ├── env-unsupported-get-secret.mdx
│ │ │ ├── expected-image-options.mdx
│ │ │ ├── expected-image.mdx
│ │ │ ├── expected-not-esmimage.mdx
│ │ │ ├── experimental-fonts-not-enabled.mdx
│ │ │ ├── failed-to-fetch-remote-image-dimensions.mdx
│ │ │ ├── failed-to-find-page-map-ssr.mdx
│ │ │ ├── failed-to-load-module-ssr.mdx
│ │ │ ├── font-family-not-found.mdx
│ │ │ ├── forbidden-rewrite.mdx
│ │ │ ├── generate-content-types-error.mdx
│ │ │ ├── get-entry-deprecation-error.mdx
│ │ │ ├── get-static-paths-expected-params.mdx
│ │ │ ├── get-static-paths-invalid-route-param.mdx
│ │ │ ├── get-static-paths-removed-rsshelper.mdx
│ │ │ ├── get-static-paths-required.mdx
│ │ │ ├── i18n-no-locale-found-in-path.mdx
│ │ │ ├── i18n-not-enabled.mdx
│ │ │ ├── image-missing-alt.mdx
│ │ │ ├── image-not-found.mdx
│ │ │ ├── incompatible-descriptor-options.mdx
│ │ │ ├── incorrect-strategy-for-i18n.mdx
│ │ │ ├── invalid-component-args.mdx
│ │ │ ├── invalid-content-entry-data-error.mdx
│ │ │ ├── invalid-content-entry-frontmatter-error.mdx
│ │ │ ├── invalid-content-entry-slug-error.mdx
│ │ │ ├── invalid-dynamic-route.mdx
│ │ │ ├── invalid-frontmatter-injection-error.mdx
│ │ │ ├── invalid-get-static-path-param.mdx
│ │ │ ├── invalid-get-static-paths-entry.mdx
│ │ │ ├── invalid-get-static-paths-return.mdx
│ │ │ ├── invalid-glob.mdx
│ │ │ ├── invalid-image-service.mdx
│ │ │ ├── invalid-prerender-export.mdx
│ │ │ ├── invalid-rewrite404.mdx
│ │ │ ├── local-image-used-wrongly.mdx
│ │ │ ├── locals-not-an-object.mdx
│ │ │ ├── locals-not-serializable.mdx
│ │ │ ├── locals-reassigned.mdx
│ │ │ ├── markdown-content-schema-validation-error.mdx
│ │ │ ├── markdown-frontmatter-parse-error.mdx
│ │ │ ├── markdown-image-not-found.mdx
│ │ │ ├── mdx-integration-missing-error.mdx
│ │ │ ├── middleware-cant-be-loaded.mdx
│ │ │ ├── middleware-no-data-or-next-called.mdx
│ │ │ ├── middleware-not-aresponse.mdx
│ │ │ ├── missing-image-dimension.mdx
│ │ │ ├── missing-index-for-internationalization.mdx
│ │ │ ├── missing-locale.mdx
│ │ │ ├── missing-media-query-directive.mdx
│ │ │ ├── missing-middleware-for-internationalization.mdx
│ │ │ ├── missing-sharp.mdx
│ │ │ ├── mixed-content-data-collection-error.mdx
│ │ │ ├── no-adapter-installed-server-islands.mdx
│ │ │ ├── no-adapter-installed.mdx
│ │ │ ├── no-client-entrypoint.mdx
│ │ │ ├── no-client-only-hint.mdx
│ │ │ ├── no-image-metadata.mdx
│ │ │ ├── no-matching-import.mdx
│ │ │ ├── no-matching-renderer.mdx
│ │ │ ├── no-matching-static-path-found.mdx
│ │ │ ├── no-prerendered-routes-with-domains.mdx
│ │ │ ├── only-response-can-be-returned.mdx
│ │ │ ├── page-number-param-not-found.mdx
│ │ │ ├── prerender-client-address-not-available.mdx
│ │ │ ├── prerender-dynamic-endpoint-path-collide.mdx
│ │ │ ├── redirect-with-no-location.mdx
│ │ │ ├── render-undefined-entry-error.mdx
│ │ │ ├── reserved-slot-name.mdx
│ │ │ ├── response-sent-error.mdx
│ │ │ ├── rewrite-encountered-an-error.mdx
│ │ │ ├── rewrite-with-body-used.mdx
│ │ │ ├── route-not-found.mdx
│ │ │ ├── server-only-module.mdx
│ │ │ ├── session-config-missing-error.mdx
│ │ │ ├── session-config-without-flag-error.mdx
│ │ │ ├── session-storage-init-error.mdx
│ │ │ ├── session-storage-save-error.mdx
│ │ │ ├── session-without-supported-adapter-output-error.mdx
│ │ │ ├── static-client-address-not-available.mdx
│ │ │ ├── static-redirect-not-available.mdx
│ │ │ ├── unhandled-rejection.mdx
│ │ │ ├── unknown-clierror.mdx
│ │ │ ├── unknown-compiler-error.mdx
│ │ │ ├── unknown-config-error.mdx
│ │ │ ├── unknown-content-collection-error.mdx
│ │ │ ├── unknown-csserror.mdx
│ │ │ ├── unknown-filesystem-error.mdx
│ │ │ ├── unknown-markdown-error.mdx
│ │ │ ├── unknown-vite-error.mdx
│ │ │ ├── unsupported-config-transform-error.mdx
│ │ │ ├── unsupported-external-redirect.mdx
│ │ │ ├── unsupported-image-conversion.mdx
│ │ │ └── unsupported-image-format.mdx
│ │ ├── experimental-flags
│ │ │ ├── client-prerender.mdx
│ │ │ ├── content-intellisense.mdx
│ │ │ ├── fonts.mdx
│ │ │ ├── heading-id-compat.mdx
│ │ │ ├── index.mdx
│ │ │ ├── preserve-scripts-order.mdx
│ │ │ └── responsive-images.mdx
│ │ ├── modules
│ │ │ ├── astro-actions.mdx
│ │ │ ├── astro-assets.mdx
│ │ │ ├── astro-config.mdx
│ │ │ ├── astro-content.mdx
│ │ │ ├── astro-env.mdx
│ │ │ ├── astro-i18n.mdx
│ │ │ ├── astro-middleware.mdx
│ │ │ └── astro-transitions.mdx
│ │ ├── adapter-reference.mdx
│ │ ├── api-reference.mdx
│ │ ├── astro-syntax.mdx
│ │ ├── cli-reference.mdx
│ │ ├── configuration-reference.mdx
│ │ ├── container-reference.mdx
│ │ ├── content-loader-reference.mdx
│ │ ├── dev-toolbar-app-reference.mdx
│ │ ├── directives-reference.mdx
│ │ ├── error-reference.mdx
│ │ ├── image-service-reference.mdx
│ │ ├── integrations-reference.mdx
│ │ ├── legacy-flags.mdx
│ │ ├── programmatic-reference.mdx
│ │ ├── publish-to-npm.mdx
│ │ └── routing-reference.mdx
│ ├── tutorial
│ │ ├── 0-introduction
│ │ │ ├── 1.mdx
│ │ │ └── index.mdx
│ │ ├── 1-setup
│ │ │ ├── 1.mdx
│ │ │ ├── 2.mdx
│ │ │ ├── 3.mdx
│ │ │ ├── 4.mdx
│ │ │ ├── 5.mdx
│ │ │ └── index.mdx
│ │ ├── 2-pages
│ │ │ ├── 1.mdx
│ │ │ ├── 2.mdx
│ │ │ ├── 3.mdx
│ │ │ ├── 4.mdx
│ │ │ ├── 5.mdx
│ │ │ └── index.mdx
│ │ ├── 3-components
│ │ │ ├── 1.mdx
│ │ │ ├── 2.mdx
│ │ │ ├── 3.mdx
│ │ │ ├── 4.mdx
│ │ │ └── index.mdx
│ │ ├── 4-layouts
│ │ │ ├── 1.mdx
│ │ │ ├── 2.mdx
│ │ │ ├── 3.mdx
│ │ │ └── index.mdx
│ │ ├── 5-astro-api
│ │ │ ├── 1.mdx
│ │ │ ├── 2.mdx
│ │ │ ├── 3.mdx
│ │ │ ├── 4.mdx
│ │ │ └── index.mdx
│ │ └── 6-islands
│ │ ├── 1.mdx
│ │ ├── 2.mdx
│ │ ├── 3.mdx
│ │ ├── 4.mdx
│ │ └── index.mdx
│ ├── contribute.mdx
│ ├── develop-and-build.mdx
│ ├── editor-setup.mdx
│ ├── getting-started.mdx
│ ├── install-and-setup.mdx
│ └── upgrade-astro.mdx
└── README.md
---
File: /src/content/docs/en/basics/astro-components.mdx
---
---
title: Components
description: An introduction to Astro components.
i18nReady: true
---
import ReadMore from '~/components/ReadMore.astro';
**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.
<ReadMore>Learn more in the [Astro syntax reference](/en/reference/astro-syntax/).</ReadMore>
:::
Astro components are extremely flexible. An Astro component can be as small as a snippet of HTML, like a collection of common `<meta>` 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 `<script>` tags](/en/guides/client-side-scripts/) or [UI Framework components](/en/guides/framework-components/#hydrating-interactive-components) as "client islands".
For components that need to render personalized or dynamic content, you can defer their server rendering by adding a [server directive](/en/reference/directives-reference/#server-directives). These "server islands" will render their content when it is available, without delaying the entire page load.
## Component Structure
An Astro component is made up of two main parts: the **Component Script** and the **Component Template**. Each part performs a different job, but together they provide a framework that is both easy to use and expressive enough to handle whatever you might want to build.
```astro title="src/components/EmptyComponent.astro"
---
// Component Script (JavaScript)
---
<!-- Component Template (HTML + JS Expressions) -->
```
### The Component Script
Astro uses a code fence (`---`) to identify the component script in your Astro component. If you've ever written Markdown before, you may already be familiar with a similar concept called *frontmatter.* Astro's idea of a component script was directly inspired by this concept.
You can use the component script to write any JavaScript code that you need to render your template. This can include:
- importing other Astro components
- importing other framework components, like React
- importing data, like a JSON file
- fetching content from an API or database
- creating variables that you will reference in your template
```astro title="src/components/MyComponent.astro"
---
import SomeAstroComponent from '../components/SomeAstroComponent.astro';
import SomeReactComponent from '../components/SomeReactComponent.jsx';
import someData from '../data/pokemon.json';
// Access passed-in component props, like `<X title="Hello, World" />`
const { title } = Astro.props;
// Fetch external data, even from a private API or database
const data = await fetch('SOME_SECRET_API_URL/users').then(r => r.json());
---
<!-- Your template here! -->
```
The code fence is designed to guarantee that the JavaScript that you write in it is "fenced in." It won't escape into your frontend application, or fall into your user's hands. You can safely write code here that is expensive or sensitive (like a call to your private database) without worrying about it ever ending up in your user's browser.
:::note
The Astro component script is TypeScript, which allows you to add additional syntax to JavaScript for editor tooling, and error checking.
<ReadMore>Read more about Astro's [built-in TypeScript support](/en/guides/typescript/).</ReadMore>
:::
### The Component Template
The component template is below the code fence and determines the HTML output of your component.
If you write plain HTML here, your component will render that HTML in any Astro page it is imported and used.
However, [Astro's component template syntax](/en/reference/astro-syntax/) also supports **JavaScript expressions**, Astro [`<style>`](/en/guides/styling/#styling-in-astro) and [`<script>`](/en/guides/client-side-scripts/#using-script-in-astro) tags, **imported components**, and [**special Astro directives**](/en/reference/directives-reference/). Data and values defined in the component script can be used in the component template to produce dynamically-created HTML.
```astro title="src/components/MyFavoritePokemon.astro"
---
// Your component script here!
import Banner from '../components/Banner.astro';
import Avatar from '../components/Avatar.astro';
import ReactPokemonComponent from '../components/ReactPokemonComponent.jsx';
const myFavoritePokemon = [/* ... */];
const { title } = Astro.props;
---
<!-- HTML comments supported! -->
{/* JS comment syntax is also valid! */}
<Banner />
<h1>Hello, world!</h1>
<!-- Use props and other variables from the component script: -->
<p>{title}</p>
<!-- Delay component rendering and provide fallback loading content: -->
<Avatar server:defer>
<svg slot="fallback" class="generic-avatar" transition:name="avatar">...</svg>
</Avatar>
<!-- Include other UI framework components with a `client:` directive to hydrate: -->
<ReactPokemonComponent client:visible />
<!-- Mix HTML with JavaScript expressions, similar to JSX: -->
<ul>
{myFavoritePokemon.map((data) => <li>{data.name}</li>)}
</ul>
<!-- Use a template directive to build class names from multiple strings or even objects! -->
<p class:list={["add", "dynamic", { classNames: true }]} />
```
## Component-based design
Components are designed to be **reusable** and **composable**. You can use components inside of other components to build more and more advanced UI. For example, a `Button` component could be used to create a `ButtonGroup` component:
```astro title="src/components/ButtonGroup.astro"
---
import Button from './Button.astro';
---
<div>
<Button title="Button 1" />
<Button title="Button 2" />
<Button title="Button 3" />
</div>
```
## Component Props
An Astro component can define and accept props. These props then become available to the component template for rendering HTML. Props are available on the `Astro.props` global in your frontmatter script.
Here is an example of a component that receives a `greeting` prop and a `name` prop. Notice that the props to be received are destructured from the global `Astro.props` object.
```astro "Astro.props"
---
// src/components/GreetingHeadline.astro
// Usage: <GreetingHeadline greeting="Howdy" name="Partner" />
const { greeting, name } = Astro.props;
---
<h2>{greeting}, {name}!</h2>
```
This component, when imported and rendered in other Astro components, layouts or pages, can pass these props as attributes:
```astro /(\w+)=\S+/
---
// src/components/GreetingCard.astro
import GreetingHeadline from './GreetingHeadline.astro';
const name = 'Astro';
---
<h1>Greeting Card</h1>
<GreetingHeadline greeting="Hi" name={name} />
<p>I hope you have a wonderful day!</p>
```
You can also define your props with TypeScript with a `Props` type interface. Astro will automatically pick up the `Props` interface in your frontmatter and give type warnings/errors. These props can also be given default values when destructured from `Astro.props`.
```astro ins={3-6}
---
// src/components/GreetingHeadline.astro
interface Props {
name: string;
greeting?: string;
}
const { greeting = "Hello", name } = Astro.props;
---
<h2>{greeting}, {name}!</h2>
```
Component props can be given default values to use when none are provided.
```astro ins="= \"Hello\"" ins="= \"Astronaut\""
---
// src/components/GreetingHeadline.astro
const { greeting = "Hello", name = "Astronaut" } = Astro.props;
---
<h2>{greeting}, {name}!</h2>
```
## Slots
The `<slot />` element is a placeholder for external HTML content, allowing you to inject (or "slot") child elements from other files into your component template.
By default, all child elements passed to a component will be rendered in its `<slot />`.
:::note
Unlike _props_, which are attributes passed to an Astro component available for use throughout your component with `Astro.props`, _slots_ render child HTML elements where they are written.
:::
```astro "<slot />"
---
// src/components/Wrapper.astro
import Header from './Header.astro';
import Logo from './Logo.astro';
import Footer from './Footer.astro';
const { title } = Astro.props;
---
<div id="content-wrapper">
<Header />
<Logo />
<h1>{title}</h1>
<slot /> <!-- children will go here -->
<Footer />
</div>
```
```astro {6-7}
---
// src/pages/fred.astro
import Wrapper from '../components/Wrapper.astro';
---
<Wrapper title="Fred's Page">
<h2>All about Fred</h2>
<p>Here is some stuff about Fred.</p>
</Wrapper>
```
This pattern is the basis of an [Astro layout component](/en/basics/layouts/): an entire page of HTML content can be “wrapped” with `<SomeLayoutComponent></SomeLayoutComponent>` tags and sent to the component to render inside of common page elements defined there.
### Named Slots
An Astro component can also have named slots. This allows you to pass only HTML elements with the corresponding slot name into a slot's location.
Slots are named using the `name` attribute:
```astro /<slot .*?/>/
---
// src/components/Wrapper.astro
import Header from './Header.astro';
import Logo from './Logo.astro';
import Footer from './Footer.astro';
const { title } = Astro.props;
---
<div id="content-wrapper">
<Header />
<!-- children with the `slot="after-header"` attribute will go here -->
<slot name="after-header" />
<Logo />
<h1>{title}</h1>
<!-- children without a `slot`, or with `slot="default"` attribute will go here -->
<slot />
<Footer />
<!-- children with the `slot="after-footer"` attribute will go here -->
<slot name="after-footer" />
</div>
```
To inject HTML content into a particular slot, use the `slot` attribute on any child element to specify the name of the slot. All other child elements of the component will be injected into the default (unnamed) `<slot />`.
```astro /slot=".*?"/
---
// src/pages/fred.astro
import Wrapper from '../components/Wrapper.astro';
---
<Wrapper title="Fred's Page">
<img src="https://my.photo/fred.jpg" slot="after-header" />
<h2>All about Fred</h2>
<p>Here is some stuff about Fred.</p>
<p slot="after-footer">Copyright 2022</p>
</Wrapper>
```
:::tip
Use a `slot="my-slot"` attribute on the child element that you want to pass through to a matching `<slot name="my-slot" />` placeholder in your component.
:::
To pass multiple HTML elements into a component's `<slot/>` placeholder without a wrapping `<div>`, use the `slot=""` attribute on [Astro's `<Fragment/>` component](/en/reference/astro-syntax/#fragments):
```astro title="src/components/CustomTable.astro" "<slot name="header"/>" "<slot name="body"/>"
---
// Create a custom table with named slot placeholders for header and body content
---
<table class="bg-white">
<thead class="sticky top-0 bg-white"><slot name="header" /></thead>
<tbody class="[&_tr:nth-child(odd)]:bg-gray-100"><slot name="body" /></tbody>
</table>
```
Inject multiple rows and columns of HTML content using a `slot=""` attribute to specify the `"header"` and `"body"` content. Individual HTML elements can also be styled:
```astro title="src/components/StockTable.astro" {5-7, 9-13} '<Fragment slot="header">' '<Fragment slot="body">'
---
import CustomTable from './CustomTable.astro';
---
<CustomTable>
<Fragment slot="header"> <!-- pass table header -->
<tr><th>Product name</th><th>Stock units</th></tr>
</Fragment>
<Fragment slot="body"> <!-- pass table body -->
<tr><td>Flip-flops</td><td>64</td></tr>
<tr><td>Boots</td><td>32</td></tr>
<tr><td>Sneakers</td><td class="text-red-500">0</td></tr>
</Fragment>
</CustomTable>
```
Note that named slots must be an immediate child of the component. You cannot pass named slots through nested elements.
:::tip
Named slots can also be passed to [UI framework components](/en/guides/framework-components/)!
:::
:::note
It is not possible to dynamically generate an Astro slot name, such as within a [map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) function. If this feature is needed within UI framework components, it might be best to generate these dynamic slots within the framework itself.
:::
### Fallback Content for Slots
Slots can also render **fallback content**. When there are no matching children passed to a slot, a `<slot />` element will render its own placeholder children.
```astro {14}
---
// src/components/Wrapper.astro
import Header from './Header.astro';
import Logo from './Logo.astro';
import Footer from './Footer.astro';
const { title } = Astro.props;
---
<div id="content-wrapper">
<Header />
<Logo />
<h1>{title}</h1>
<slot>
<p>This is my fallback content, if there is no child passed into slot</p>
</slot>
<Footer />
</div>
```
Fallback content will only be displayed when there are no matching elements with the `slot="name"` attribute being passed in to a named slot.
Astro will pass an empty slot when a slot element exists but has no content to pass. Fallback content cannot be used as a default when an empty slot is passed. Fallback content is only displayed when no slot element can be found.
### Transferring slots
Slots can be transferred to other components. For example, when creating nested layouts:
```astro title="src/layouts/BaseLayout.astro" {9,12}
---
---
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<slot name="head" />
</head>
<body>
<slot />
</body>
</html>
```
```astro {6,7}
// src/layouts/HomeLayout.astro
---
import BaseLayout from './BaseLayout.astro';
---
<BaseLayout>
<slot name="head" slot="head" />
<slot />
</BaseLayout>
```
:::note
Named slots can be transferred to another component using both the `name` and `slot` attributes on a `<slot />` tag.
:::
Now, the default and `head` slots passed to `HomeLayout` will be transferred to the `BaseLayout` parent.
```astro
// src/pages/index.astro
---
import HomeLayout from '../layouts/HomeLayout.astro';
---
<HomeLayout>
<title slot="head">Astro</title>
<h1>Astro</h1>
</HomeLayout>
```
## HTML Components
Astro supports importing and using `.html` files as components or placing these files within the `src/pages/` subdirectory as pages. You may want to use HTML components if you're reusing code from an existing site built without a framework, or if you want to ensure that your component has no dynamic features.
HTML components must contain only valid HTML, and therefore lack key Astro component features:
- They don't support frontmatter, server-side imports, or dynamic expressions.
- Any `<script>` tags are left unbundled, treated as if they had `is:inline`.
- They can only [reference assets that are in the `public/` folder](/en/basics/project-structure/#public).
:::note
A [`<slot />` element](/en/basics/astro-components/#slots) inside an HTML component will work as it would in an Astro component. In order to use the [HTML Web Component Slot](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot) element instead, add `is:inline` to your `<slot>` element.
:::
## Next Steps
<ReadMore>Read more about using [UI framework components](/en/guides/framework-components/) in your Astro project.</ReadMore>
---
File: /src/content/docs/en/basics/astro-pages.mdx
---
---
title: Pages
description: An introduction to Astro pages.
i18nReady: true
---
import ReadMore from '~/components/ReadMore.astro';
import Since from '~/components/Since.astro'
**Pages** are files that live in the `src/pages/` subdirectory of your Astro project. They are responsible for handling routing, data loading, and overall page layout for every page in your website.
## Supported page files
Astro supports the following file types in the `src/pages/` directory:
- [`.astro`](#astro-pages)
- [`.md`](#markdownmdx-pages)
- `.mdx` (with the [MDX Integration installed](/en/guides/integrations-guide/mdx/#installation))
- [`.html`](#html-pages)
- `.js`/`.ts` (as [endpoints](/en/guides/endpoints/))
## File-based routing
Astro leverages a routing strategy called **file-based routing**. Each file in your `src/pages/` directory becomes an endpoint on your site based on its file path.
A single file can also generate multiple pages using [dynamic routing](/en/guides/routing/#dynamic-routes). This allows you to create pages even if your content lives outside of the special `/pages/` directory, such as in a [content collection](/en/guides/content-collections/) or a [CMS](/en/guides/cms/).
<ReadMore>Read more about [Routing in Astro](/en/guides/routing/).</ReadMore>
### Link between pages
Write standard HTML [`<a>` elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a) in your Astro pages to link to other pages on your site. Use a **URL path relative to your root domain** as your link, not a relative file path.
For example, to link to `https://example.com/authors/sonali/` from any other page on `example.com`:
```astro title="src/pages/index.astro"
Read more <a href="/authors/sonali/">about Sonali</a>.
```
## Astro Pages
Astro pages use the `.astro` file extension and support the same features as [Astro components](/en/basics/astro-components/).
```astro title="src/pages/index.astro"
---
---
<html lang="en">
<head>
<title>My Homepage</title>
</head>
<body>
<h1>Welcome to my website!</h1>
</body>
</html>
```
A page must produce a full HTML document. If not explicitly included, Astro will add the necessary `<!DOCTYPE html>` declaration and `<head>` content to any `.astro` component located within `src/pages/` by default. You can opt-out of this behavior on a per-component basis by marking it as a [partial](#page-partials) page.
To avoid repeating the same HTML elements on every page, you can move common `<head>` and `<body>` elements into your own [layout components](/en/basics/layouts/). You can use as many or as few layout components as you'd like.
```astro title="src/pages/index.astro" /</?MySiteLayout>/ {2}
---
import MySiteLayout from "../layouts/MySiteLayout.astro";
---
<MySiteLayout>
<p>My page content, wrapped in a layout!</p>
</MySiteLayout>
```
<ReadMore>Read more about [layout components](/en/basics/layouts/) in Astro.</ReadMore>
## Markdown/MDX Pages
Astro also treats any Markdown (`.md`) files inside of `src/pages/` as pages in your final website. If you have the [MDX Integration installed](/en/guides/integrations-guide/mdx/#installation), it also treats MDX (`.mdx`) files the same way.
:::tip
Consider creating [content collections](/en/guides/content-collections/) instead of pages for directories of related Markdown files that share a similar structure, such as blog posts or product items.
:::
Markdown files can use the special `layout` frontmatter property to specify a [layout component](/en/basics/layouts/) that will wrap their Markdown content in a full `<html>...</html>` page document.
```md {3}
---
# Example: src/pages/page.md
layout: ../layouts/MySiteLayout.astro
title: My Markdown page
---
# Title
This is my page, written in **Markdown.**
```
<ReadMore>Read more about [Markdown](/en/guides/markdown-content/) in Astro.</ReadMore>
## HTML Pages
Files with the `.html` file extension can be placed in the `src/pages/` directory and used directly as pages on your site. Note that some key Astro features are not supported in [HTML Components](/en/basics/astro-components/#html-components).
## Custom 404 Error Page
For a custom 404 error page, you can create a `404.astro` or `404.md` file in `src/pages`.
This will build to a `404.html` page. Most [deploy services](/en/guides/deploy/) will find and use it.
## Custom 500 Error Page
For a custom 500 error page to show for pages that are [rendered on demand](/en/guides/on-demand-rendering/), create the file `src/pages/500.astro`. This custom page is not available for prerendered pages and can't be prerendered.
If an error occurs rendering this page, your host's default 500 error page will be shown to your visitor.
<p><Since v="4.10.3" /></p>
During development, if you have a `500.astro`, the error thrown at runtime is logged in your terminal, as opposed to being shown in the error overlay.
### `error`
<p><Since v="4.11.0" /></p>
`src/pages/500.astro` is a special page that is automatically passed an `error` prop for any error thrown during rendering. This allows you to use the details of an error (e.g. from a page, from middleware, etc.) to display information to your visitor.
The `error` prop's data type can be anything, which may affect how you type or use the value in your code:
```astro title="src/pages/500.astro"
---
interface Props {
error: unknown;
}
const { error } = Astro.props;
---
<div>{error instanceof Error ? error.message : "Unknown error"}</div>
```
To avoid leaking sensitive information when displaying content from the `error` prop, consider evaluating the error first, and returning appropriate content based on the error thrown. For example, you should avoid displaying the error's stack as it contains information about how your code is structured on the server.
## Page Partials
<p><Since v="3.4.0" /></p>
:::caution
Page partials are intended to be used in conjunction with a front-end library, such as [htmx](https://htmx.org/) or [Unpoly](https://unpoly.com/). You can also use them if you are comfortable writing low-level front-end JavaScript. For this reason they are an advanced feature.
Additionally, partials should not be used if the component contains scoped styles or scripts, as these elements will be stripped from the HTML output. If you need scoped styles, it is better to use regular, non-partial pages along with a frontend library that knows how to merge the contents into the head.
:::
Partials are page components located within `src/pages/` that are not intended to render as full pages.
Like components located outside of this folder, these files do not automatically include the `<!DOCTYPE html>` declaration, nor any `<head>` content such as scoped styles and scripts.
However, because they are located in the special `src/pages/` directory, the generated HTML is available at a URL corresponding to its file path. This allows a rendering library (e.g. [htmx](https://htmx.org/), [Stimulus](https://stimulus.hotwired.dev/), [jQuery](https://jquery.com/)) to access it on the client and load sections of HTML dynamically on a page without a browser refresh or page navigation.
Partials, when combined with a rendering library, provide an alternative to [Astro islands](/en/concepts/islands/) and [`<script>` tags](/en/guides/client-side-scripts/) for building dynamic content in Astro.
Page files that can export a value for [`partial`](/en/reference/routing-reference/#partial) (e.g. `.astro` and `.mdx`, but not `.md`) can be marked as partials.
```astro title="src/pages/partial.astro" ins={2}
---
export const partial = true;
---
<li>I'm a partial!</li>
```
### Using with a library
Partials are used to dynamically update a section of a page using a library such as [htmx](https://htmx.org/).
The following example shows an `hx-post` attribute set to a partial's URL. The content from the partial page will be used to update the targeted HTML element on this page.
```astro title="src/pages/index.astro" 'hx-post="/partials/clicked/"'
<html>
<head>
<title>My page</title>
<script src="https://unpkg.com/[email protected]"
integrity="sha384-FhXw7b6AlE/jyjlZH5iHa/tTe9EpJ1Y55RjcgPbjeWMskSxZt1v9qkxLJWNJaGni"
crossorigin="anonymous"></script>
</head>
<body>
<section>
<div id="parent-div">Target here</div>
<button hx-post="/partials/clicked/"
hx-trigger="click"
hx-target="#parent-div"
hx-swap="innerHTML"
>
Click Me!
</button>
</section>
</body>
</html>
```
The `.astro` partial must exist at the corresponding file path, and include an export defining the page as a partial:
```astro title="src/pages/partials/clicked.astro" {2}
---
export const partial = true;
---
<div>I was clicked!</div>
```
See the [htmx documentation](https://htmx.org/docs/) for more details on using htmx.
---
File: /src/content/docs/en/basics/layouts.mdx
---
---
title: Layouts
description: An introduction to layouts in Astro.
i18nReady: true
---
import ReadMore from '~/components/ReadMore.astro'
**Layouts** are [Astro components](/en/basics/astro-components/) used to provide a reusable UI structure, such as a page template.
We conventionally use the term "layout" for Astro components that provide common UI elements shared across pages such as headers, navigation bars, and footers. A typical Astro layout component provides [Astro, Markdown or MDX pages](/en/basics/astro-pages/) with:
- a **page shell** (`<html>`, `<head>` and `<body>` tags)
- a [**`<slot />`**](/en/basics/astro-components/#slots) to specify where individual page content should be injected.
But, there is nothing special about a layout component! They can [accept props](/en/basics/astro-components/#component-props) and [import and use other components](/en/basics/astro-components/#component-structure) like any other Astro component. They can include [UI frameworks components](/en/guides/framework-components/) and [client-side scripts](/en/guides/client-side-scripts/). They do not even have to provide a full page shell, and can instead be used as partial UI templates.
However, if a layout component does contain a page shell, its `<html>` element must be the parent of all other elements in the component.
Layout components are commonly placed in a `src/layouts` directory in your project for organization, but this is not a requirement; you can choose to place them anywhere in your project. You can even colocate layout components alongside your pages by [prefixing the layout names with `_`](/en/guides/routing/#excluding-pages).
## Sample Layout
```astro "<slot />"
---
// src/layouts/MySiteLayout.astro
import BaseHead from '../components/BaseHead.astro';
import Footer from '../components/Footer.astro';
const { title } = Astro.props;
---
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<BaseHead title={title}/>
</head>
<body>
<nav>
<a href="#">Home</a>
<a href="#">Posts</a>
<a href="#">Contact</a>
</nav>
<h1>{title}</h1>
<article>
<slot /> <!-- your content is injected here -->
</article>
<Footer />
</body>
<style>
h1 {
font-size: 2rem;
}
</style>
</html>
```
```astro title="src/pages/index.astro"
---
import MySiteLayout from '../layouts/MySiteLayout.astro';
---
<MySiteLayout title="Home Page">
<p>My page content, wrapped in a layout!</p>
</MySiteLayout>
```
<ReadMore>Learn more about [slots](/en/basics/astro-components/#slots).</ReadMore>
## Using TypeScript with layouts
Any Astro layout can be modified to introduce type safety & autocompletion by providing the types for your props:
```astro ins={2-7} title="src/components/MyLayout.astro"
---
interface Props {
title: string;
description: string;
publishDate: string;
viewCount: number;
}
const { title, description, publishDate, viewCount } = Astro.props;
---
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="description" content={description}>
<title>{title}</title>
</head>
<body>
<header>
<p>Published on {publishDate}</p>
<p>Viewed by {viewCount} folks</p>
</header>
<main>
<slot />
</main>
</body>
</html>
```
## 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 `<head>` content like meta tags (e.g. `<meta charset="utf-8">`) 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.
```markdown title="src/pages/page.md" {2}
---
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 [`<slot />`](/en/basics/astro-components/#slots) to indicate where the page's Markdown content should be rendered.
```astro title="src/layouts/BlogPostLayout.astro" /(?<!//.*){?frontmatter(?:\\.\w+)?}?/ "<slot />"
---
// 1. The frontmatter prop gives access to frontmatter and other data
const { frontmatter } = Astro.props;
---
<html>
<head>
<!-- Add other Head elements here, like styles and meta tags. -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8">
<title>{frontmatter.title}</title>
</head>
<body>
<!-- Add other UI components here, like common headers and footers. -->
<h1>{frontmatter.title} by {frontmatter.author}</h1>
<!-- 2. Rendered HTML will be passed into the default slot. -->
<slot />
<p>Written on: {frontmatter.date}</p>
</body>
</html>
```
You can set a layout’s [`Props` type](/en/guides/typescript/#component-props) with the `MarkdownLayoutProps` helper:
```astro title="src/layouts/BlogPostLayout.astro" ins={2,4-9}
---
import type { MarkdownLayoutProps } from 'astro';
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;
---
<html>
<head>
<meta charset="utf-8">
<link rel="canonical" href={new URL(url, Astro.site).pathname}>
<title>{frontmatter.title}</title>
</head>
<body>
<h1>{frontmatter.title} by {frontmatter.author}</h1>
<slot />
<p>Written on: {frontmatter.date}</p>
</body>
</html>
```
### 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 `<Layout />` component. This works like any other Astro component, and will not receive any props automatically. Pass it any necessary props directly:
```mdx title="src/pages/posts/first-post.mdx" ins={6} del={2} /</?BaseLayout>/ /</?BaseLayout title={frontmatter.title} fancyJsHelper={fancyJsHelper}>/
---
layout: ../../layouts/BaseLayout.astro
title: 'My first MDX post'
publishDate: '21 September 2022'
---
import BaseLayout from '../../layouts/BaseLayout.astro';
export function fancyJsHelper() {
return "Try doing that with YAML!";
}
<BaseLayout title={frontmatter.title} fancyJsHelper={fancyJsHelper}>
Welcome to my new Astro blog, using MDX!
</BaseLayout>
```
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 `<slot />` component is written:
```astro title="src/layouts/BaseLayout.astro" /{?title}?/ "fancyJsHelper" "{fancyJsHelper()}"
---
const { title, fancyJsHelper } = Astro.props;
---
<html>
<head>
<!-- -->
<meta charset="utf-8">
</head>
<body>
<!-- -->
<h1>{title}</h1>
<slot /> <!-- your content is injected here -->
<p>{fancyJsHelper()}</p>
<!-- -->
</body>
</html>
```
When using any layout (either through the frontmatter `layout` property or by importing a layout), you must include the `<meta charset="utf-8">` tag in your layout as Astro will no longer add it automatically to your MDX page.
<ReadMore>Learn more about Astro’s Markdown and MDX support in our [Markdown guide](/en/guides/markdown-content/).</ReadMore>
## 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.
```astro {3} /</?BaseLayout>/ /</?BaseLayout url={frontmatter.url}>/
---
// src/layouts/BlogPostLayout.astro
import BaseLayout from './BaseLayout.astro';
const { frontmatter } = Astro.props;
---
<BaseLayout url={frontmatter.url}>
<h1>{frontmatter.title}</h1>
<h2>Post author: {frontmatter.author}</h2>
<slot />
</BaseLayout>
```
---
File: /src/content/docs/en/basics/project-structure.mdx
---
---
title: Project structure
description: An introduction to the basic file structure of an Astro project.
i18nReady: true
---
import { FileTree } from '@astrojs/starlight/components';
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:
<FileTree>
- 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
</FileTree>
### `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`, `astro.config.cjs` and `astro.config.ts`. We recommend using `.mjs` in most cases or `.ts` if you want to write TypeScript in your config file.
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.
---
File: /src/content/docs/en/concepts/islands.mdx
---
---
title: Islands architecture
description: Learn about how Astro's islands architecture helps keep sites fast.
i18nReady: true
---
import IslandsDiagram from '~/components/IslandsDiagram.astro';
import ReadMore from '~/components/ReadMore.astro';
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.
<IslandsDiagram>
<Fragment slot="headerApp">Header (interactive island)</Fragment>
<Fragment slot="sidebarApp">Sidebar (static HTML)</Fragment>
<Fragment slot="main">
Static content like text, images, etc.
</Fragment>
<Fragment slot="carouselApp">Image carousel (interactive island)</Fragment>
<Fragment slot="footer">Footer (static HTML)</Fragment>
<Fragment slot="source">Source: [Islands Architecture: Jason Miller](https://jasonformat.com/islands-architecture/)</Fragment>
</IslandsDiagram>
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.**
```astro title="src/pages/index.astro"
<MyReactComponent />
```
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.
```astro title="src/pages/index.astro" ins="client:load"
<!-- This component is now interactive on the page!
The rest of your website remains static. -->
<MyReactComponent client:load />
```
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.
<h3>Benefits of client islands</h3>
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!**
<ReadMore>Read more about [using JavaScript framework components](/en/guides/framework-components/) in your project.</ReadMore>
## 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:
```astro title="src/pages/index.astro" "server:defer"
---
import Avatar from "../components/Avatar.astro";
---
<Avatar server:defer />
```
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.
<h3>Benefits of server islands</h3>
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.
<ReadMore>Read more about [using server islands](/en/guides/server-islands/) in your project.</ReadMore>
---
File: /src/content/docs/en/concepts/why-astro.mdx
---
---
title: Why Astro?
description: "Astro is the web framework for building content-driven websites like blogs, marketing, and e-commerce. Learn why Astro might be a good choice for your next website."
i18nReady: true
---
**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-guide/):** 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.
---
File: /src/content/docs/en/guides/backend/appwriteio.mdx
---
---
title: Appwrite & Astro
description: Add a backend to your project with Appwrite
sidebar:
label: Appwrite
type: backend
service: Appwrite
stub: true
i18nReady: true
---
[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)
---
File: /src/content/docs/en/guides/backend/google-firebase.mdx
---
---
title: Firebase & Astro
description: Add a backend to your project with Firebase
sidebar:
label: Firebase
type: backend
service: Firebase
stub: false
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
import { FileTree } from '@astrojs/starlight/components';
[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/google-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:
```ini title=".env"
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:
```ts title="src/env.d.ts"
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:
<FileTree title="Project Structure">
- src/
- **env.d.ts**
- **.env**
- astro.config.mjs
- package.json
</FileTree>
### 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
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install firebase firebase-admin
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm add firebase firebase-admin
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn add firebase firebase-admin
```
</Fragment>
</PackageManagerTabs>
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:
```ts title="src/firebase/client.ts"
import { initializeApp } from "firebase/app";
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",
};
export const app = initializeApp(firebaseConfig);
```
:::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:
```ts title="src/firebase/server.ts"
import type { ServiceAccount } from "firebase-admin";
import { initializeApp, cert, getApps } from "firebase-admin/app";
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)
})
}
export const app = activeApps.length === 0 ? initApp() : activeApps[0];
```
:::note
Remember to replace the `serviceAccount` object with your own project credentials.
:::
Finally, your project should now include these new files:
<FileTree title="Project Structure">
- src
- env.d.ts
- firebase
- **client.ts**
- **server.ts**
- .env
- astro.config.mjs
- package.json
</FileTree>
## 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:
```ts title="src/pages/api/auth/signin.ts"
import type { APIRoute } from "astro";
import { app } from "../../../firebase/server";
import { getAuth } from "firebase-admin/auth";
export const GET: APIRoute = async ({ request, cookies, redirect }) => {
const auth = getAuth(app);
/* 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:
```ts title="src/pages/api/auth/signout.ts"
import type { APIRoute } from "astro";
export const GET: APIRoute = async ({ redirect, cookies }) => {
cookies.delete("__session", {
path: "/",
});
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:
```ts title="src/pages/api/auth/register.ts"
import type { APIRoute } from "astro";
import { getAuth } from "firebase-admin/auth";
import { app } from "../../../firebase/server";
export const POST: APIRoute = async ({ request, redirect }) => {
const auth = getAuth(app);
/* 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:
<FileTree title="Project Structure">
- src
- env.d.ts
- firebase
- client.ts
- server.ts
- pages
- api
- auth
- **signin.ts**
- **signout.ts**
- **register.ts**
- .env
- astro.config.mjs
- package.json
</FileTree>
### 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.
```astro title="src/pages/register.astro"
---
import Layout from "../layouts/Layout.astro";
---
<Layout title="Register">
<h1>Register</h1>
<p>Already have an account? <a href="/signin">Sign in</a></p>
<form action="/api/auth/register" method="post">
<label for="name">Name</label>
<input type="text" name="name" id="name" />
<label for="email" for="email">Email</label>
<input type="email" name="email" id="email" />
<label for="password">Password</label>
<input type="password" name="password" id="password" />
<button type="submit">Login</button>
</form>
</Layout>
```
`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.
```astro title="src/pages/signin.astro"
---
import { app } from "../firebase/server";
import { getAuth } from "firebase-admin/auth";
import Layout from "../layouts/Layout.astro";
/* 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");
}
}
---
<Layout title="Sign in">
<h1>Sign in</h1>
<p>New here? <a href="/register">Create an account</a></p>
<form action="/api/auth/signin" method="post">
<label for="email" for="email">Email</label>
<input type="email" name="email" id="email" />
<label for="password">Password</label>
<input type="password" name="password" id="password" />
<button type="submit">Login</button>
</form>
</Layout>
<script>
import {
getAuth,
inMemoryPersistence,
signInWithEmailAndPassword,
} from "firebase/auth";
import { app } from "../firebase/client";
const auth = getAuth(app);
// This will prevent the browser from storing session data
auth.setPersistence(inMemoryPersistence);
const form = document.querySelector("form") as HTMLFormElement;
form.addEventListener("submit", async (e) => {
e.preventDefault();
const formData = new FormData(form);
const email = formData.get("email")?.toString();
const password = formData.get("password")?.toString();
if (!email || !password) {
return;
}
const userCredential = await signInWithEmailAndPassword(
auth,
email,
password
);
const idToken = await userCredential.user.getIdToken();
const response = await fetch("/api/auth/signin", {
method: "GET",
headers: {
Authorization: `Bearer ${idToken}`,
},
});
if (response.redirected) {
window.location.assign(response.url);
}
});
</script>
```
`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.
```astro title="src/pages/dashboard.astro"
---
import { app } from "../firebase/server";
import { getAuth } from "firebase-admin/auth";
import Layout from "../layouts/Layout.astro";
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");
}
---
<Layout title="dashboard">
<h1>Welcome {user.displayName}</h1>
<p>We are happy to see you here</p>
<form action="/api/auth/signout">
<button type="submit">Sign out</button>
</form>
</Layout>
```
### 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 `<script>`.
```astro title="src/pages/signin.astro" ins={27, 34, 35, 69-83}
---
import { app } from "../firebase/server";
import { getAuth } from "firebase-admin/auth";
import Layout from "../layouts/Layout.astro";
/* 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");
}
}
---
<Layout title="Sign in">
<h1>Sign in</h1>
<p>New here? <a href="/register">Create an account</a></p>
<form action="/api/auth/signin" method="post">
<label for="email" for="email">Email</label>
<input type="email" name="email" id="email" />
<label for="password">Password</label>
<input type="password" name="password" id="password" />
<button type="submit">Login</button>
</form>
<button id="google">Sign in with Google</button>
</Layout>
<script>
import {
getAuth,
inMemoryPersistence,
signInWithEmailAndPassword,
GoogleAuthProvider,
signInWithPopup,
} from "firebase/auth";
import { app } from "../firebase/client";
const auth = getAuth(app);
auth.setPersistence(inMemoryPersistence);
const form = document.querySelector("form") as HTMLFormElement;
form.addEventListener("submit", async (e) => {
e.preventDefault();
const formData = new FormData(form);
const email = formData.get("email")?.toString();
const password = formData.get("password")?.toString();
if (!email || !password) {
return;
}
const userCredential = await signInWithEmailAndPassword(
auth,
email,
password
);
const idToken = await userCredential.user.getIdToken();
const response = await fetch("/api/auth/signin", {
headers: {
Authorization: `Bearer ${idToken}`,
},
});
if (response.redirected) {
window.location.assign(response.url);
}
});
const googleSignin = document.querySelector("#google") as HTMLButtonElement;
googleSignin.addEventListener("click", async () => {
const provider = new GoogleAuthProvider();
const userCredential = await signInWithPopup(auth, provider);
const idToken = await userCredential.user.getIdToken();
const res = await fetch("/api/auth/signin", {
headers: {
Authorization: `Bearer ${idToken}`,
},
});
if (res.redirected) {
window.location.assign(res.url);
}
});
</script>
```
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:
```ts title="src/pages/api/friends/index.ts"
import type { APIRoute } from "astro";
import { app } from "../../../firebase/server";
import { getFirestore } from "firebase-admin/firestore";
export const POST: APIRoute = async ({ request, redirect }) => {
const formData = await request.formData();
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:
```ts title="src/pages/api/friends/[id].ts"
import type { APIRoute } from "astro";
import { app } from "../../../firebase/server";
import { getFirestore } from "firebase-admin/firestore";
const db = getFirestore(app);
const friendsRef = db.collection("friends");
export const POST: APIRoute = async ({ params, redirect, request }) => {
const formData = await request.formData();
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");
};
export const DELETE: APIRoute = async ({ params, redirect }) => {
if (!params.id) {
return new Response("Cannot find friend", {
status: 404,
});
}
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:
<FileTree title="Project Structure">
- src
- env.d.ts
- firebase
- client.ts
- server.ts
- pages
- api
- friends
- **index.ts**
- **[id].ts**
- .env
- astro.config.mjs
- package.json
</FileTree>
### 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.
```astro title="src/pages/add.astro"
---
import Layout from "../layouts/Layout.astro";
---
<Layout title="Add a new friend">
<h1>Add a new friend</h1>
<form method="post" action="/api/friends">
<label for="name">Name</label>
<input type="text" id="name" name="name" />
<label for="age">Age</label>
<input type="number" id="age" name="age" />
<label for="isBestFriend">Is best friend?</label>
<input type="checkbox" id="isBestFriend" name="isBestFriend" />
<button type="submit">Add friend</button>
</form>
</Layout>
```
#### 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.
```astro title="src/pages/edit/[id].astro"
---
import Layout from "../../layouts/Layout.astro";
import { app } from "../../firebase/server";
import { getFirestore } from "firebase-admin/firestore";
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;
---
<Layout title="Edit {friend.name}">
<h1>Edit {friend.name}</h1>
<p>Here you can edit or delete your friend's data.</p>
<form method="post" action={`/api/friends/${id}`}>
<label for="name">Name</label>
<input type="text" id="name" name="name" value={friend.name} />
<label for="age">Age</label>
<input type="number" id="age" name="age" value={friend.age} />
<label for="isBestFriend">Is best friend?</label>
<input
type="checkbox"
id="isBestFriend"
name="isBestFriend"
checked={friend.isBestFriend}
/>
<button type="submit">Edit friend</button>
</form>
<button type="button" id="delete-document">Delete</button>
</Layout>
<script>
const deleteButton = document.getElementById(
"delete-document"
) as HTMLButtonElement;
const url = document.querySelector("form")?.getAttribute("action") as string;
deleteButton.addEventListener("click", async () => {
const response = await fetch(url, {
method: "DELETE",
});
if (response.redirected) {
window.location.assign(response.url);
}
});
</script>
```
#### Display an individual record
`src/pages/friend/[id].astro` will display the details of a friend.
```astro title="src/pages/friend/[id].astro"
---
import Layout from "../../layouts/Layout.astro";
import { app } from "../../firebase/server";
import { getFirestore } from "firebase-admin/firestore";
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;
---
<Layout title={friend.name}>
<h1>{friend.name}</h1>
<p>Age: {friend.age}</p>
<p>Is best friend: {friend.isBestFriend ? "Yes" : "No"}</p>
</Layout>
```
#### 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.
```astro title="src/pages/dashboard.astro"
---
import { app } from "../firebase/server";
import { getFirestore } from "firebase-admin/firestore";
import Layout from "../layouts/Layout.astro";
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[];
---
<Layout title="My friends">
<h1>Friends</h1>
<ul>
{
friends.map((friend) => (
<li>
<a href={`/friend/${friend.id}`}>{friend.name}</a>
<span>({friend.age})</span>
<strong>{friend.isBestFriend ? "Bestie" : "Friend"}</strong>
<a href={`/edit/${friend.id}`}>Edit</a>
</li>
))
}
</ul>
</Layout>
```
After creating all the pages, you should have the following file structure:
<FileTree title="Project 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
</FileTree>
## 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)
---
File: /src/content/docs/en/guides/backend/index.mdx
---
---
title: Use a backend service with Astro
description: How to use a backend service to add authentication, storage and data
sidebar:
label: Backend services overview
i18nReady: true
---
import BackendGuidesNav from '~/components/BackendGuidesNav.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!
<BackendGuidesNav />
## 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
---
File: /src/content/docs/en/guides/backend/neon.mdx
---
---
title: Neon Postgres & Astro
description: Add a serverless Postgres database to your Astro project with Neon
sidebar:
label: Neon
type: backend
service: Neon
stub: false
i18nReady: true
---
import ReadMore from '~/components/ReadMore.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:
```ini title=".env"
NEON_DATABASE_URL="postgresql://<user>:<password>@<endpoint_hostname>.neon.tech:<port>/<dbname>?sslmode=require"
```
For better TypeScript support, define environment variables in a `src/env.d.ts` file:
```typescript title="src/env.d.ts"
interface ImportMetaEnv {
readonly NEON_DATABASE_URL: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
```
<ReadMore>Learn more about [environment variables](/en/guides/environment-variables/) and `.env` files in Astro.</ReadMore>
### Installing dependencies
Install the `@neondatabase/serverless` package to connect to Neon:
```bash
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:
```typescript title="src/lib/neon.ts"
import { neon } from '@neondatabase/serverless';
export const sql = neon(import.meta.env.NEON_DATABASE_URL);
```
## 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:
```astro title="src/pages/index.astro"
---
import { sql } from '../lib/neon';
const response = await sql`SELECT NOW() as current_time`;
const currentTime = response[0].current_time;
---
<h1>Current Time</h1>
<p>The time is: {currentTime}</p>
```
## 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:
```ini title=".env.development"
NEON_DATABASE_URL=your_development_branch_url
```
```ini title=".env.production"
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)
---
File: /src/content/docs/en/guides/backend/sentry.mdx
---
---
title: Monitor your Astro Site with Sentry
description: How to monitor your Astro site with Sentry
sidebar:
label: Sentry
type: backend
service: Sentry
stub: false
i18nReady: true
---
import ReadMore from '~/components/ReadMore.astro';
import { Steps } from '@astrojs/starlight/components';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
[Sentry](https://sentry.io) offers a comprehensive application monitoring and error tracking service designed to help developers identify, diagnose, and resolve issues in real-time.
Read more on our blog about [Astro's partnership with Sentry](https://astro.build/blog/sentry-official-monitoring-partner/) and Sentry's Spotlight dev toolbar app that brings a rich debug overlay into your Astro development environment. Spotlight shows errors, traces, and important context right in your browser during local development.
Sentry's Astro SDK enables automatic reporting of errors and tracing data in your Astro application.
## Project Configuration
A full list of prerequisites can be found in [the Sentry guide for Astro](https://docs.sentry.io/platforms/javascript/guides/astro/#prerequisites).
## Install
Sentry captures data by using an SDK within your application’s runtime.
Install the SDK by running the following command for the package manager of your choice in the Astro CLI:
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npx astro add @sentry/astro
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm astro add @sentry/astro
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn astro add @sentry/astro
```
</Fragment>
</PackageManagerTabs>
The astro CLI installs the SDK package and adds the Sentry integration to your `astro.config.mjs` file.
## Configure
To configure the Sentry integration, you need to provide the following credentials in your `astro.config.mjs` file.
1. **Client key (DSN)** - You can find the DSN in your Sentry project settings under *Client keys (DSN)*.
2. **Project name** - You can find the project name in your Sentry project settings under *General settings*.
3. **Auth token** - You can create an auth token in your Sentry organization settings under *Auth tokens*.
:::note
If you are creating a new Sentry project, select Astro as your platform to get all the necessary information to configure the SDK.
:::
```js title="astro.config.mjs" ins={2, 6-12}
import { defineConfig } from 'astro/config';
import sentry from '@sentry/astro';
export default defineConfig({
integrations: [
sentry({
dsn: 'https://[email protected]/0',
sourceMapsUploadOptions: {
project: 'example-project',
authToken: process.env.SENTRY_AUTH_TOKEN,
},
}),
],
});
```
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 `<button>` element to one of your `.astro` pages. This will allow you to manually trigger an error so you can test the error reporting process.
```astro title="src/pages/index.astro"
<button onclick="throw new Error('This is a test error')">Throw test error</button>
```
To view and resolve the recorded error, log into [sentry.io](https://sentry.io/) and open your project.
---
File: /src/content/docs/en/guides/backend/supabase.mdx
---
---
title: Supabase & Astro
description: Add a backend to your project with Supabase
sidebar:
label: Supabase
type: backend
service: Supabase
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
import { FileTree } from '@astrojs/starlight/components';
[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:
```ini title=".env"
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:
```ts title="src/env.d.ts"
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:
<FileTree title="Project Structure">
- src/
- **env.d.ts**
- **.env**
- astro.config.mjs
- package.json
</FileTree>
### Installing dependencies
To connect to Supabase, you will need to install `@supabase/supabase-js` in your project.
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install @supabase/supabase-js
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm add @supabase/supabase-js
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn add @supabase/supabase-js
```
</Fragment>
</PackageManagerTabs>
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:
```ts title="src/lib/supabase.ts"
import { createClient } from "@supabase/supabase-js";
export const supabase = createClient(
import.meta.env.SUPABASE_URL,
import.meta.env.SUPABASE_ANON_KEY,
);
```
Now, your project should include these files:
<FileTree title="Project Structure">
- src/
- lib/
- **supabase.ts**
- env.d.ts
- .env
- astro.config.mjs
- package.json
</FileTree>
## 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:
<FileTree title="Project Structure">
- src/
- lib/
- supabase.ts
- pages/
- api/
- auth/
- **signin.ts**
- **signout.ts**
- **register.ts**
- env.d.ts
- .env
- astro.config.mjs
- package.json
</FileTree>
`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.
```ts title="src/pages/api/auth/register.ts"
// With `output: 'static'` configured:
// export const prerender = false;
import type { APIRoute } from "astro";
import { supabase } from "../../../lib/supabase";
export const POST: APIRoute = async ({ request, redirect }) => {
const formData = await request.formData();
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.
```ts title="src/pages/api/auth/signin.ts"
// With `output: 'static'` configured:
// export const prerender = false;
import type { APIRoute } from "astro";
import { supabase } from "../../../lib/supabase";
export const POST: APIRoute = async ({ request, cookies, redirect }) => {
const formData = await request.formData();
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.
```ts title="src/pages/api/auth/signout.ts"
// With `output: 'static'` configured:
// export const prerender = false;
import type { APIRoute } from "astro";
export const GET: APIRoute = async ({ cookies, redirect }) => {
cookies.delete("sb-access-token", { path: "/" });
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:
<FileTree title="Project Structure">
- 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
</FileTree>
`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`.
```astro title="src/pages/register.astro"
---
import Layout from "../layouts/Layout.astro";
---
<Layout title="Register">
<h1>Register</h1>
<p>Already have an account? <a href="/signin">Sign in</a></p>
<form action="/api/auth/register" method="post">
<label for="email">Email</label>
<input type="email" name="email" id="email" />
<label for="password">Password</label>
<input type="password" name="password" id="password" />
<button type="submit">Register</button>
</form>
</Layout>
```
`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.
```astro title="src/pages/signin.astro"
---
import Layout from "../layouts/Layout.astro";
const { cookies, redirect } = Astro;
const accessToken = cookies.get("sb-access-token");
const refreshToken = cookies.get("sb-refresh-token");
if (accessToken && refreshToken) {
return redirect("/dashboard");
}
---
<Layout title="Sign in">
<h1>Sign in</h1>
<p>New here? <a href="/register">Create an account</a></p>
<form action="/api/auth/signin" method="post">
<label for="email">Email</label>
<input type="email" name="email" id="email" />
<label for="password">Password</label>
<input type="password" name="password" id="password" />
<button type="submit">Login</button>
</form>
</Layout>
```
`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.
```astro title="src/pages/dashboard.astro"
---
import Layout from "../layouts/Layout.astro";
import { supabase } from "../lib/supabase";
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;
---
<Layout title="dashboard">
<h1>Welcome {email}</h1>
<p>We are happy to see you here</p>
<form action="/api/auth/signout">
<button type="submit">Sign out</button>
</form>
</Layout>
```
### 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).
```ts title="src/lib/supabase.ts" ins={6-10}
import { createClient } from "@supabase/supabase-js";
export const supabase = createClient(
import.meta.env.SUPABASE_URL,
import.meta.env.SUPABASE_ANON_KEY,
{
auth: {
flowType: "pkce",
},
},
);
```
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.
```ts title="src/pages/api/auth/callback.ts"
import type { APIRoute } from "astro";
import { supabase } from "../../../lib/supabase";
export const GET: APIRoute = async ({ url, cookies, redirect }) => {
const authCode = url.searchParams.get("code");
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.
```astro title="src/pages/signin.astro" ins={23}
---
import Layout from "../layouts/Layout.astro";
const { cookies, redirect } = Astro;
const accessToken = cookies.get("sb-access-token");
const refreshToken = cookies.get("sb-refresh-token");
if (accessToken && refreshToken) {
return redirect("/dashboard");
}
---
<Layout title="Sign in">
<h1>Sign in</h1>
<p>New here? <a href="/register">Create an account</a></p>
<form action="/api/auth/signin" method="post">
<label for="email">Email</label>
<input type="email" name="email" id="email" />
<label for="password">Password</label>
<input type="password" name="password" id="password" />
<button type="submit">Login</button>
<button value="github" name="provider" type="submit">Sign in with GitHub</button>
</form>
</Layout>
```
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.
```ts title="src/pages/api/auth/signin.ts" ins={10-23}
import type { APIRoute } from "astro";
import { supabase } from "../../../lib/supabase";
import type { Provider } from "@supabase/supabase-js";
export const POST: APIRoute = async ({ request, cookies, redirect }) => {
const formData = await request.formData();
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:
<FileTree title="Project 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
</FileTree>
## 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)
---
File: /src/content/docs/en/guides/backend/turso.mdx
---
---
title: Turso & Astro
description: Build locally with a SQLite file and deploy globally using Turso.
sidebar:
label: Turso
type: backend
service: Turso
stub: false
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
[Turso](https://turso.tech) is a distributed database built on libSQL, a fork of SQLite. It is optimized for low query latency, making it suitable for global applications.
## Initializing Turso in Astro
### Prerequisites
- The [Turso CLI](https://docs.turso.tech/reference/turso-cli) installed and signed in
- A [Turso](https://turso.tech) Database with schema
- Your Database URL
- An Access Token
### Configure environment variables
Obtain your database URL using the following command:
```bash
turso db show <database-name> --url
```
Create an auth token for the database:
```bash
turso db tokens create <database-name>
```
Add the output from both commands above into your `.env` file at the root of your project. If this file does not exist, create one.
```ini title=".env"
TURSO_DATABASE_URL=libsql://...
TURSO_AUTH_TOKEN=
```
:::caution
Do not use the `PUBLIC_` prefix when creating these private [environment variables](/en/guides/environment-variables/). This will expose these values on the client.
:::
### Install LibSQL Client
Install the `@libsql/client` to connect Turso to Astro:
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install @libsql/client
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm add @libsql/client
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn add @libsql/client
```
</Fragment>
</PackageManagerTabs>
### Initialize a new client
Create a file `turso.ts` in the `src` folder and invoke `createClient`, passing it `TURSO_DATABASE_URL` and `TURSO_AUTH_TOKEN`:
```ts title="src/turso.ts"
import { createClient } from "@libsql/client/web";
export const turso = createClient({
url: import.meta.env.TURSO_DATABASE_URL,
authToken: import.meta.env.TURSO_AUTH_TOKEN,
});
```
## Querying your database
To access information from your database, import `turso` and [execute a SQL query](https://docs.turso.tech/libsql/client-access/javascript-typescript-sdk#execute-a-single-statement) inside any `.astro` component.
The following example fetches all `posts` from your table, then displays a list of titles in a `<BlogIndex />` component:
```astro title="src/components/BlogIndex.astro"
---
import { turso } from '../turso'
const { rows } = await turso.execute('SELECT * FROM posts')
---
<ul>
{rows.map((post) => (
<li>{post.title}</li>
))}
</ul>
```
### SQL Placeholders
The `execute()` method can take [an object to pass variables to the SQL statement](https://docs.turso.tech/libsql/client-access/javascript-typescript-sdk#positional-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.
```astro title="src/pages/index.astro"
---
import { turso } from '../turso'
const { slug } = Astro.params
const { rows } = await turso.execute({
sql: 'SELECT * FROM posts WHERE slug = ?',
args: [slug!]
})
---
<h1>{rows[0].title}</h1>
```
## 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)
---
File: /src/content/docs/en/guides/backend/xata.mdx
---
---
title: Xata & Astro
description: Add a serverless database with full-text search to your project with Xata
sidebar:
label: Xata
type: backend
service: Xata
stub: false
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
import { FileTree } from '@astrojs/starlight/components';
[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_TOKEN_API`).
- 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:
```ini title=".env"
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:
```ini title=".xatarc"
{
"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:
```ts title="src/env.d.ts"
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:
<FileTree title="Project Structure">
- src/
- **xata.ts**
- **env.d.ts**
- **.env**
- astro.config.mjs
- package.json
- **.xatarc**
</FileTree>
## 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.
```astro title="src/pages/blog/index.astro"
---
import { XataClient } from '../../xata';
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
}
})
---
<ul>
{records.map((post) => (
<li>{post.title}</li>
))}
</ul>
```
It's important to note the SDK needs to be regenerated everytime 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)
---
File: /src/content/docs/en/guides/cms/apostrophecms.mdx
---
---
title: ApostropheCMS & Astro
description: Edit content on the page in your Astro project using Apostrophe as your CMS.
sidebar:
label: ApostropheCMS
type: cms
stub: true
service: Apostrophe
i18nReady: true
---
import { FileTree } from '@astrojs/starlight/components';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
[ApostropheCMS](https://apostrophecms.com/) is a content management system supporting on-page editing in Astro.
## Integrating with Astro
In this section, you will use the [Apostrophe integration](https://apostrophecms.com/extensions/astro-integration) to connect ApostropheCMS to Astro.
### Prerequisites
To get started, you will need to have the following:
1. **An on-demand rendered Astro project** with the [Node.js adapter](/en/guides/integrations-guide/node/) installed and `output: 'server'` configured - 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 ApostropheCMS project with a configured environment variable called `APOS_EXTERNAL_FRONT_KEY`** - This environment variable can be set to any random string. If you don't have an ApostropheCMS project yet, the [installation guide](https://docs.apostrophecms.org/guide/development-setup.html) will get one setup quickly. We highly recommend using the [Apostrophe CLI tool](https://apostrophecms.com/extensions/apos-cli) to facilitate this.
### Setting up project communication
Your Astro project needs to have an `APOS_EXTERNAL_FRONT_KEY` environment variable set to the same value as the one in your ApostropheCMS project to allow communication between the two. This shared key acts as a means to verify requests between the frontend (Astro) and the backend (ApostropheCMS).
Create a `.env` file in the root of your Astro project with the following variable:
```ini title=".env"
APOS_EXTERNAL_FRONT_KEY='RandomStrongString'
```
Your root directory should now include this new file:
<FileTree title="Project Structure">
- src/
- **.env**
- astro.config.mjs
- package.json
</FileTree>
### Installing dependencies
To connect Astro with your ApostropheCMS project, install the official Apostrophe integration in your Astro project using the command below for your preferred package manager.
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install @apostrophecms/apostrophe-astro vite @astro/node
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm add @apostrophecms/apostrophe-astro vite @astro/node
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn add @apostrophecms/apostrophe-astro vite @astro/node
```
</Fragment>
</PackageManagerTabs>
### Configuring Astro
Configure both the `apostrophe-astro` integration and `vite` in your `astro.config.mjs` file.
The following example provides the base URL of your Apostrophe instance and paths to folders in your project to map between the ApostropheCMS [widgets](https://docs.apostrophecms.org/guide/core-widgets.html) and [page template](https://docs.apostrophecms.org/guide/pages.html) types and your Astro project. It also configures Vite's server-side rendering.
```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';
import apostrophe from '@apostrophecms/apostrophe-astro';
import { loadEnv } from 'vite';
const env = loadEnv("", process.cwd(), 'APOS');
export default defineConfig({
output: 'server',
adapter: node({
mode: 'standalone'
}),
integrations: [
apostrophe({
aposHost: 'http://localhost:3000',
widgetsMapping: './src/widgets',
templatesMapping: './src/templates'
})
],
vite: {
ssr: {
// Do not externalize the @apostrophecms/apostrophe-astro plugin, we need
// to be able to use virtual: URLs there
noExternal: [ '@apostrophecms/apostrophe-astro' ],
},
define: {
'process.env.APOS_EXTERNAL_FRONT_KEY': JSON.stringify(env.APOS_EXTERNAL_FRONT_KEY),
'process.env.APOS_HOST': JSON.stringify(env.APOS_HOST)
}
}
});
```
For complete configuration options and explanations, see the [`apostrophe-astro` documentation](https://apostrophecms.com/extensions/astro-integration#configuration-astro).
### Connecting ApostropheCMS widgets to Astro components
ApostropheCMS widgets are blocks of structured content that can be added to the page such as layout columns, images, and text blocks. You will need to create an Astro component for each widget in your Apostrophe project, plus a file to map those components to the corresponding Apostrophe widget.
Create a new folder at `src/widgets/` for your Astro components and the mapping file, `index.js`.
Mapped components located in this folder receive a `widget` property containing your widget's schema fields, and any custom props, through `Astro.props`. These values are then available for on-page editing.
The following example shows a `RichTextWidget.astro` component accessing the content from its corresponding ApostropheCMS widget to allow for in-context editing:
```js title="src/widgets/RichTextWidget.astro"
---
const { widget } = Astro.props;
const { content } = widget;
---
<Fragment set:html={ content }></Fragment>
```
Some standard Apostrophe widgets, such as images and videos, require **placeholders** because they do not contain editable content by default. The following example creates a standard `ImageWidget.astro` component that sets the `src` value conditionally to either the value of the `aposPlaceholder` image passed by the widget, a fallback image from the Astro project, or the image selected by the content manager using the Apostrophe `attachment` helper:
```js title="src/widgets/ImageWidget.astro"
---
const { widget } = Astro.props;
const placeholder = widget?.aposPlaceholder;
const src = placeholder ?
'/images/image-widget-placeholder.jpg' :
widget?._image[0]?.attachment?._urls['full'];
---
<style>
.img-widget {
width: 100%;
}
</style>
<img class="img-widget" {src} />
```
For more examples, see [the `astro-frontend` starter project widget examples](https://github.com/apostrophecms/astro-frontend/tree/main/src/widgets).
Each `.astro` component must be mapped to the corresponding core Apostrophe widget in `src/widgets/index.js`.
The example below adds the previous two components to this file:
```js title="src/widgets/index.js"
import RichTextWidget from './RichTextWidget.astro';
import ImageWidget from './ImageWidget.astro';
const widgetComponents = {
'@apostrophecms/rich-text': RichTextWidget,
'@apostrophecms/image': ImageWidget
};
export default widgetComponents;
```
See [the ApostropheCMS documentation](https://apostrophecms.com/extensions/astro-integration) for naming conventions for standard, pro, and custom-project-level widgets
The project directory should now look like this:
<FileTree title="Project Structure">
- src/
- widgets/
- **index.js**
- **ImageWidget.astro**
- **RichTextWidget.astro**
- .env
- astro.config.mjs
- package.json
</FileTree>
### Adding page types
Much like widgets, any page type template in your ApostropheCMS project needs to have a corresponding template component in your Astro project. You will also need a file that maps the Apostrophe page types to individual components.
Create a new folder at `src/templates/` for your Astro components and the mapping file, `index.js`. Mapped components located in this folder receive a `page` property containing the schema fields of your page, and any custom props, through `Astro.props`. These components can also access an `AposArea` component to render Apostrophe areas.
The following example shows a `HomePage.astro` component rendering a page template from its corresponding `home-page` ApostropheCMS page type, including an area schema field named `main`:
```js title="src/templates/HomePage.astro"
---
import AposArea from '@apostrophecms/apostrophe-astro/components/AposArea.astro';
const { page, user, query } = Astro.props.aposData;
const { main } = page;
---
<section class="bp-content">
<h1>{ page.title }</h1>
<AposArea area={main} />
</section>
```
Each `.astro` component must be mapped to the corresponding core Apostrophe page type in `src/templates/index.js`.
The example below adds the previous `HomePage.astro` component to this file:
```js title="src/templates/index.js"
import HomePage from './HomePage.astro';
const templateComponents = {
'@apostrophecms/home-page': HomePage
};
export default templateComponents;
```
See [the ApostropheCMS documentation](https://apostrophecms.com/extensions/astro-integration/#how-apostrophe-template-names-work) for template naming conventions, including special pages and piece page types.
The project directory should now look like this:
<FileTree title="Project Structure">
- src/
- widgets/
- index.js
- ImageWidget.astro
- RichTextWidget.astro
- templates/
- **HomePage.astro**
- **index.js**
- .env
- astro.config.mjs
- package.json
</FileTree>
### Creating the [...slug.astro] component and fetching Apostrophe data
Since Apostrophe is responsible for connecting URLs to content, including creating new content and pages on the fly, you will only need one top-level Astro page component: the `[...slug].astro` route.
The following example shows a minimal `[...slug].astro` component:
```js title="src/pages/[...slug].astro"
---
import aposPageFetch from '@apostrophecms/apostrophe-astro/lib/aposPageFetch.js';
import AposLayout from '@apostrophecms/apostrophe-astro/components/layouts/AposLayout.astro';
import AposTemplate from '@apostrophecms/apostrophe-astro/components/AposTemplate.astro';
const aposData = await aposPageFetch(Astro.request);
const bodyClass = `myclass`;
if (aposData.redirect) {
return Astro.redirect(aposData.url, aposData.status);
}
if (aposData.notFound) {
Astro.response.status = 404;
}
---
<AposLayout title={aposData.page?.title} {aposData} {bodyClass}>
<Fragment slot="standardHead">
<meta name="description" content={aposData.page?.seoDescription} />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charset="UTF-8" />
</Fragment>
<AposTemplate {aposData} slot="main"/>
</AposLayout>
```
See [the ApostropheCMS documentation](https://apostrophecms.com/extensions/astro-integration#creating-the-slugastro-component-and-fetching-apostrophe-data) for additional templating options, including slots available in the `AposTemplate` component.
## Making a blog with Astro and ApostropheCMS
With the integration set up, you can now create a blog with Astro and ApostropheCMS. Your blog will use an Apostrophe piece, a stand-alone content type that can be included on any page, and a piece page type, a specialized page type that is used for displaying those pieces either individually or collectively.
### Prerequisites
1. **An ApostropheCMS project with the Apostrophe CLI tool installed** - You can create a new project or use an existing one. However, this tutorial will only show how to create a blog piece and piece page type. You will have to integrate any other existing project code independently. If you don't have the CLI tool installed, consult the [Apostrophe CLI installation instructions](https://docs.apostrophecms.org/guide/setting-up.html#the-apostrophe-cli-tool).
2. **An Astro project integrated with ApostropheCMS** - To create a project from scratch, see [integrating with Astro](#integrating-with-astro) for instructions on how to set up the integration, or use the [astro-frontend](https://github.com/apostrophecms/astro-frontend) starter project.
### Creating a blog piece and piece page type
To create your blog piece and piece page type for their display, navigate to the root of your ApostropheCMS project in your terminal. Use the ApostropheCMS CLI tool to create the new piece and piece page type with the following command:
```sh
apos add piece blog --page
```
This will create two new modules in your project, one for the blog piece type and one for the corresponding piece page type. Next, open the `app.js` file at the root of your ApostropheCMS project in your code editor and add your new modules.
```js title="app.js"
require('apostrophe')({
// other configuration options
modules: {
// other project modules
blog: {},
'blog-page': {}
}
});
```
The `blog-page` module also needs to be added to the `@apostrophecms/page` module `types` option array so that it can be selected in the page creation modal. In your ApostropheCMS project, open the `modules/@apostrophecms/page/index.js` file and add the `blog-page`.
```js title="modules/@apostrophecms/page/index.js"
module.exports = {
options: {
types: [
{
name: '@apostrophecms/home-page',
label: 'Home'
},
// Any other project pages
{
name: 'blog-page',
label: 'Blog'
}
]
}
};
```
### Creating the blog schema
In an ApostropheCMS project, editors are offered a set of input fields for adding content. Here is an example of a simple blog post that adds three input fields, an `authorName`, `publicationDate` and `content` area where content managers can add multiple widget instances. In this case, we are specifying they can add the image and rich-text widgets we created during the [integration setup](#connecting-apostrophecms-widgets-to-astro-components).
```js title="modules/blog/index.js"
module.exports = {
extend: '@apostrophecms/piece-type',
options: {
label: 'Blog',
// Additionally add a `pluralLabel` option if needed.
},
fields: {
add: {
authorName: {
type: 'string',
label: 'Author'
},
publicationDate: {
type: 'date',
label: 'Publication date'
},
content: {
type: 'area',
label: 'Content',
options: {
widgets: {
'@apostrophecms/rich-text': {},
'@apostrophecms/image': {}
}
}
}
},
group: {
basics: {
label: 'Basic',
fields: [ 'authorName', 'publicationDate', 'content' ]
}
}
}
};
```
At this point, all the components coming from the ApostropheCMS project are set up. Start the local site from the command line using `npm run dev`, making sure to pass in the `APOS_EXTERNAL_FRONT_KEY` environment variable set to your selected string:
```bash
APOS_EXTERNAL_FRONT_KEY='MyRandomString' npm run dev
```
### Displaying the blog posts
To display a page with all the blog posts create a `BlogIndex.astro` component file in the `src/templates` directory of your Astro project and add the following code:
After fetching both the page and pieces data from the `aposData` prop, this component creates markup using both fields from the blog piece schema we created, but also from the `piece.title` and `piece._url` that is added to each piece by Apostrophe.
```js title="src/templates/BlogIndex.astro"
---
import dayjs from 'dayjs';
const { page, pieces } = Astro.props.aposData;
---
<section class="bp-content">
<h1>{ page.title }</h1>
<h2>Blog Posts</h2>
{pieces.map(piece => (
<h4>
Released On { dayjs(piece.publicationDate).format('MMMM D, YYYY') }
</h4>
<h3>
<a href={ piece._url }>{ piece.title }</a>
</h3>
<h4>{ piece.authorName }</h4>
))}
</section>
```
To display individual blog posts, create a `BlogShow.astro` file in the Astro project `src/templates` folder with the following code:
This component uses the `<AposArea>` component to display any widgets added to the `content` area and the `authorName` and `publicationDate` content entered into the fields of the same names.
```js title="src/templates/BlogShow.astro"
---
import AposArea from '@apostrophecms/apostrophe-astro/components/AposArea.astro';
import dayjs from 'dayjs';
const { page, piece } = Astro.props.aposData;
const { main } = piece;
---
<section class="bp-content">
<h1>{ piece.title }</h1>
<h3>Created by: { piece.authorName }
<h4>
Released On { dayjs(piece.publicationDate).format('MMMM D, YYYY') }
</h4>
<AposArea area={content} />
</section>
```
Finally, these Astro components must be mapped to the corresponding ApostropheCMS page types. Open the Astro project `src/templates/index.js` file and modify it to contain the following code:
```js title="src/templates/index.js"
import HomePage from './HomePage.astro';
import BlogIndexPage from './BlogIndexPage.astro';
import BlogShowPage from './BlogShowPage.astro';
const templateComponents = {
'@apostrophecms/home-page': HomePage,
'@apostrophecms/blog-page:index': BlogIndexPage,
'@apostrophecms/blog-page:show': BlogShowPage
};
export default templateComponents;
```
### Creating blog posts
Adding blog posts to your site is accomplished by using the ApostropheCMS content and management tools to create those posts and by publishing at least one index page to display them.
With the Astro dev server running, navigate to the login page located at [http://localhost:4321/login](http://localhost:4321/login) in your browser preview. Use the credentials that were added during the [creation of the ApostropheCMS project](https://docs.apostrophecms.org/guide/development-setup.html#creating-a-project) to log in as an administrator. Your ApostropheCMS project should still be running.
Once you are logged in, your browser will be redirected to the home page of your project and will display an admin bar at the top for editing content and managing your project.
To add your first blog post, click on the `Blogs` button in the admin bar to open the blog piece creation modal. Clicking on the `New Blog` button in the upper right will open an editing modal where you can add content. The `content` area field will allow you to add as many image and rich text widgets as you desire.
You can repeat this step and add as many posts as you want. You will also follow these steps every time you want to add a new post.
To publish a page for displaying all your posts, click on the `Pages` button in the admin bar. From the page tree modal click on the `New Page` button. In the `Type` dropdown in the right column select `Blog`. Add a title for the page and then click `Publish and View`. You will only need to do this once.
Any new blog posts that are created will be automatically displayed on this page. Individual blog posts can be displayed by clicking on the link created on the index page.
The `content` area of individual posts can be edited directly on the page by navigating to the post and clicking `edit` in the admin bar. Other fields can be edited by using the editing modal opened when clicking the `Blogs` menu item in the admin bar.
### Deploying your site
To deploy your website, you need to host both your Astro and ApostropheCMS projects.
For Astro, visit our [deployment guides](/en/guides/deploy/) and follow the instructions for your preferred hosting provider.
For the ApostropheCMS project, follow the instructions for your hosting type in our [hosting guide](https://docs.apostrophecms.org/guide/hosting.html). Finally, you'll need to supply an `APOS_HOST` environment variable to the Astro project to reflect the correct URL where your ApostropheCMS site has been deployed.
## Official Resources
- [Astro integration for ApostropheCMS](https://apostrophecms.com/extensions/astro-integration) - ApostropheCMS Astro plugin, integration guide and starter projects for both Apostrophe and Astro
- [Sample Astro project for use with ApostropheCMS](https://github.com/apostrophecms/astro-frontend) - A simple Astro project with several pages and Apostrophe widgets already created.
- [Sample ApostropheCMS starter-kit for use with Astro](https://apostrophecms.com/starter-kits/astro-integration-starter-kit) - An ApostropheCMS project with core page modifications for use with Astro.
## Community Resources
- [Integrating ApostropheCMS with Astro, Pt. 1](https://apostrophecms.com/blog/how-to-integrate-astro-with-apostrophecms-pt-1) by Antonello Zaini
- [Integrating ApostropheCMS with Astro, Pt. 2](https://apostrophecms.com/blog/how-to-integrate-astro-with-apostrophecms-pt-2) by Antonello Zaini
- [Show & Tell Live | Astro & Apostrophe](https://youtu.be/cwJhtJhAhwA?si=6iUU9EjidfdnOdCh)
---
File: /src/content/docs/en/guides/cms/builderio.mdx
---
---
title: Builder.io & Astro
description: Add content to your Astro project using Builder.io’s visual CMS
sidebar:
label: Builder.io
type: cms
service: Builder.io
stub: false
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
import { FileTree } from '@astrojs/starlight/components';
import { Steps } from '@astrojs/starlight/components';
[Builder.io](https://www.builder.io/) is a visual CMS that supports drag-and-drop content editing for building websites.
This recipe will show you how to connect your Builder space to Astro with zero client-side JavaScript.
## Prerequisites
To get started, you will need to have the following:
* **A Builder account and space** - If you don't have an account yet, [sign up for free](https://www.builder.io/) and create a new space. If you already have a space with Builder, feel free to use it, but you will need to modify the code to match the model name (`blogpost`) and custom data fields.
* **A Builder API key** - This public key will be used to fetch your content from Builder. [Read Builder's guide on how to find your key](https://www.builder.io/c/docs/using-your-api-key#finding-your-public-api-key).
## Setting up credentials
To add your Builder API key and your Builder model name to Astro, create a `.env` file in the root of your project (if one does not already exist) and add the following variables:
```ini title=".env"
BUILDER_API_PUBLIC_KEY=YOUR_API_KEY
BUILDER_BLOGPOST_MODEL='blogpost'
```
Now, you should be able to use this API key in your project.
:::note
At the time of writing, [this key is public](https://www.builder.io/c/docs/using-your-api-key#prerequisites), so you don't have to worry about hiding or encrypting it.
:::
If you would like to have IntelliSense for your environment variables, you can create a `env.d.ts` file in the `src/` directory and configure `ImportMetaEnv` like this:
```ts title="src/env.d.ts"
interface ImportMetaEnv {
readonly BUILDER_API_PUBLIC_KEY: string;
}
```
Your project should now include these files:
<FileTree title="Project Structure">
- src/
- **env.d.ts**
- **.env**
- astro.config.mjs
- package.json
</FileTree>
## Making a blog with Astro and Builder
### Creating a model for a blog post
The instructions below create an Astro blog using a Builder model (Type: "Section") called `blogpost` that contains two required text fields: `title` and `slug`.
:::tip[For visual types]
You can find videos showing this procedure in one of [Builder's official tutorials](https://www.builder.io/blog/creating-blog#creating-a-blog-article-model).
:::
In the Builder app create the model that will represent a blog post: go to the **Models** tab and click the **+ Create Model** button to create model with the following fields and values:
- **Type:** Section
- **Name:** "blogpost"
- **Description:** "This model is for a blog post"
In your new model use the **+ New Custom Field** button to create 2 new fields:
1. Text field
- **Name:** "title"
- **Required:** Yes
- **Default value** "I forgot to give this a title"
(leave the other parameters as their defaults)
2. Text field
- **Name:** "slug"
- **Required:** Yes
- **Default value** "some-slugs-take-their-time"
(leave the other parameters as their defaults)
Then click the **Save** button in the upper right.
:::caution[Slugs]
There are some pitfalls with the `slug` field:
* Make sure your slug is not just a number. This seems to break the fetch request to Builder's API.
* Make sure your slugs are unique, since your site's routing will depend on that.
:::
### Setting up the preview
To use Builder's visual editor, create the page `src/pages/builder-preview.astro` that will render the special `<builder-component>`:
<FileTree title="Project Structure">
- src/
- pages/
- **builder-preview.astro**
- env.d.ts
- .env
- astro.config.mjs
- package.json
</FileTree>
Then add the following content:
```astro title="src/pages/builder-preview.astro"
---
const builderAPIpublicKey = import.meta.env.BUILDER_API_PUBLIC_KEY;
const builderModel = import.meta.env.BUILDER_BLOGPOST_MODEL;
---
<html lang="en">
<head>
<title>Preview for builder.io</title>
</head>
<body>
<header>This is your header</header>
<builder-component model={builderModel} api-key={builderAPIpublicKey}
></builder-component>
<script async src="https://cdn.builder.io/js/webcomponents"></script>
<footer>This is your footer</footer>
</body>
</html>
```
In the above example, `<builder-component>` tells Builder where to insert the content from its CMS.
#### Setting the new route as the preview URL
<Steps>
1. Copy the full URL of your preview, including the protocol, to your clipboard (e.g. `https://{your host}/builder-preview`).
2. Go to the **Models** tab in your Builder space, pick the model you've created and paste the URL from step 1 into the **Preview URL** field. Make sure the URL is complete and includes the protocol, for example `https://`.
3. Click the **Save** button in the upper right.
</Steps>
:::tip
When you deploy your site, change the preview URL to match your production URL, for example `https://myAwesomeAstroBlog.com/builder-preview`.
:::
#### Testing the preview URL setup
<Steps>
1. Make sure your site is live (e.g. your dev server is running) and the `/builder-preview` route is working.
2. In your Builder space under the **Content** tab, click on **New** to create a new content entry for your `blogpost` model.
3. In the Builder editor that just opened, you should be able to see the `builder-preview.astro` page with a big **Add Block** in the middle.
</Steps>
:::tip[Troubleshooting]
Things can sometimes go wrong when setting up the preview. If something's not right, you can try one of these things:
* Make sure the site is live - for example, your dev server is running.
* Make sure that the URLs match exactly - the one in your Astro project and the one set in the Builder app.
* Make sure it's the full URL including the protocol, for example `https://`.
* If you're working in a virtual environment like [IDX](https://idx.dev), [StackBlitz](https://stackblitz.com/), or [Gitpod](https://www.gitpod.io/), you might have to copy and paste the URL again when you restart your workspace, since this usually generates a new URL for your project.
For more ideas, read [Builder's troubleshooting guide](https://www.builder.io/c/docs/guides/preview-url-working).
:::
### Creating a blog post
<Steps>
1. In Builder's visual editor, create a new content entry with the following values:
- **title:** 'First post, woohoo!'
- **slug:** 'first-post-woohoo'
2. Complete your post using the **Add Block** button and add a text field with some post content.
3. In the text field above the editor, give your entry a name. This is how it will be listed in the Builder app.
4. When you're ready click the **Publish** button in the upper right corner.
5. Create as many posts as you like, ensuring that all content entries contain a `title` and a `slug` as well as some post content.
</Steps>
### Displaying a list of blog posts
Add the following content to `src/pages/index.astro` in order to fetch and display a list of all post titles, each linking to its own page:
```astro title="src/pages/index.astro" {9}
---
const builderAPIpublicKey = import.meta.env.BUILDER_API_PUBLIC_KEY;
const builderModel = import.meta.env.BUILDER_BLOGPOST_MODEL;
const { results: posts } = await fetch(
`https://cdn.builder.io/api/v3/content/${builderModel}?${new URLSearchParams({
apiKey: builderAPIpublicKey,
fields: ["data.slug", "data.title"].join(","),
cachebust: "true",
}).toString()}`
)
.then((res) => res.json())
.catch();
---
<html lang="en">
<head>
<title>Blog Index</title>
</head>
<body>
<ul>
{
posts.flatMap(({ data: { slug, title } }) => (
<li>
<a href={`/posts/${slug}`}>{title}</a>
</li>
))
}
</ul>
</body>
</html>
```
Fetching via the content API returns an array of objects containing data for each post. The `fields` query parameter tells Builder which data is included (see highlighted code). `slug` and `title` should match the names of the custom data fields you've added to your Builder model.
The `posts` array returned from the fetch displays a list of blog post titles on the home page. The individual page routes will be created in the next step.
:::tip[Framework integrations]
If you are using a JavaScript framework (e.g. Svelte, Vue, or React) in your Astro project you can use [one of Builder's integrations](https://www.builder.io/m/integrations) as an alternative to making raw fetch calls through the REST API.
:::
Go to your index route and you should be able to see a list of links each with the title of a blog post!
### Displaying a single blog post
Create the page `src/pages/posts/[slug].astro` that will [dynamically generate a page](/en/guides/routing/#dynamic-routes) for each post.
<FileTree title="Project Structure">
- src/
- pages/
- index.astro
- posts/
- **[slug].astro**
- env.d.ts
- .env
- astro.config.mjs
- package.json
</FileTree>
This file must contain:
- A [`getStaticPaths()`](/en/reference/routing-reference/#getstaticpaths) function to fetch `slug` information from Builder and create a static route for each blog post.
- A `fetch()` to the Builder API using the `slug` identifier to return post content and metadata (e.g. a `title`).
- A `<Fragment />` in the template to render the post content as HTML.
Each of these is highlighted in the following code snippet.
```astro title="src/pages/posts/[slug].astro" ins={2, 26, 33, 40, 51}
---
export async function getStaticPaths() {
const builderModel = import.meta.env.BUILDER_BLOGPOST_MODEL;
const builderAPIpublicKey = import.meta.env.BUILDER_API_PUBLIC_KEY;
const { results: posts } = await fetch(
`https://cdn.builder.io/api/v3/content/${builderModel}?${new URLSearchParams(
{
apiKey: builderAPIpublicKey,
fields: ["data.slug", "data.title"].join(","),
cachebust: "true",
}
).toString()}`
)
.then((res) => res.json())
.catch
// ...catch some errors...);
();
return posts.map(({ data: { slug, title } }) => ({
params: { slug },
props: { title },
}))
}
const { slug } = Astro.params;
const { title } = Astro.props;
const builderModel = import.meta.env.BUILDER_BLOGPOST_MODEL;
const builderAPIpublicKey = import.meta.env.BUILDER_API_PUBLIC_KEY;
// Builder's API requires this field but for this use case the url doesn't seem to matter - the API returns the same HTML
const encodedUrl = encodeURIComponent("moot");
const { html: postHTML } = await fetch(
`https://cdn.builder.io/api/v1/qwik/${builderModel}?${new URLSearchParams({
apiKey: builderAPIpublicKey,
url: encodedUrl,
"query.data.slug": slug,
cachebust: "true",
}).toString()}`
)
.then((res) => res.json())
.catch();
---
<html lang="en">
<head>
<title>{title}</title>
</head>
<body>
<header>This is your header</header>
<article>
<Fragment set:html={postHTML} />
</article>
<footer>This is your footer</footer>
</body>
</html>
```
:::note
The variables `builderModel` and `builderAPIpublicKey` need to be created twice, since [`getStaticPaths()` runs in its own isolated scope](/en/reference/routing-reference/#getstaticpaths).
:::
Now when you click on a link on your index route, you will be taken to the individual blog post page.
### Publishing your site
To deploy your website, visit our [deployment guides](/en/guides/deploy/) and follow the instructions for your preferred hosting provider.
#### Rebuild on Builder changes
If your project is using Astro's default static mode, you will need to set up a webhook to trigger a new build when your content changes. If you are using Netlify or Vercel as your hosting provider, you can use its webhook feature to trigger a new build whenever you click **Publish** in the Builder editor.
##### Netlify
<Steps>
1. Go to your site dashboard, then **Site Settings** and click on **Build & deploy**.
2. Under the **Continuous Deployment** tab, find the **Build hooks** section and click on **Add build hook**.
3. Provide a name for your webhook and select the branch you want to trigger the build on. Click on **Save** and copy the generated URL.
</Steps>
##### Vercel
<Steps>
1. Go to your project dashboard and click on **Settings**.
2. Under the **Git** tab, find the **Deploy Hooks** section.
3. Provide a name for your webhook and the branch you want to trigger the build on. Click **Add** and copy the generated URL.
</Steps>
##### Adding a webhook to Builder
:::tip[Official resource]
See [Builder's guide on adding webhooks](https://www.builder.io/c/docs/webhooks) for more information.
:::
<Steps>
1. In your Builder dashboard, go into your **`blogpost`** model. Under **Show More Options**, select **Edit Webhooks** at the bottom.
2. Add a new webhook by clicking on **Webhook**. Paste the URL generated by your hosting provider into the **Url** field.
3. Click on **Show Advanced** under the URL field and toggle the option to select **Disable Payload**. With the payload disabled, Builder sends a simpler POST request to your hosting provider, which can be helpful as your site grows. Click **Done** to save this selection.
</Steps>
With this webhook in place, whenever you click the **Publish** button in the Builder editor, your hosting provider rebuilds your site - and Astro fetches the newly published data for you. Nothing to do but lean back and pump out that sweet sweet content!
## Official resources
- Check out [the official Builder.io starter project](https://github.com/BuilderIO/builder/tree/main/examples/astro-solidjs), which uses Astro and SolidJS.
- The [official Builder quickstart guide](https://www.builder.io/c/docs/quickstart#step-1-add-builder-as-a-dependency) covers both the use of the REST API as well as data fetching through an integration with a JavaScript framework like Qwik, React or Vue.
- [Builder's API explorer](https://builder.io/api-explorer) can help if you need to troubleshoot your API calls.
## Community resources
- Read [Connecting Builder.io's Visual CMS to Astro](https://www.hamatoyogi.dev/blog/astro-log/connecting-builderio-to-astro) by Yoav Ganbar.
---
File: /src/content/docs/en/guides/cms/buttercms.mdx
---
---
title: ButterCMS & Astro
description: Add content to your Astro project using ButterCMS
sidebar:
label: ButterCMS
type: cms
service: ButterCMS
stub: false
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
[ButterCMS](https://buttercms.com/) is a headless CMS and blog engine that allows you to publish structured content to use in your project.
## Integrating with Astro
:::note
For a full blog site example, see the [Astro + ButterCMS Starter Project](https://buttercms.com/starters/astro-starter-project/).
:::
In this section, we'll use the [ButterCMS SDK](https://www.npmjs.com/package/buttercms) to bring your data into your Astro project.
To get started, you will need to have the following:
### 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 ButterCMS account**. If you don't have an account, you can [sign up](https://buttercms.com/join/) for a free trial.
3. **Your ButterCMS API Token** - You can find your API Token on the [Settings](https://buttercms.com/settings/) page.
### Setup
<Steps>
1. Create a `.env` file in the root of your project and add your API token as an environment variable:
```ini title=".env"
BUTTER_TOKEN=YOUR_API_TOKEN_HERE
```
:::tip
Read more about [using environment variables](/en/guides/environment-variables/) and `.env` files in Astro.
:::
2. Install the ButterCMS SDK as a dependency:
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install buttercms
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm add buttercms
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn add buttercms
```
</Fragment>
</PackageManagerTabs>
3. Create a `buttercms.js` file in a new `src/lib/` directory in your project:
```js title="src/lib/buttercms.js"
import Butter from "buttercms";
export const butterClient = Butter(import.meta.env.BUTTER_TOKEN);
```
</Steps>
**This authenticates the SDK using your API Token and exports it to be used across your project.**
### Fetching Data
To fetch content, import this client and use one of its `retrieve` functions.
In this example, we [retrieve a collection](https://buttercms.com/docs/api/#retrieve-a-collection) that has three fields: a short text `name`, a number `price`, and a WYSIWYG `description`.
```astro title="src/pages/ShopItem.astro"
---
import { butterClient } from "../lib/buttercms";
const response = await butterClient.content.retrieve(["shopitem"]);
interface ShopItem {
name: string,
price: number,
description: string,
}
const items = response.data.data.shopitem as ShopItem[];
---
<body>
{items.map(item => <div>
<h2>{item.name} - ${item.price}</h2>
<p set:html={item.description}></p>
</div>)}
</body>
```
The interface mirrors the field types. The WYSIWYG `description` field loads as a string of HTML, and [`set:html`](/en/reference/directives-reference/#sethtml) lets you render it.
Similarly, you can [retrieve a page](https://buttercms.com/docs/api/#get-a-single-page) and display its fields:
```astro title="src/pages/ShopItem.astro"
---
import { butterClient } from "../lib/buttercms";
const response = await butterClient.page.retrieve("*", "simple-page");
const pageData = response.data.data;
interface Fields {
seo_title: string,
headline: string,
hero_image: string,
}
const fields = pageData.fields as Fields;
---
<html>
<title>{fields.seo_title}</title>
<body>
<h1>{fields.headline}</h1>
<img src={fields.hero_image} />
</body>
</html>
```
## Official Resources
- [Astro + ButterCMS Starter Project](https://buttercms.com/starters/astro-starter-project/)
- The [full ButterCMS API documentation](https://buttercms.com/docs/api/)
- ButterCMS's [JavaScript Guide](https://buttercms.com/docs/api-client/javascript/)
## Community Resources
- Add yours!
---
File: /src/content/docs/en/guides/cms/caisy.mdx
---
---
title: Caisy & Astro
description: Add content to your Astro project using Caisy as a CMS
sidebar:
label: Caisy
type: cms
service: Caisy
i18nReady: true
stub: true
---
[Caisy](https://caisy.io/) is a headless CMS that exposes a GraphQL API to access content.
## Using Caisy CMS with Astro
Use `graphql-request` and Caisy's rich text renderer for Astro to fetch your CMS data and display your content on an Astro page:
```astro title="src/pages/blog/[...slug].astro"
---
import RichTextRenderer from '@caisy/rich-text-astro-renderer';
import { gql, GraphQLClient } from 'graphql-request';
const params = Astro.params;
const client = new GraphQLClient(
`https://cloud.caisy.io/api/v3/e/${import.meta.env.CAISY_PROJECT_ID}/graphql`,
{
headers: {
'x-caisy-apikey': import.meta.env.CAISY_API_KEY
}
}
);
const gqlResponse = await client.request(
gql`
query allBlogArticle($slug: String) {
allBlogArticle(where: { slug: { eq: $slug } }) {
edges {
node {
text {
json
}
title
slug
id
}
}
}
}
`,
{ slug: params.slug }
);
const post = gqlResponse?.allBlogArticle?.edges?.[0]?.node;
---
<h1>{post.title}</h1>
<RichTextRenderer node={post.text.json} />
```
## Official Resources
- Check out the Caisy + Astro example on [GitHub](https://github.com/caisy-io/caisy-example-astro) or [StackBlitz](https://stackblitz.com/github/caisy-io/caisy-example-astro?file=src%2Fpages%2Fblog%2F%5B...slug%5D.astro)
- Query your documents in [draft mode](https://caisy.io/developer/docs/external-api/localization-and-preview#preview-mode-15) and multiple [locales](https://caisy.io/developer/docs/external-api/localization-and-preview#localization-in-a-graphql-query-8).
- Use [pagination](https://caisy.io/developer/docs/external-api/queries-pagination) to query large numbers of documents.
- Use [filter](https://caisy.io/developer/docs/external-api/external-filter-and-sorting) in your queries and [order](https://caisy.io/developer/docs/external-api/external-filter-and-sorting#sorting-8) the results
---
File: /src/content/docs/en/guides/cms/cloudcannon.mdx
---
---
title: CloudCannon & Astro
description: Add content to your Astro project using CloudCannon as a CMS
sidebar:
label: CloudCannon
type: cms
stub: true
service: CloudCannon
i18nReady: true
---
import Grid from '~/components/FluidGrid.astro'
import Card from '~/components/ShowcaseCard.astro'
[CloudCannon](https://cloudcannon.com) is a Git-based headless content management system that provides a visual editor for your content.
## Official Resources
- [Astro Starter Template](https://cloudcannon.com/templates/astro-starter/)
- [Astro Multilingual Starter Template](https://cloudcannon.com/templates/astro-multilingual-starter/)
- [Astro Starter Guide](https://cloudcannon.com/documentation/guides/astro-starter-guide/)
- [Bookshop & Astro Guide](https://cloudcannon.com/documentation/guides/bookshop-astro-guide/)
- [Astro Beginner Tutorial Series](https://cloudcannon.com/tutorials/astro-beginners-tutorial-series/)
- Blog: [How CloudCannon’s live editing works with Astro and Bookshop](https://cloudcannon.com/blog/how-cloudcannons-live-editing-works-with-astro-and-bookshop/)
- Blog: [Out-of-this-world support for all Astro users](https://cloudcannon.com/blog/out-of-this-world-support-for-all-astro-users/)
## Community Resources
- [CloudCannon announces official support for Astro](https://astro.build/blog/astro-cloudcannon-support/)
## Themes
<Grid>
<Card title="Sendit" href="https://astro.build/themes/details/sendit/" thumbnail="sendit.png"/>
</Grid>
---
File: /src/content/docs/en/guides/cms/contentful.mdx
---
---
title: Contentful & Astro
description: Add content to your Astro project using Contentful as a CMS
sidebar:
label: Contentful
type: cms
service: Contentful
i18nReady: true
---
import { FileTree } from '@astrojs/starlight/components';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import { Steps } from '@astrojs/starlight/components';
[Contentful](https://www.contentful.com/) is a headless CMS that allows you to manage content, integrate with other services, and publish to multiple platforms.
## Integrating with Astro
In this section, we'll use the [Contentful SDK](https://github.com/contentful/contentful.js) to connect your Contentful space to Astro with zero client-side JavaScript.
### 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 Contentful account and a Contentful space**. If you don't have an account, you can [sign up](https://www.contentful.com/sign-up/) for a free account and create a new Contentful space. You can also use an existing space if you have one.
3. **Contentful credentials** - You can find the following credentials in your Contentful dashboard **Settings > API keys**. If you don't have any API keys, create one by selecting **Add API key**.
- **Contentful space ID** - The ID of your Contentful space.
- **Contentful delivery access token** - The access token to consume _published_ content from your Contentful space.
- **Contentful preview access token** - The access token to consume _unpublished_ content from your Contentful space.
### Setting up credentials
To add your Contentful space's credentials to Astro, create an `.env` file in the root of your project with the following variables:
```ini title=".env"
CONTENTFUL_SPACE_ID=YOUR_SPACE_ID
CONTENTFUL_DELIVERY_TOKEN=YOUR_DELIVERY_TOKEN
CONTENTFUL_PREVIEW_TOKEN=YOUR_PREVIEW_TOKEN
```
Now, you can use these environment variables in your project.
If you would like to have IntelliSense for your Contentful environment variables, you can create a `env.d.ts` file in the `src/` directory and configure `ImportMetaEnv` like this:
```ts title="src/env.d.ts"
interface ImportMetaEnv {
readonly CONTENTFUL_SPACE_ID: string;
readonly CONTENTFUL_DELIVERY_TOKEN: string;
readonly CONTENTFUL_PREVIEW_TOKEN: string;
}
```
:::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:
<FileTree title="Project Structure">
- src/
- **env.d.ts**
- **.env**
- astro.config.mjs
- package.json
</FileTree>
### Installing dependencies
To connect with your Contentful space, install both of the following using the single command below for your preferred package manager:
- [`contentful.js`](https://github.com/contentful/contentful.js), the official Contentful SDK for JavaScript
- [`rich-text-html-renderer`](https://github.com/contentful/rich-text/tree/master/packages/rich-text-html-renderer), a package to render Contentful's rich text fields to HTML.
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install contentful @contentful/rich-text-html-renderer
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm add contentful @contentful/rich-text-html-renderer
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn add contentful @contentful/rich-text-html-renderer
```
</Fragment>
</PackageManagerTabs>
Next, create a new file called `contentful.ts` in the `src/lib/` directory of your project.
```ts title="src/lib/contentful.ts"
import * as contentful from "contentful";
export const contentfulClient = contentful.createClient({
space: import.meta.env.CONTENTFUL_SPACE_ID,
accessToken: import.meta.env.DEV
? import.meta.env.CONTENTFUL_PREVIEW_TOKEN
: import.meta.env.CONTENTFUL_DELIVERY_TOKEN,
host: import.meta.env.DEV ? "preview.contentful.com" : "cdn.contentful.com",
});
```
The above code snippet creates a new Contentful client, passing in credentials from the `.env` file.
:::caution
While in development mode, your content will be fetched from the **Contentful preview API**. This means that you will be able to see unpublished content from the Contentful web app.
At build time, your content will be fetched from the **Contentful delivery API**. This means that only published content will be available at build time.
:::
Finally, your root directory should now include these new files:
<FileTree title="Project Structure">
- src/
- env.d.ts
- lib/
- **contentful.ts**
- .env
- astro.config.mjs
- package.json
</FileTree>
### Fetching data
Astro components can fetch data from your Contentful account by using the `contentfulClient` and specifying the `content_type`.
For example, if you have a "blogPost" content type that has a text field for a title and a rich text field for content, your component might look like this:
```astro
---
import { contentfulClient } from "../lib/contentful";
import { documentToHtmlString } from "@contentful/rich-text-html-renderer";
import type { EntryFieldTypes } from "contentful";
interface BlogPost {
contentTypeId: "blogPost",
fields: {
title: EntryFieldTypes.Text
content: EntryFieldTypes.RichText,
}
}
const entries = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
});
---
<body>
{entries.items.map((item) => (
<section>
<h2>{item.fields.title}</h2>
<article set:html={documentToHtmlString(item.fields.content)}></article>
</section>
))}
</body>
```
:::tip
If you have an empty Contentful space, check out [setting up a Contentful model](#setting-up-a-contentful-model) to learn how to create a basic blog model for your content.
:::
You can find more querying options in the [Contentful documentation](https://contentful.github.io/contentful.js/).
## Making a blog with Astro and Contentful
With the setup above, you are now able to create a blog that uses Contentful as the CMS.
### Prerequisites
1. **A Contentful space** - For this tutorial we recommend starting with an empty space. If you already have a content model, feel free to use it, but you will need to modify our code snippets to match your content model.
2. **An Astro project integrated with the [Contentful SDK](https://github.com/contentful/contentful.js)** - See [integrating with Astro](#integrating-with-astro) for more details on how to set up an Astro project with Contentful.
### Setting up a Contentful model
Inside your Contentful space, in the **Content model** section, create a new content model with the following fields and values:
- **Name:** Blog Post
- **API identifier:** `blogPost`
- **Description:** This content type is for a blog post
In your newly created content type, use the **Add Field** button to add 5 new fields with the following parameters:
1. Text field
- **Name:** title
- **API identifier:** `title`
(leave the other parameters as their defaults)
2. Date and time field
- **Name:** date
- **API identifier:** `date`
3. Text field
- **Name:** slug
- **API identifier:** `slug`
(leave the other parameters as their defaults)
4. Text field
- **Name:** description
- **API identifier:** `description`
5. Rich text field
- **Name:** content
- **API identifier:** `content`
Click **Save** to save your changes.
In the **Content** section of your Contentful space, create a new entry by clicking the **Add Entry** button. Then, fill in the fields:
- **Title:** `Astro is amazing!`
- **Slug:** `astro-is-amazing`
- **Description:** `Astro is a new static site generator that is blazing fast and easy to use.`
- **Date:** `2022-10-05`
- **Content:** `This is my first blog post!`
Click **Publish** to save your entry. You have just created your first blog post.
Feel free to add as many blog posts as you want, then switch to your favorite code editor to start hacking with Astro!
### Displaying a list of blog posts
Create a new interface called `BlogPost` and add it to your `contentful.ts` file in `src/lib/`. This interface will match the fields of your blog post content type in Contentful. You will use it to type your blog post entries response.
```ts title="src/lib/contentful.ts" ins="type { EntryFieldTypes }" ins={4-13}
import * as contentful from "contentful";
import type { EntryFieldTypes } from "contentful";
export interface BlogPost {
contentTypeId: "blogPost",
fields: {
title: EntryFieldTypes.Text
content: EntryFieldTypes.RichText,
date: EntryFieldTypes.Date,
description: EntryFieldTypes.Text,
slug: EntryFieldTypes.Text
}
}
export const contentfulClient = contentful.createClient({
space: import.meta.env.CONTENTFUL_SPACE_ID,
accessToken: import.meta.env.DEV
? import.meta.env.CONTENTFUL_PREVIEW_TOKEN
: import.meta.env.CONTENTFUL_DELIVERY_TOKEN,
host: import.meta.env.DEV ? "preview.contentful.com" : "cdn.contentful.com",
});
```
Next, go to the Astro page where you will fetch data from Contentful. We will use the home page `index.astro` in `src/pages/` in this example.
Import `BlogPost` interface and `contentfulClient` from `src/lib/contentful.ts`.
Fetch all the entries from Contentful with a content type of `blogPost` while passing the `BlogPost` interface to type your response.
```astro title="src/pages/index.astro"
---
import { contentfulClient } from "../lib/contentful";
import type { BlogPost } from "../lib/contentful";
const entries = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
});
---
```
This fetch call will return an array of your blog posts at `entries.items`. You can use `map()` to create a new array (`posts`) that formats your returned data.
The example below returns the `items.fields` properties from our Content model to create a blog post preview, and at the same time, reformats the date to a more readable format.
```astro title="src/pages/index.astro" ins={9-17}
---
import { contentfulClient } from "../lib/contentful";
import type { BlogPost } from "../lib/contentful";
const entries = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
});
const posts = entries.items.map((item) => {
const { title, date, description, slug } = item.fields;
return {
title,
slug,
description,
date: new Date(date).toLocaleDateString()
};
});
---
```
Finally, you can use `posts` in your template to show a preview of each blog post.
```astro astro title="src/pages/index.astro" ins={19-37}
---
import { contentfulClient } from "../lib/contentful";
import type { BlogPost } from "../lib/contentful";
const entries = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
});
const posts = entries.items.map((item) => {
const { title, date, description, slug } = item.fields;
return {
title,
slug,
description,
date: new Date(date).toLocaleDateString()
};
});
---
<html lang="en">
<head>
<title>My Blog</title>
</head>
<body>
<h1>My Blog</h1>
<ul>
{posts.map((post) => (
<li>
<a href={`/posts/${post.slug}/`}>
<h2>{post.title}</h2>
</a>
<time>{post.date}</time>
<p>{post.description}</p>
</li>
))}
</ul>
</body>
</html>
```
### Generating individual blog posts
Use the same method to fetch your data from Contentful as above, but this time, on a page that will create a unique page route for each blog post.
#### Static site generation
If you're using Astro's default static mode, you'll use [dynamic routes](/en/guides/routing/#dynamic-routes) and the `getStaticPaths()` function. This function will be called at build time to generate the list of paths that become pages.
Create a new file named `[slug].astro` in `src/pages/posts/`.
As you did on `index.astro`, import the `BlogPost` interface and `contentfulClient` from `src/lib/contentful.ts`.
This time, fetch your data inside a `getStaticPaths()` function.
```astro title="src/pages/posts/[slug].astro"
---
import { contentfulClient } from "../../lib/contentful";
import type { BlogPost } from "../../lib/contentful";
export async function getStaticPaths() {
const entries = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
});
}
---
```
Then, map each item 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` property will be passed to the page component as props.
```astro title="src/pages/posts/[slug].astro" ins={3,11-19}
---
import { contentfulClient } from "../../lib/contentful";
import { documentToHtmlString } from "@contentful/rich-text-html-renderer";
import type { BlogPost } from "../../lib/contentful";
export async function getStaticPaths() {
const entries = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
});
const pages = entries.items.map((item) => ({
params: { slug: item.fields.slug },
props: {
title: item.fields.title,
content: documentToHtmlString(item.fields.content),
date: new Date(item.fields.date).toLocaleDateString(),
},
}));
return pages;
}
---
```
The property inside `params` must match the name of the dynamic route. Since our filename is `[slug].astro`, we use `slug`.
In our example, the `props` object passes three properties to the page:
- title (a string)
- content (a rich text Document converted to HTML using `documentToHtmlString`)
- date (formatted using the `Date` constructor)
Finally, you can use the page `props` to display your blog post.
```astro title="src/pages/posts/[slug].astro" ins={21,23-32}
---
import { contentfulClient } from "../../lib/contentful";
import { documentToHtmlString } from "@contentful/rich-text-html-renderer";
import type { BlogPost } from "../../lib/contentful";
export async function getStaticPaths() {
const { items } = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
});
const pages = items.map((item) => ({
params: { slug: item.fields.slug },
props: {
title: item.fields.title,
content: documentToHtmlString(item.fields.content),
date: new Date(item.fields.date).toLocaleDateString(),
},
}));
return pages;
}
const { content, title, date } = Astro.props;
---
<html lang="en">
<head>
<title>{title}</title>
</head>
<body>
<h1>{title}</h1>
<time>{date}</time>
<article set:html={content} />
</body>
</html>
```
Navigate to http://localhost:4321/ and click on one of your posts to make sure your dynamic route is working!
#### Server side rendering
If you've [opted in to SSR mode](/en/guides/on-demand-rendering/), you will use a dynamic route that uses a `slug` parameter to fetch the data from Contentful.
Create a `[slug].astro` page in `src/pages/posts`. Use [`Astro.params`](/en/reference/api-reference/#params) to get the slug from the URL, then pass that to `getEntries`:
```astro title="src/pages/posts/[slug].astro"
---
import { contentfulClient } from "../../lib/contentful";
import type { BlogPost } from "../../lib/contentful";
const { slug } = Astro.params;
const data = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
"fields.slug": slug,
});
---
```
If the entry is not found, you can redirect the user to the 404 page using [`Astro.redirect`](/en/guides/routing/#dynamic-redirects).
```astro title="src/pages/posts/[slug].astro" ins={7, 12-13}
---
import { contentfulClient } from "../../lib/contentful";
import type { BlogPost } from "../../lib/contentful";
const { slug } = Astro.params;
try {
const data = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
"fields.slug": slug,
});
} catch (error) {
return Astro.redirect("/404");
}
---
```
To pass post data to the template section, create a `post` object outside the `try/catch` block.
Use `documentToHtmlString` to convert `content` from a Document to HTML, and use the Date constructor to format the date. `title` can be left as-is. Then, add these properties to your `post` object.
```astro title="src/pages/posts/[slug].astro" ins={7,14-19}
---
import Layout from "../../layouts/Layout.astro";
import { contentfulClient } from "../../lib/contentful";
import { documentToHtmlString } from "@contentful/rich-text-html-renderer";
import type { BlogPost } from "../../lib/contentful";
let post;
const { slug } = Astro.params;
try {
const data = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
"fields.slug": slug,
});
const { title, date, content } = data.items[0].fields;
post = {
title,
date: new Date(date).toLocaleDateString(),
content: documentToHtmlString(content),
};
} catch (error) {
return Astro.redirect("/404");
}
---
```
Finally, you can reference `post` to display your blog post in the template section.
```astro title="src/pages/posts/[slug].astro" ins={24-33}
---
import Layout from "../../layouts/Layout.astro";
import { contentfulClient } from "../../lib/contentful";
import { documentToHtmlString } from "@contentful/rich-text-html-renderer";
import type { BlogPost } from "../../lib/contentful";
let post;
const { slug } = Astro.params;
try {
const data = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
"fields.slug": slug,
});
const { title, date, content } = data.items[0].fields;
post = {
title,
date: new Date(date).toLocaleDateString(),
content: documentToHtmlString(content),
};
} catch (error) {
return Astro.redirect("/404");
}
---
<html lang="en">
<head>
<title>{post?.title}</title>
</head>
<body>
<h1>{post?.title}</h1>
<time>{post?.date}</time>
<article set:html={post?.content} />
</body>
</html>
```
### Publishing your site
To deploy your website, visit our [deployment guides](/en/guides/deploy/) and follow the instructions for your preferred hosting provider.
#### Rebuild on Contentful changes
If your project is using Astro's default static mode, you will need to set up a webhook to trigger a new build when your content changes. If you are using Netlify or Vercel as your hosting provider, you can use its webhook feature to trigger a new build from Contentful events.
##### Netlify
To set up a webhook in Netlify:
<Steps>
1. Go to your site dashboard and click on **Build & deploy**.
2. Under the **Continuous Deployment** tab, find the **Build hooks** section and click on **Add build hook**.
3. Provide a name for your webhook and select the branch you want to trigger the build on. Click on **Save** and copy the generated URL.
</Steps>
##### Vercel
To set up a webhook in Vercel:
<Steps>
1. Go to your project dashboard and click on **Settings**.
2. Under the **Git** tab, find the **Deploy Hooks** section.
3. Provide a name for your webhook and the branch you want to trigger the build on. Click **Add** and copy the generated URL.
</Steps>
##### Adding a webhook to Contentful
In your Contentful space **settings**, click on the **Webhooks** tab and create a new webhook by clicking the **Add Webhook** button. Provide a name for your webhook and paste the webhook URL you copied in the previous section. Finally, hit **Save** to create the webhook.
Now, whenever you publish a new blog post in Contentful, a new build will be triggered and your blog will be updated.
---
File: /src/content/docs/en/guides/cms/cosmic.mdx
---
---
title: Cosmic & Astro
description: Add content to your Astro project using Cosmic as a CMS
sidebar:
label: Cosmic
type: cms
service: Cosmic
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import Grid from '~/components/FluidGrid.astro';
import Card from '~/components/ShowcaseCard.astro';
import { Steps } from '@astrojs/starlight/components';
[Cosmic](https://www.cosmicjs.com/) is a [headless CMS](https://www.cosmicjs.com/headless-cms) that provides an admin dashboard to manage content and an API that can integrate with a diverse range of frontend tools.
## Prerequisites
1. **An Astro project**- If you’d like to start with a fresh Astro project, read the [installation guide](/en/install-and-setup/). This guide follows a simplified version of the [Astro Headless CMS Theme](https://astro.build/themes/details/cosmic-cms-blog/) with [Tailwind CSS](https://tailwindcss.com/) for styling.
2. **A Cosmic account and Bucket** - [Create a free Cosmic account](https://app.cosmicjs.com/signup) if you don’t have one. After creating your account, you'll be prompted to create a new empty project. There's also a [Simple Astro Blog template](https://www.cosmicjs.com/marketplace/templates/simple-astro-blog) available (this is the same template as the Astro Headless CMS Theme) to automatically import all of the content used in this guide.
3. **Your Cosmic API keys** - From your Cosmic dashboard, you will need to locate both the **Bucket slug** and **Bucket read key** in order to connect to Cosmic.
## Integrating Cosmic with Astro
### Installing Necessary Dependencies
Add the [Cosmic JavaScript SDK](https://www.npmjs.com/package/@cosmicjs/sdk) to fetch data from your Cosmic Bucket.
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install @cosmicjs/sdk
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm add @cosmicjs/sdk
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn add @cosmicjs/sdk
```
</Fragment>
</PackageManagerTabs>
### Configuring API Keys
Create a `.env` file at the root of your Astro project (if it does not already exist). Add both the **Bucket slug** and **Bucket read key** available from your Cosmic dashboard as public environment variables.
```ini title=".env"
PUBLIC_COSMIC_BUCKET_SLUG=YOUR_BUCKET_SLUG
PUBLIC_COSMIC_READ_KEY=YOUR_READ_KEY
```
## Fetching Data from Cosmic
<Steps>
1. Create a new file called `cosmic.js`. Place this file inside of the `src/lib` folder in your project.
2. Add the following code to fetch data from Cosmic using the SDK and your environment variables.
The example below creates a function called `getAllPosts()` to fetch metadata from Cosmic `posts` objects:
```js
// src/lib/cosmic.js
import { createBucketClient } from '@cosmicjs/sdk'
const cosmic = createBucketClient({
bucketSlug: import.meta.env.PUBLIC_COSMIC_BUCKET_SLUG,
readKey: import.meta.env.PUBLIC_COSMIC_READ_KEY
})
export async function getAllPosts() {
const data = await cosmic.objects
.find({
type: 'posts'
})
.props('title,slug,metadata,created_at')
return data.objects
}
```
Read more about [queries in the Cosmic documentation](https://www.cosmicjs.com/docs/api/queries).
3. Import your query function in a `.astro` component. After fetching data, the results from the query can be used in your Astro template.
The following example shows fetching metadata from Cosmic `posts` and passing these values as props to a `<Card />` component to create a list of blog posts:
```astro
---
// src/pages/index.astro
import Card from '../components/Card.astro'
import { getAllPosts } from '../lib/cosmic'
const data = await getAllPosts()
---
<section>
<ul class="grid gap-8 md:grid-cols-2">
{
data.map((post) => (
<Card
title={post.title}
href={post.slug}
body={post.metadata.excerpt}
tags={post.metadata.tags.map((tag) => tag)}
/>
))
}
</ul>
</section>
```
[View source code for the Card component](https://github.com/cosmicjs/simple-astro-blog/blob/main/src/components/Card.astro)
</Steps>
## Building a Blog with Astro and Cosmic
You can manage your Astro blog's content using Cosmic and create webhooks to automatically redeploy your website when you update or add new content.
### Cosmic Content Objects
The following instructions assume that you have an **Object Type** in Cosmic called **posts**. Each individual blog post is a `post` that is defined in the Cosmic dashboard with the following Metafields:
1. **Text input** - Author
2. **Image** - Cover Image
3. **Repeater** - Tags
- **Text input** - Title
4. **Text area** - Excerpt
5. **Rich Text** - Content
### Displaying a List of Blog Posts in Astro
Using the same [data-fetching method](#fetching-data-from-cosmic) as above, import the `getAllPosts()` query from your script file and await the data. This function provides metadata about each `post` which can be displayed dynamically.
The example below passes these values to a `<Card />` component to display a formatted list of blog posts:
```astro
---
// src/pages/index.astro
import Card from '../components/Card.astro'
import { getAllPosts } from '../lib/cosmic'
const data = await getAllPosts()
---
<section>
<ul class="grid gap-8 md:grid-cols-2">
{
data.map((post) => (
<Card
title={post.title}
href={post.slug}
body={post.metadata.excerpt}
tags={post.metadata.tags.map((tag) => tag)}
/>
))
}
</ul>
</section>
```
### Generating Individual Blog Posts with Astro
To generate an individual page with full content for each blog post, create a [dynamic routing page](/en/guides/routing/#dynamic-routes) at `src/pages/blog/[slug].astro`.
This page will export a `getStaticPaths()` function to generate a page route at the `slug` returned from each `post` object. This function is called at build time and generates and renders all of your blog posts at once.
To access your data from Cosmic:
- Query Cosmic inside of `getStaticPaths()` to fetch data about each post and provide it to the function.
- Use each `post.slug` as a route parameter, creating the URLs for each blog post.
- Return each `post` inside of `Astro.props`, making the post data available to HTML template portion of the page component for rendering.
The HTML markup below uses various data from Cosmic, such as the post title, cover image, and the **rich text content** (full blog post content). Use [set&colon;html](/en/reference/directives-reference/#sethtml) on the element displaying the rich text content from Cosmic to render HTML elements on the page exactly as fetched from Cosmic.
```astro
---
// src/pages/blog/[slug].astro
import { getAllPosts } from '../../lib/cosmic'
import { Image } from 'astro:assets'
export async function getStaticPaths() {
const data = (await getAllPosts()) || []
return data.map((post) => {
return {
params: { slug: post.slug },
props: { post }
}
})
}
const { post } = Astro.props
---
<article class="mx-auto max-w-screen-md pt-20">
<section class="border-b pb-8">
<h1 class="text-4xl font-bold">{post.title}</h1>
<div class="my-4"></div>
<span class="text-sm font-semibold">{post.metadata.author?.title}</span>
</section>
{
post.metadata.cover_image && (
<Image
src={post.metadata.cover_image.imgix_url}
format="webp"
width={1200}
height={675}
aspectRatio={16 / 9}
quality={50}
alt={`Cover image for the blog ${post.title}`}
class={'my-12 rounded-md shadow-lg'}
/>
)
}
<div set:html={post.metadata.content} />
</article>
```
### Publishing your site
To deploy your website, visit the [deployment guides](/en/guides/deploy/) and follow the instructions for your preferred hosting provider.
#### Rebuild on Cosmic content updates
You can set up a webhook in Cosmic directly to trigger a redeploy of your site on your hosting platform (e.g. Vercel) whenever you update or add content Objects.
Under "Settings" > "webhooks", add the URL endpoint from Vercel and select the Object Type you would like to trigger the webhook. Click “Add webhook” to save it.
##### Netlify
To set up a webhook in Netlify:
<Steps>
1. Go to your site dashboard and click on **Build & deploy**.
2. Under the **Continuous Deployment** tab, find the **Build hooks** section and click on **Add build hook**.
3. Provide a name for your webhook and select the branch you want to trigger the build on. Click on **Save** and copy the generated URL.
</Steps>
##### Vercel
To set up a webhook in Vercel:
<Steps>
1. Go to your project dashboard and click on **Settings**.
2. Under the **Git** tab, find the **Deploy Hooks** section.
3. Provide a name for your webhook and the branch you want to trigger the build on. Click **Add** and copy the generated URL.
</Steps>
## Themes
<Grid>
<Card title="Astro Headless CMS Blog" href="https://astro.build/themes/details/cosmic-cms-blog/" thumbnail="simple-astro-blog.png" />
</Grid>
---
File: /src/content/docs/en/guides/cms/craft-cms.mdx
---
---
title: Craft CMS & Astro
description: Add content to your Astro project using Craft CMS as a CMS
sidebar:
label: Craft CMS
type: cms
service: Craft CMS
i18nReady: true
stub: true
---
[Craft CMS](https://craftcms.com/) is a flexible open source CMS with an accessible authoring experience. It includes its own front end, but can also be used as a headless CMS to provide content to your Astro project.
## Official Resources
- Check out the official Craft CMS [GraphQL API guide](https://craftcms.com/docs/5.x/development/graphql.html)
- The official documentation for Craft's [`headlessMode` config setting](https://craftcms.com/docs/5.x/reference/config/general.html#headlessmode)
## Community Resources
- [SSG Astro with Headless Craft CMS Content Fetched At Build Time](https://www.olets.dev/posts/ssg-astro-with-headless-craft-cms-content-fetched-at-build-time/)
- [SSG Astro with Headless Craft CMS Content Fetched At Build Time Or Cached In Advance](https://www.olets.dev/posts/ssg-astro-with-headless-craft-cms-content-fetched-at-build-time-or-cached-in-advance/)
- [SSR Astro With Headless Craft CMS](https://www.olets.dev/posts/ssr-astro-with-headless-craft-cms/)
---
File: /src/content/docs/en/guides/cms/crystallize.mdx
---
---
title: Crystallize & Astro
description: Add content to your Astro project using Crystallize as a CMS
sidebar:
label: Crystallize
type: cms
stub: true
service: Crystallize
i18nReady: true
---
[Crystallize](https://crystallize.com/) is a headless content management system for eCommerce that exposes a GraphQL API.
## Example
```astro title="src/pages/index.astro"
---
// Fetch your catalogue paths from Crystallize GraphQL API
import BaseLayout from '../../layouts/BaseLayout.astro';
import { createClient } from '@crystallize/js-api-client';
const apiClient = createClient({
tenantIdentifier: 'furniture'
});
const query = `
query getCataloguePaths{
catalogue(language: "en", path: "/shop") {
name
children {
name
path
}
}
}
`
const { data: { catalogue } } = await apiClient.catalogueApi(query)
---
<BaseLayout>
<h1>{catalogue.name}</h1>
<nav>
<ul>
{catalogue.children.map(child => (
<li><a href={child.path}>{child.name}</a></li>
))}
</ul>
</nav>
</BaseLayout>
```
---
File: /src/content/docs/en/guides/cms/datocms.mdx
---
---
title: DatoCMS & Astro
description: Add content to your Astro project using DatoCMS
sidebar:
label: DatoCMS
type: cms
stub: false
service: DatoCMS
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
import { FileTree } from '@astrojs/starlight/components';
[DatoCMS](https://datocms.com/) is a web-based, headless CMS to manage digital content for your sites and apps.
## Integrating with Astro
In this guide, you will fetch content data from DatoCMS in your Astro project, then display it on a page.
## Prerequisites
To get started, you will need to have the following:
- **An Astro project** - If you don’t have an Astro project yet, you can follow the instructions in our [Installation guide](/en/install-and-setup/).
- **A DatoCMS account and project** - If you don't have an account, you can [sign up for a free account](https://dashboard.datocms.com/signup).
- **The read-only API Key for your DatoCMS project** - You can find it in the admin dashboard of your project, under "Settings" > "API Tokens".
## Setting up the credentials
Create a new file (if one does not already exist) named `.env` in the root of your Astro project. Add a new environment variable as follows, using the API key found in your DatoCMS admin dashboard:
```ini title=".env"
DATOCMS_API_KEY=YOUR_API_KEY
```
For TypeScript support, declare the typing of this environment variable in the `env.d.ts` file in the `src/` folder. If this file does not exist, you can create it and add the following:
```ts title="src/env.d.ts"
interface ImportMetaEnv {
readonly DATOCMS_API_KEY: string;
}
```
Your root directory should now include these files:
<FileTree title="Project Structure">
- src/
- **env.d.ts**
- **.env**
- astro.config.mjs
- package.json
</FileTree>
## Create a Model in DatoCMS
In the DatoCMS admin dashboard of your project, navigate to "Settings" > "Models" and create a new Model called "Home" with the "Single Instance" toggle selected. This will create a home page for your project. In this model, add a new text field for the page title.
Navigate to the "Content" tab in your project and click on your newly-created home page. You can now add a title. Save the page, and continue.
## Fetching data
In your Astro project, navigate to the page that will fetch and display your CMS content. Add the following query to fetch the content for `home` using the DatoCMS GraphQL API.
This example displays the page title from DatoCMS on `src/pages/index.astro`:
```astro title="src/pages/index.astro"
---
const response = await fetch('https://graphql.datocms.com/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: `Bearer ${import.meta.env.DATOCMS_API_KEY}`,
},
body: JSON.stringify({
query: `query Homepage {
home {
title
}
}
`,
}),
});
const json = await response.json();
const data = json.data.home;
---
<h1>{data.title}</h1>
```
This GraphQL query will fetch the `title` field in the `home` page from your DatoCMS Project. When you refresh your browser, you should see the title on your page.
## Adding Dynamic modular content blocks
If your DatosCMS project includes [modular content](https://www.datocms.com/docs/content-modelling/modular-content), then you will need to build a corresponding `.astro` component for each block of content (e.g. a text section, a video, a quotation block, etc.) that the modular field allows in your project.
The example below is a `<Text />` Astro component for displaying a "Multiple-paragraph text" block from DatoCMS.
```astro title="src/components/Text.astro"
---
export interface TextProps {
text: string
}
export interface Props {
item: TextProps
}
const { item } = Astro.props;
---
<div set:html={item.text} />
```
To fetch these blocks, edit your GraphQL query to include the modular content block you created in DatoCMS.
In this example, the modular content block is named **content** in DatoCMS. This query also includes the unique `_modelApiKey` of each item to check which block should be displayed in the modular field, based on which block was chosen by the content author in the DatoCMS editor. Use a switch statement in the Astro template to allow for dynamic rendering based on the data received from the query.
The following example represents a DatoCMS modular content block that allows an author to choose between a text field (`<Text />`) and an image (`<Image />`) rendered on the home page:
```astro title="src/pages/index.astro" ins={2,3, 16-27, 39-50}
---
import Image from '../components/Image.astro';
import Text from '../components/Text.astro';
const response = await fetch('https://graphql.datocms.com/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: `Bearer ${import.meta.env.DATOCMS_API_KEY}`,
},
body: JSON.stringify({
query: `query Homepage {
home {
title
content {
... on ImageRecord {
_modelApiKey
image{
url
}
}
... on TextRecord {
_modelApiKey
text(markdown: true)
}
}
}
}
`,
}),
});
const json = await response.json();
const data = json.data.home;
---
<h1>{data.title}</h1>
{
data.content.map((item: any) => {
switch (item._modelApiKey) {
case 'image':
return <Image item={item} />;
case 'text':
return <Text item={item} />;
default:
return null;
}
})
}
```
## Publishing your site
To deploy your website, visit our [deployment guides](/en/guides/deploy/) and follow the instructions for your preferred hosting provider.
## Publish on DatoCMS content changes
If your project is using Astro’s default static mode, you will need to set up a webhook to trigger a new build when your content changes. If you are using Netlify or Vercel as your hosting provider, you can use its webhook feature to trigger a new build when you change content in DatoCMS.
### Netlify
To set up a webhook in Netlify:
<Steps>
1. Go to your site dashboard and click on **Build & deploy**.
2. Under the **Continuous Deployment** tab, find the **Build hooks** section and click on **Add build hook**.
3. Provide a name for your webhook and select the branch you want to trigger the build on. Click on **Save** and copy the generated URL.
</Steps>
### Vercel
To set up a webhook in Vercel:
<Steps>
1. Go to your project dashboard and click on **Settings**.
2. Under the **Git** tab, find the **Deploy Hooks** section.
3. Provide a name for your webhook and the branch you want to trigger the build on. Click **Add** and copy the generated URL.
</Steps>
### Adding a webhook to DatoCMS
In your DatoCMS project admin dashboard, navigate to the **Settings** tab and click **Webhooks**. Click the plus icon to create a new webhook and give it a name. In the URL field, paste the URL generated by your preferred hosting service. As Trigger, select whichever option suits your needs. (For example: build every time a new record is published.)
## Starter project
You can also check out the [Astro blog template](https://www.datocms.com/marketplace/starters/astro-template-blog) on the DatoCMS marketplace to learn how to create a blog with Astro and DatoCMS.
---
File: /src/content/docs/en/guides/cms/decap-cms.mdx
---
---
title: Decap CMS & Astro
description: Add content to your Astro project using Decap as a CMS
sidebar:
label: Decap CMS
type: cms
stub: true
service: Decap CMS
i18nReady: true
---
import Grid from '~/components/FluidGrid.astro';
import Card from '~/components/ShowcaseCard.astro';
import { Steps } from '@astrojs/starlight/components';
import { FileTree } from '@astrojs/starlight/components';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
[Decap CMS](https://www.decapcms.org/) (formerly Netlify CMS) is an open-source, Git-based content management system.
Decap allows you to take full advantage of all of Astro's features, including image optimization and content collections.
Decap adds a route (typically `/admin`) to your project that will load a React app to allow authorized users to manage content directly from the deployed website. Decap will commit changes directly to your Astro project's source repository.
## Installing DecapCMS
There are two options for adding Decap to Astro:
1. [Install Decap via a package manager](https://decapcms.org/docs/install-decap-cms/#installing-with-npm) with the following command:
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install decap-cms-app
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm add decap-cms-app
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn add decap-cms-app
```
</Fragment>
</PackageManagerTabs>
2. Import the package into a `<script>` tag in your page `<body>`
```html title='/admin'
<body>
<!-- Include the script that builds the page and powers Decap CMS -->
<script src="https://unpkg.com/decap-cms@^3.1.2/dist/decap-cms.js"></script>
</body>
```
## Configuration
<Steps>
1. Create a static admin folder at `public/admin/`
2. Add `config.yml` to `public/admin/`:
<FileTree>
- public
- admin
- config.yml
</FileTree>
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:
```yml title="/public/admin/config.yml"
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`.
<FileTree>
- public
- admin
- config.yml
- src
- pages
- admin.html
</FileTree>
```html title="/src/pages/admin.html" {7, 11}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="robots" content="noindex" />
<link href="/admin/config.yml" type="text/yaml" rel="cms-config-url" />
<title>Content Manager</title>
</head>
<body>
<script src="https://unpkg.com/decap-cms@^3.1.2/dist/decap-cms.js"></script>
</body>
</html>
```
5. To enable media uploads to a specific folder via the Decap editor, add an appropriate path:
```yml title="/public/admin/config.yml"
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
```
</Steps>
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
## Production Sites
The following sites use Astro + Decap CMS in production:
- [yunielacosta.com](https://www.yunielacosta.com/) by Yuniel Acosta — [source code on GitHub](https://github.com/yacosta738/yacosta738.github.io) (Netlify CMS)
- [portfolioris.nl](https://www.portfolioris.nl/) by Joris Hulsbosch – [source code on GitHub](https://github.com/portfolioris/portfolioris.nl)
## Themes
<Grid >
<Card title="Astros" href="https://astro.build/themes/details/astros" thumbnail="astros.png"/>
<Card title="Enhanced Astro Starter" href="https://astro.build/themes/details/enhanced-astro-starter" thumbnail="enhanced-astro-starter.png"/>
<Card title="Astro Decap CMS Starter" href="https://astro.build/themes/details/astro-decap-cms-starter" thumbnail="astro-decap-starter.png"/>
</Grid>
---
File: /src/content/docs/en/guides/cms/directus.mdx
---
---
title: Directus & Astro
description: Add content to your Astro project using Directus as a CMS
sidebar:
label: Directus
type: cms
stub: true
service: Directus
i18nReady: true
---
import { CardGrid, LinkCard } from '@astrojs/starlight/components';
[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
<CardGrid>
<LinkCard title="Using Directus CMS with Neon Postgres and Astro to build a blog" href="https://neon.tech/guides/directus-cms" />
</CardGrid>
:::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)!
:::
---
File: /src/content/docs/en/guides/cms/drupal.mdx
---
---
title: Drupal & Astro
description: Add content to your Astro project using Drupal as a CMS
sidebar:
label: Drupal
type: cms
service: Drupal
i18nReady: true
stub: true
---
import { FileTree, Steps, CardGrid, LinkCard } from '@astrojs/starlight/components';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import ReadMore from '~/components/ReadMore.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:
```ini title=".env"
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.
```ini title=".env"
DRUPAL_BASIC_USERNAME="editor"
DRUPAL_BASIC_PASSWORD="editor"
DRUPAL_JWT_TOKEN="abc123"
...
```
<ReadMore>Read more about [using environment variables](/en/guides/environment-variables/) and `.env` files in Astro.</ReadMore>
Your root directory should now include this new files:
<FileTree title="Project Structure">
- **.env**
- astro.config.mjs
- package.json
</FileTree>
### 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.
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install jsona drupal-jsonapi-params
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm add jsona drupal-jsonapi-params
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn add jsona drupal-jsonapi-params
```
</Fragment>
</PackageManagerTabs>
## 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:
```astro
---
import {Jsona} from "jsona";
import {DrupalJsonApiParams} from "drupal-jsonapi-params";
import type {TJsonApiBody} from "jsona/lib/JsonaTypes";
// Get the Drupal base URL
export const baseUrl: string = import.meta.env.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);
---
<body>
{articles?.length ? articles.map((article: any) => (
<section>
<h2>{article.title}</h2>
<article set:html={article.body.value}></article>
</section>
)): <div><h1>No Content found</h1></div> }
</body>
```
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
<Steps>
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.
```ts title="src/types.ts"
export interface Path {
alias: string
pid: number
langcode: string
}
export interface DrupalNode extends Record<string, any> {
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:
<FileTree title="Project Structure">
- .env
- astro.config.mjs
- package.json
- src/
- **types.ts**
</FileTree>
2. Create a new file called `drupal.ts` under `src/api` and add the following code:
```ts title="src/api/drupal.ts"
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:
<FileTree title="Project Structure">
- .env
- astro.config.mjs
- package.json
- src/
- api/
- **drupal.ts**
- types.ts
</FileTree>
3. In that same file, create the `fetchUrl` function to make the fetch request and deserialize the response.
```ts title="src/api/drupal.ts" ins={9-23}
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<TJsonaModel | TJsonaModel[]> as Promise<any>
*/
export const fetchUrl = async (url: string): Promise<any> => {
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.
```ts title="src/api/drupal.ts" ins={23-40}
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<TJsonaModel | TJsonaModel[]> as Promise<any>
*/
export const fetchUrl = async (url: string): Promise<any> => {
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<DrupalNode[]>
*/
export const getArticles = async (): Promise<DrupalNode[]> => {
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.
```astro title="src/pages/articles/index.astro"
---
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:
<FileTree title="Project Structure">
- .env
- astro.config.mjs
- package.json
- src/
- api/
- drupal.ts
- pages/
- articles/
- **index.astro**
- types.ts
</FileTree>
6. Add content to your page, such as a title. Use `articles.map()` to show your Drupal entries as line items in a list.
```astro title="src/pages/articles/index.astro" ins={12-29}
---
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();
---
<html lang="en">
<head>
<title>My news site</title>
</head>
<body>
<h1>My news site</h1>
<ul>
{articles.map((article: DrupalNode) => (
<li>
<a href={article.path.alias.replace("internal:en/", "")}>
<h2>{article.title}</h2>
<p>Published on {article.created}</p>
</a>
</li>
))}
</ul>
</body>
</html>
```
</Steps>
### 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.
<Steps>
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.
```astro title="src/pages/articles/[path].astro"
---
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:
<FileTree title="Project Structure">
- .env
- astro.config.mjs
- package.json
- src/
- api/
- drupal.ts
- pages/
- articles/
- index.astro
- **[path].astro**
- types.ts
</FileTree>
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.
```astro title="src/pages/articles/[path].astro" ins={12-33}
---
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.
```astro title="src/pages/articles/[path].astro" ins={30, 32-42}
---
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;
---
<html lang="en">
<head>
<title>{title}</title>
</head>
<body>
<h1>{title}</h1>
<time>{date}</time>
<article set:html={body.value} />
</body>
</html>
```
4. Navigate to your dev server preview and click on one of your posts to make sure your dynamic route is working.
</Steps>
### 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
<CardGrid>
<LinkCard title="Create a web application with Astro and Drupal" href="https://www.linkedin.com/pulse/create-web-application-astrojs-website-generator-using-gambino-kx6cf"/>
</CardGrid>
:::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)!
:::
---
File: /src/content/docs/en/guides/cms/flotiq.mdx
---
---
title: Flotiq & Astro
description: Add content to your Astro project using Flotiq as a CMS
sidebar:
label: Flotiq
type: cms
service: Flotiq
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import { Steps } from '@astrojs/starlight/components';
[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:
```ini title=".env"
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:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm install flotiq-api-ts
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm add flotiq-api-ts
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn add flotiq-api-ts
```
</Fragment>
</PackageManagerTabs>
Next, configure the SDK using your credentials. Create a new file named `flotiq.ts` inside the `src/lib` directory of your project:
```ts title="src/lib/flotiq.ts"
import { FlotiqApi } from "flotiq-api-ts";
export const flotiq = new FlotiqApi(import.meta.env.FLOTIQ_API_KEY);
```
This configuration can now be used throughout your project.
### Fetching and Displaying Data from Flotiq
<Steps>
1. Fetch the `Blog Post` data on an Astro page using your content's custom API `BlogpostAPI`:
```astro title="src/pages/index.astro"
---
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:
```astro title="src/pages/index.astro" ins={6-21}
---
import { flotiq } from "../lib/flotiq";
const posts = await flotiq.BlogpostAPI.list();
---
<html lang="en">
<head>
<title>Astro</title>
</head>
<body>
{posts.data?.map((post) => (
<section>
<a href={`/posts/${post.slug}`}>
<h2>{post.title}</h2>
</a>
<div>{post.internal?.createdAt}</div>
<div set:html={post.content}/>
</section>
))}
</body>
</html>
```
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.
</Steps>
### 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.
<Steps>
1. Create a new file `[slug].astro` in the `/src/pages/posts/` directory. Fetch all blog posts and return them within the `getStaticPaths()` method:
```astro title="src/pages/posts/[slug].astro"
---
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:
```astro title="src/pages/posts/[slug].astro" ins={12-20}
---
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;
---
<html lang="en">
<title>{post.title}</title>
<body>
<h1>{post.title}</h1>
<div set:html={post.content}/>
</body>
</html>
```
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.
</Steps>
#### 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`.
<Steps>
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:
```astro title="src/pages/posts/[slug].astro"
---
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:
```astro title="src/pages/posts/[slug].astro" ins={24-30}
---
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');
}
---
<html lang="en">
<title>{post.title}</title>
<body>
<h1>{post.title}</h1>
<div set:html={post.content}/>
</body>
</html>
```
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.
</Steps>
### 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:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm rebuild flotiq-api-ts
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm rebuild flotiq-api-ts
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn rebuild flotiq-api-ts
// for yarn v1 (Classic):
// yarn add flotiq-api-ts
```
</Fragment>
</PackageManagerTabs>
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
---
File: /src/content/docs/en/guides/cms/frontmatter-cms.mdx
---
---
title: Front Matter CMS & Astro
description: Add content to your Astro project using Front Matter CMS
sidebar:
label: Front Matter CMS
type: cms
service: Front Matter CMS
i18nReady: true
---
import { FileTree } from '@astrojs/starlight/components';
[Front Matter CMS](https://frontmatter.codes/) brings the CMS to your editor, it runs within Visual Studio Code, Gitpod, and many more.
## 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: <kbd>alt</kbd> + <kbd>d</kbd> (Windows & Linux) or <kbd>options</kbd> + <kbd>d</kbd> (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.
<FileTree title="Project Structure">
- .frontmatter/
- database/
- mediaDb.json
- src/
- astro.config.mjs
- **frontmatter.json**
- package.json
</FileTree>
#### 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:
```json title="frontmatter.json"
"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:
```json title="frontmatter.json"
"frontMatter.content.supportedFileTypes": [ "md", "markdown", "mdx", "mdoc" ]
```
To allow your content to be created as Markdoc, specify the `fileType` property on the content-type.
```json title="frontmatter.json" ins={7}
"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)
---
File: /src/content/docs/en/guides/cms/ghost.mdx
---
---
title: Ghost & Astro
description: Add content to your Astro project using Ghost as a CMS
sidebar:
label: Ghost
type: cms
stub: false
service: Ghost
i18nReady: true
---
import { FileTree, CardGrid, LinkCard } from '@astrojs/starlight/components';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
[Ghost](https://github.com/TryGhost/Ghost) is an open-source, headless content management system built on Node.js.
## Integrating with Astro
In this section, we’ll use the [Ghost content API](https://ghost.org/docs/content-api/) to bring your 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 Ghost site** - It is assumed that you have a site set up with Ghost. If you don't you can set one up on a [local environment.](https://ghost.org/docs/install/local/)
3. **Content API Key** - You can make an integration under your site's `Settings > Integrations`. From there you can find your `Content API key`
### Setting up credentials
To add your site's credentials to Astro, create an `.env` file in the root of your project with the following variable:
```ini title=".env"
CONTENT_API_KEY=YOUR_API_KEY
```
Now, you should be able to use this environment variable in your project.
If you would like to have IntelliSense for your environment variable, you can create a `env.d.ts` file in the `src/` directory and configure `ImportMetaEnv` like this:
```ts title="src/env.d.ts"
interface ImportMetaEnv {
readonly CONTENT_API_KEY: string;
}
```
:::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:
<FileTree title="Project Structure">
- src/
- **env.d.ts**
- **.env**
- astro.config.mjs
- package.json
</FileTree>
### 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:
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install @tryghost/content-api
npm install --save @types/tryghost__content-api
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm add @tryghost/content-api
pnpm add --save-dev @types/tryghost__content-api
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn add @tryghost/content-api
yarn add --dev @types/tryghost__content-api
```
</Fragment>
</PackageManagerTabs>
## 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.
<FileTree title="Project Structure">
- src/
- lib/
- **ghost.ts**
- pages/
- index.astro
- astro.config.mjs
- package.json
</FileTree>
Initialize an API instance with the Ghost API using the API key from the Ghost dashboard's Integrations page.
```ts title="src/lib/ghost.ts"
import GhostContentAPI from '@tryghost/content-api';
// Create API instance with site credentials
export const ghostClient = new GhostContentAPI({
url: 'http://127.0.0.1:2368', // This is the default URL if your site is running on a local environment
key: import.meta.env.CONTENT_API_KEY,
version: 'v5.0',
});
```
### 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.
<FileTree title="Project Structure">
- src/
- lib/
- ghost.ts
- pages/
- **index.astro**
- astro.config.mjs
- package.json
</FileTree>
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.
```astro title="src/pages/index.astro"
---
import { ghostClient } from '../lib/ghost';
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.
```astro title="src/pages/index.astro"
---
import { ghostClient } from '../lib/ghost';
const posts = await ghostClient.posts
.browse({
limit: 'all',
})
.catch((err) => {
console.error(err);
});
---
<html lang="en">
<head>
<title>Astro + Ghost 👻</title>
</head>
<body>
{
posts.map((post) => (
<a href={`/post/${post.slug}`}>
<h1> {post.title} </h1>
</a>
))
}
</body>
</html>
```
### Generating pages
The page `src/pages/post/[slug].astro` [dynamically generates a page](/en/guides/routing/#dynamic-routes) for each post.
<FileTree title="Project Structure">
- src/
- lib/
- ghost.ts
- pages/
- index.astro
- post/
- **[slug].astro**
- astro.config.mjs
- package.json
</FileTree>
Import `ghostClient()` to access blog posts using `posts.browse()` and return a post as props to each of your dynamic routes.
```astro title="src/pages/post/[slug].astro"
---
import { ghostClient } from '../../lib/ghost';
export async function getStaticPaths() {
const posts = await ghostClient.posts
.browse({
limit: 'all',
})
.catch((err) => {
console.error(err);
});
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.
```astro title="src/pages/post/[slug].astro" ins={24-37}
---
import { ghostClient } from '../../lib/ghost';
export async function getStaticPaths() {
const posts = await ghostClient.posts
.browse({
limit: 'all',
})
.catch((err) => {
console.error(err);
});
return posts.map((post) => {
return {
params: {
slug: post.slug,
},
props: {
post: post,
},
};
});
}
const { post } = Astro.props;
---
<!DOCTYPE html>
<html lang="en">
<head>
<title>{post.title}</title>
</head>
<body>
<img src={post.feature_image} alt={post.title} />
<h1>{post.title}</h1>
<p>{post.reading_time} min read</p>
<Fragment set:html={post.html} />
</body>
</html>
```
:::note
`<Fragment />` 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
<CardGrid>
<LinkCard title="Ghost CMS & Astro Tutorial" href="https://matthiesen.xyz/blog/astro-ghostcms" />
<LinkCard title="Astro + Ghost + Tailwind CSS" href="https://andr.ec/posts/astro-ghost-blog/" />
<LinkCard title="Building a Corporate site with Astro and Ghost" href="https://artabric.com/post/building-a-corporate-site-with-astro-and-ghost/" />
<LinkCard title="`astro-starter-ghost`" href="https://github.com/PhilDL/astro-starter-ghost" />
</CardGrid>
:::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)!
:::
---
File: /src/content/docs/en/guides/cms/gitcms.mdx
---
---
title: GitCMS & Astro
description: Integrate GitCMS into your Astro project for seamless content management
sidebar:
label: GitCMS
type: cms
service: GitCMS
i18nReady: true
---
[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)
---
File: /src/content/docs/en/guides/cms/hashnode.mdx
---
---
title: Hashnode & Astro
description: Add content to your Astro project using Hashnode as a CMS
sidebar:
label: Hashnode
type: cms
stub: false
service: Hashnode
i18nReady: true
---
import { FileTree } from '@astrojs/starlight/components';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
import { Steps } from '@astrojs/starlight/components';
[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:
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install graphql-request
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm add graphql-request
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn add graphql-request
```
</Fragment>
</PackageManagerTabs>
## 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
<Steps>
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`:
<FileTree title="Project Structure">
- src/
- lib/
- **client.ts**
- **schema.ts**
- pages/
- index.astro
- astro.config.mjs
- package.json
</FileTree>
2. Initialize an API instance with the GraphQLClient using the URL from your Hashnode Website.
```ts title="src/lib/client.ts" "astroplayground.hashnode.dev"
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<AllPostsData>(
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<PostData>(
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.
```ts title="src/lib/schema.ts"
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<typeof PostSchema>
export type AllPostsData = z.infer<typeof AllPostsDataSchema>
export type PostData = z.infer<typeof PostDataSchema>
```
</Steps>
### 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.
```astro title="src/pages/index.astro"
---
import { getAllPosts } from '../lib/client';
const data = await getAllPosts();
const allPosts = data.publication.posts.edges;
---
<html lang="en">
<head>
<title>Astro + Hashnode</title>
</head>
<body>
{
allPosts.map((post) => (
<div>
<h2>{post.node.title}</h2>
<p>{post.node.brief}</p>
<img src={post.node.coverImage.url} alt={post.node.title} />
<a href={`/post/${post.node.slug}`}>Read more</a>
</div>
))
}
</body>
</html>
```
### Generating pages
<Steps>
1. Create the page `src/pages/post/[slug].astro` to [dynamically generate a page](/en/guides/routing/#dynamic-routes) for each post.
<FileTree title="Project Structure">
- src/
- lib/
- client.ts
- schema.ts
- pages/
- index.astro
- post/
- **[slug].astro**
- astro.config.mjs
- package.json
</FileTree>
2. Import and use `getAllPosts()` and `getPost()` to fetch the data from Hashnode and generate individual page routes for each post.
```astro title="src/pages/post/[slug].astro"
---
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:
```astro title="src/pages/post/[slug].astro"
---
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);
---
<!DOCTYPE html>
<html lang="en">
<head>
<title>{post.title}</title>
</head>
<body>
<img src={post.coverImage.url} alt={post.title} />
<h1>{post.title}</h1>
<p>{post.readTimeInMinutes} min read</p>
<Fragment set:html={post.content.html} />
</body>
</html>
```
:::note
`<Fragment />` 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/)).
:::
</Steps>
### 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
---
File: /src/content/docs/en/guides/cms/hygraph.mdx
---
---
title: Hygraph & Astro
description: Add content to your Astro project using Hygraph as a CMS
sidebar:
label: Hygraph
type: cms
service: Hygraph
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
import { FileTree } from '@astrojs/starlight/components';
[Hygraph](https://hygraph.com/) is a federated content management platform. It exposes a GraphQL endpoint for fetching content.
## Integrating with Astro
In this section, you'll create a [Hygraph](https://hygraph.com/) GraphQL endpoint to fetch with Astro.
### Prerequisites
To get started, you will need to have the following:
1. **A Hygraph account and project**. If you don't have an account, you can [sign up for free](https://app.hygraph.com/signup) and create a new project.
2. **Hygraph access permissions** - In `Project Settings > API Access`, configure Public Content API permissions to allow read requests without authentication. If you haven't set any permissions, you can click **Yes, initialize defaults** to add the required permissions to use the "High Performance Content API".
### Setting up the endpoint
To add your Hygraph endpoint to Astro, create a `.env` file in the root of your project with the following variable:
```ini title=".env"
HYGRAPH_ENDPOINT=YOUR_HIGH_PERFORMANCE_API
```
Now, you should be able to use this environment variable in your project.
If you would like to have IntelliSense for your environment variables, you can create a `env.d.ts` file in the `src/` directory and configure `ImportMetaEnv` like this:
```ts title="src/env.d.ts"
interface ImportMetaEnv {
readonly HYGRAPH_ENDPOINT: string;
}
```
:::note
Read more about [using environment variables](/en/guides/environment-variables/) and `.env` files in Astro.
:::
Your root directory should now include these new files:
<FileTree title="Project Structure">
- src/
- **env.d.ts**
- **.env**
- astro.config.mjs
- package.json
</FileTree>
### Fetching data
Fetch data from your Hygraph project by using the `HYGRAPH_ENDPOINT`.
For example, to fetch entries of a `blogPosts` content type that has a string field `title`, create a GraphQL query to fetch that content. Then, define the type of the content, and set it as the type of the response.
```astro title="src/pages/index.astro"
---
interface Post {
title: string;
}
const query = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query: `
{
blogPosts {
title
}
}`,
}),
};
const response = await fetch(import.meta.env.HYGRAPH_ENDPOINT, query);
const json = await response.json();
const posts: Post[] = json.data.blogPosts;
---
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Astro</title>
</head>
<body>
<h1>Astro</h1>
{
posts.map((post) => (
<div>
<h2>{post.title}</h2>
</div>
))
}
</body>
</html>
```
## Deploy
### Configuring Netlify Webhook
To set up a webhook in Netlify:
<Steps>
1. Deploy your site to Netlify with [this guide](/en/guides/deploy/netlify/). Remember to add your `HYGRAPH_ENDPOINT` to the environment variables.
2. Then Go to your Hygraph project dashboard and click on **Apps**.
3. Go to the marketplace and search for Netlify and follow the instructions provided.
4. If all went good, now you can deploy your website with a click in the Hygraph interface.
</Steps>
## Community Resources
- [Hygraph + Astro example with `marked` for markdown parsing](https://github.com/camunoz2/example-astrowithhygraph)
---
File: /src/content/docs/en/guides/cms/index.mdx
---
---
title: Use a CMS with Astro
description: How to use a CMS to add content to Astro
sidebar:
label: CMS overview
i18nReady: true
---
import CMSGuidesNav from '~/components/CMSGuidesNav.astro';
import ReadMore from '~/components/ReadMore.astro';
import Badge from "~/components/Badge.astro"
**Ready to connect a Headless CMS to your Astro project?** Follow one of our guides to integrate a CMS.
:::tip
Find [community-maintained integrations](https://astro.build/integrations/?search=cms) for connecting a CMS to your project in our integrations directory.
:::
## CMS Guides
Note that many of these pages are **stubs**: they're collections of resources waiting for your contribution!
<CMSGuidesNav />
## Why use a CMS?
A Content Management System lets you write content and manage assets outside of your Astro project.
This unlocks new features for working with content. Most CMSes give you a visual content editor, the ability to specify standard types of content, and a way to collaborate with others.
A CMS can be useful for content that follows a particular structure, often giving you a dashboard-like experience and WYSIWYG editing tools. You might use a CMS to write blog posts using a CMS's rich text editor instead of Markdown files. Or you might use a CMS to maintain product listings for an eCommerce shop, making certain fields required to avoid incomplete listings.
Your Astro project can then fetch your content from your CMS and display it, wherever and however you want on your site.
## Which CMSes work well with Astro?
Because Astro takes care of the _presentation_ of your content, you'll want to choose a _headless_ CMS, like those in the list above. This means that the CMS helps you write your content, but doesn't generate a site that displays it. Instead, you fetch the content data and use in your Astro project.
Some headless CMSes, like Storyblok, provide an Astro [integration](/en/guides/integrations-guide/) that helps fetch the content specifically for an Astro site. Others provide a JavaScript SDK, a library that you install and use to fetch your remote content.
<ReadMore> Explore a [list of over 100 headless content management systems](https://jamstack.org/headless-cms/) <Badge text="External" /> where you can filter by type (e.g. Git-based, API driven) and license (open-source or closed-source).</ReadMore>
## Can I use Astro without a CMS?
Yes! Astro provides built-in support for [Markdown](/en/guides/markdown-content/).
---
File: /src/content/docs/en/guides/cms/keystatic.mdx
---
---
title: Keystatic & Astro
description: Add content to your Astro project using Keystatic as a CMS
sidebar:
label: Keystatic
type: cms
service: Keystatic
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
import { FileTree } from '@astrojs/starlight/components';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.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:
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm create @keystatic@latest
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm create @keystatic@latest
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn create @keystatic
```
</Fragment>
</PackageManagerTabs>
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.
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npx astro add react markdoc
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm astro add react markdoc
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn astro add react markdoc
```
</Fragment>
</PackageManagerTabs>
You will also need two Keystatic packages:
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install @keystatic/core @keystatic/astro
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm add @keystatic/core @keystatic/astro
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn add @keystatic/core @keystatic/astro
```
</Fragment>
</PackageManagerTabs>
## Adding the Astro integration
Add the Astro integration from `@keystatic/astro` in your Astro config file:
```js ins={6} ins=", keystatic()"
// astro.config.mjs
import { defineConfig } from 'astro/config'
import react from '@astrojs/react'
import markdoc from '@astrojs/markdoc'
import keystatic from '@keystatic/astro'
// https://astro.build/config
export default defineConfig({
integrations: [react(), markdoc(), keystatic()],
output: 'static',
})
```
## Creating a Keystatic config file
A Keystatic config file is required to define your content schema. This file will also allow you to connect a project to a specific GitHub repository (if you decide to do so).
Create a file called `keystatic.config.ts` in the root of the project and add the following code to define both your storage type (`local`) and a single content collection (`posts`):
```ts {8-25}
// keystatic.config.ts
import { config, fields, collection } from '@keystatic/core';
export default config({
storage: {
kind: 'local',
},
collections: {
posts: collection({
label: 'Posts',
slugField: 'title',
path: 'src/content/posts/*',
format: { contentField: 'content' },
schema: {
title: fields.slug({ name: { label: 'Title' } }),
content: fields.markdoc({
label: 'Content',
}),
},
}),
},
});
```
:::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
<Steps>
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:
<FileTree title="Project Structure">
- src/
- content/
- posts/
- **my-first-post.mdoc**
</FileTree>
5. Navigate to that file in your code editor and verify that you can see the Markdown content you entered. For example:
```markdown
---
title: My First Post
---
This is my very first post. I am **super** excited!
```
</Steps>
## Rendering Keystatic content
Use Astro's Content Collections API to [query and display your posts and collections](/en/guides/content-collections/#querying-collections), just as you would in any Astro project.
### Displaying a collection list
The following example displays a list of each post title, with a link to an individual post page.
```tsx {4}
---
import { getCollection } from 'astro:content'
const posts = await getCollection('posts')
---
<ul>
{posts.map(post => (
<li>
<a href={`/posts/${post.slug}`}>{post.data.title}</a>
</li>
))}
</ul>
```
### Displaying a single entry
To display content from an individual post, you can import and use the `<Content />` component to [render your content to HTML](/en/guides/content-collections/#rendering-body-content):
```tsx {4-5}
---
import { getEntry } from 'astro:content'
const post = await getEntry('posts', 'my-first-post')
const { Content } = await post.render()
---
<main>
<h1>{post.data.title}</h1>
<Content />
</main>
```
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)
---
File: /src/content/docs/en/guides/cms/keystonejs.mdx
---
---
title: KeystoneJS & Astro
description: Add content to your Astro project using KeystoneJS as a CMS
sidebar:
label: KeystoneJS
type: cms
stub: true
service: KeystoneJS
i18nReady: true
---
[KeystoneJS](https://keystonejs.com/) is an open source, headless content-management system that allows you to describe the structure of your schema.
---
File: /src/content/docs/en/guides/cms/kontent-ai.mdx
---
---
title: Kontent.ai & Astro
description: Add content to your Astro project using Kontent.ai as CMS
sidebar:
label: Kontent.ai
type: cms
service: Kontent.ai
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
import { FileTree } from '@astrojs/starlight/components';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
[Kontent.ai](https://www.kontent.ai/) is a headless CMS that allows you to manage content in a structured and modular way, supported by AI capabilities.
## Integrating with Astro
In this section, you'll use the [Kontent.ai TypeScript SDK](https://github.com/kontent-ai/delivery-sdk-js) to connect your Kontent.ai project to your Astro application.
### Prerequisites
To get started, you'll need the following:
1. **Kontent.ai project** - If you don't have a Kontent.ai account yet, [sign up for free](https://app.kontent.ai/sign-up) and create a new project.
2. **Delivery API keys** - You will need the Environment ID for published content and the Preview API key for fetching drafts (optional). Both keys are located in the **Environment Settings -> API keys** tab in Kontent.ai.
### Setting up credentials
To add your Kontent.ai credentials to Astro, create a `.env` file in the root of your project with the following variables:
```ini title=".env"
KONTENT_ENVIRONMENT_ID=YOUR_ENVIRONMENT_ID
KONTENT_PREVIEW_API_KEY=YOUR_PREVIEW_API_KEY
```
Now, these environment variables can be used in your Astro project.
If you would like to get [TypeScript IntelliSense for these environment variables](/en/guides/environment-variables/#intellisense-for-typescript), you can create a new `env.d.ts` file in the `src/` directory and configure `ImportMetaEnv` like this:
```ts title="src/env.d.ts"
interface ImportMetaEnv {
readonly KONTENT_ENVIRONMENT_ID: string;
readonly KONTENT_PREVIEW_API_KEY: string;
}
```
Your root directory should now include these new files:
<FileTree title="Project Structure">
- src/
- **env.d.ts**
- **.env**
- astro.config.mjs
- package.json
</FileTree>
### Installing dependencies
To connect Astro with your Kontent.ai project, install the [Kontent.ai TypeScript SDK](https://github.com/kontent-ai/delivery-sdk-js):
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install @kontent-ai/delivery-sdk
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm add @kontent-ai/delivery-sdk
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn add @kontent-ai/delivery-sdk
```
</Fragment>
</PackageManagerTabs>
Next, create a new file called `kontent.ts` in the `src/lib/` directory of your Astro project.
```ts title="src/lib/kontent.ts"
import { createDeliveryClient } from "@kontent-ai/delivery-sdk";
export const deliveryClient = createDeliveryClient({
environmentId: import.meta.env.KONTENT_ENVIRONMENT_ID,
previewApiKey: import.meta.env.KONTENT_PREVIEW_API_KEY,
});
```
:::note
Read more on [getting environment variables in Astro](/en/guides/environment-variables/#getting-environment-variables).
:::
This implementation creates a new `DeliveryClient` object using credentials from the `.env` file.
:::note[Previews]
The `previewApiKey` is optional. When used, you can [configure each query](https://github.com/kontent-ai/delivery-sdk-js#enable-preview-mode-per-query) to the Delivery API endpoint to return the latest versions of content items regardless of their state in the workflow. Otherwise, only published items are returned.
:::
Finally, the root directory of your Astro project should now include these new files:
<FileTree title="Project Structure">
- src/
- lib/
- **kontent.ts**
- env.d.ts
- .env
- astro.config.mjs
- package.json
</FileTree>
### Fetching data
The `DeliveryClient` is now available to all components. To fetch content, use the `DeliveryClient` and method chaining to define your desired items. This example shows a basic fetch of blog posts and renders their titles in a list:
```astro title="src/pages/index.astro" ins={2-7, 16-20}
---
import { deliveryClient } from "../lib/kontent";
const blogPosts = await deliveryClient
.items()
.type("blogPost")
.toPromise()
---
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Astro</title>
</head>
<body>
<ul>
{blogPosts.data.items.map(blogPost => (
<li>{blogPost.elements.title.value}</li>
))}
</ul>
</body>
</html>
```
You can find more querying options in the [Kontent.ai documentation](https://kontent.ai/learn/develop/hello-world/get-content/javascript).
## Making a blog with Astro and Kontent.ai
With the setup above, you are now able to create a blog that uses Kontent.ai as the source of content.
### Prerequisites
1. **Kontent.ai project** - For this tutorial, using a blank project is recommended. If you already have some content types in your content model, you may use them, but you will need to modify the code snippets to match your content model.
2. **Astro project configured for content fetching from Kontent.ai** - see above for more details on how to set up an Astro project with Kontent.ai
### Setting up content model
In Kontent.ai, navigate to **Content model** and create a new content type with the following fields and values:
* **Name:** Blog Post
* Elements:
* Text field
* **Name:** Title
* **Element Required:** yes
* Rich text field
* **Name:** Teaser
* **Element Required:** yes
* **Allowed in this element:** only check Text
* Rich text field
* **Name:** Content
* **Element Required:** yes
* Date & time field
* **Name:** Date
* URL slug field
* **Name:** URL slug
* **Element Required:** yes
* **Auto-generate from:** select "Title"
Then, click on **Save Changes**.
### Creating content
Now, navigate to **Content & assets** tab and create a new content item of type **Blog Post**. Fill the fields using these values:
* **Content item name:** Astro
* **Title:** Astro is amazing
* **Teaser:** Astro is an all-in-one framework for building fast websites faster.
* **Content:** You can use JavaScript to implement the website functionality, but no client bundle is necessary.
* **Date & time:** select today
* **URL slug:** astro-is-amazing
When you're finished, publish the blog post using the **Publish** button at the top.
*Note: Feel free to create as many blog posts as you like before moving to the next step.*
### Generating content model in TypeScript
Next, you'll generate TypeScript types out of your content model.
:::note
This step is optional but provides a much better developer experience and allows you to discover potential problems at build time rather than at runtime.
:::
First, install the [Kontent.ai JS model generator](https://github.com/kontent-ai/model-generator-js), [ts-node](https://github.com/TypeStrong/ts-node), and [dotenv](https://github.com/motdotla/dotenv):
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install @kontent-ai/model-generator ts-node dotenv
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm add @kontent-ai/model-generator ts-node dotenv
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn add @kontent-ai/model-generator ts-node dotenv
```
</Fragment>
</PackageManagerTabs>
Then, add the following script to package.json:
```json title="package.json"
{
...
"scripts": {
...
"regenerate:models": "ts-node --esm ./generate-models.ts"
},
}
```
Because the types require structural information about your project that is not available in the public API, you also need to add a Content Management API key to the `.env` file. You can generate the key under **Environment settings -> API keys -> Management API**.
```ini title=".env" ins={3}
KONTENT_ENVIRONMENT_ID=YOUR_ENVIRONMENT_ID
KONTENT_PREVIEW_API_KEY=YOUR_PREVIEW_API_KEY
KONTENT_MANAGEMENT_API_KEY=YOUR_MANAGEMENT_API_KEY
```
Finally, add the script `generate-models.ts` that configures the model generator to generate the models:
```ts title="generate-models.ts"
import { generateModelsAsync, textHelper } from '@kontent-ai/model-generator'
import { rmSync, mkdirSync } from 'fs'
import * as dotenv from 'dotenv'
dotenv.config()
const runAsync = async () => {
rmSync('./src/models', { force: true, recursive: true })
mkdirSync('./src/models')
// change working directory to models
process.chdir('./src/models')
await generateModelsAsync({
sdkType: 'delivery',
apiKey: process.env.KONTENT_MANAGEMENT_API_KEY ?? '',
environmentId: process.env.KONTENT_ENVIRONMENT_ID ?? '',
addTimestamp: false,
isEnterpriseSubscription: false,
})
}
// Self-invocation async function
;(async () => {
await runAsync()
})().catch(err => {
console.error(err)
throw err
})
```
Now, execute it:
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm run regenerate:models
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm run regenerate:models
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn run regenerate:models
```
</Fragment>
</PackageManagerTabs>
### Displaying a list of blog posts
Now you're ready to fetch some content. Go to the Astro page where you want to display a list of all blog posts, for example, the homepage `index.astro` in `src/pages`.
Fetch all blog posts in the frontmatter of the Astro page:
```astro title="src/pages/index.astro"
---
import { deliveryClient } from '../lib/kontent';
import type { BlogPost } from '../models';
import { contentTypes } from '../models/project/contentTypes';
const blogPosts = await deliveryClient
.items<BlogPost>
.type(contentTypes.blog_post.codename)
.toPromise()
---
```
If you skipped the model generation, you can also use an untyped object and string literal to define the type:
```ts
const blogPosts = await deliveryClient
.items()
.type("blogPost")
.toPromise()
```
The fetch call will return a `response` object which contains a list of all blog posts in `data.items`. In the HTML section of the Astro page, you can use the `map()` function to list the blog posts:
```astro title="src/pages/index.astro" ins={11-29}
---
import { deliveryClient } from '../lib/kontent';
import type { BlogPost } from '../models';
import { contentTypes } from '../models/project/contentTypes';
const blogPosts = await deliveryClient
.items<BlogPost>
.type(contentTypes.blogPost.codename)
.toPromise()
---
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Astro</title>
</head>
<body>
<h1>Blog posts</h1>
<ul>
{blogPosts.data.items.map(blogPost => (
<li>
<a href={`/blog/${blogPost.elements.url_slug.value}/`} title={blogPost.elements.title.value}>
{blogPost.elements.title.value}
</a>
</li>
))}
</ul>
</body>
</html>
```
### Generating individual blog posts
The last step of the tutorial is to generate detailed blog post pages.
#### Static site generation
In this section, you'll use the [Static (SSG) Mode](/en/guides/routing/#static-ssg-mode) with Astro.
First, create a file `[slug].astro` in `/src/pages/blog/` which needs to export a function `getStaticPaths` that collects all data from the CMS:
```astro title="src/pages/blog/[slug].astro"
---
import { deliveryClient } from '../../lib/kontent';
import type { BlogPost } from '../../models';
import { contentTypes } from '../../models/project/contentTypes';
export async function getStaticPaths() {
const blogPosts = await deliveryClient
.items<BlogPost>()
.type(contentTypes.blog_post.codename)
.toPromise()
---
```
So far, the function fetches all blog posts from Kontent.ai. The code snippet is exactly the same as what you used on the home page.
Next, the function must export paths and data for each blog post. You named the file `[slug].astro`, so the param which represents the URL slug is called `slug`:
```astro title="src/pages/blog/[slug].astro" ins={12-15}
---
import { deliveryClient } from '../../lib/kontent';
import type { BlogPost } from '../../models';
import { contentTypes } from '../../models/project/contentTypes';
export async function getStaticPaths() {
const blogPosts = await deliveryClient
.items<BlogPost>()
.type(contentTypes.blog_post.codename)
.toPromise()
return blogPosts.data.items.map(blogPost => ({
params: { slug: blogPost.elements.url_slug.value },
props: { blogPost }
}))
}
---
```
The last part is to provide the HTML template and display each blog post:
```astro title="src/pages/blog/[slug].astro" ins={18-33}
---
import { deliveryClient } from '../../lib/kontent';
import type { BlogPost } from '../../models';
import { contentTypes } from '../../models/project/contentTypes';
export async function getStaticPaths() {
const blogPosts = await deliveryClient
.items<BlogPost>()
.type(contentTypes.blog_post.codename)
.toPromise()
return blogPosts.data.items.map(blogPost => ({
params: { slug: blogPost.elements.url_slug.value },
props: { blogPost }
}))
}
const blogPost: BlogPost = Astro.props.blogPost
---
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>{blogPost.elements.title.value}</title>
</head>
<body>
<article>
<h1>{blogPost.elements.title.value}</h1>
<Fragment set:html={blogPost.elements.teaser.value} />
<Fragment set:html={blogPost.elements.content.value} />
<time>{new Date(blogPost.elements.date.value ?? "")}</time>
</body>
</html>
```
Navigate to your Astro preview (http://localhost:4321/blog/astro-is-amazing/ by default) to see the rendered blog post.
#### On-demand rendering
If your routes are [rendered on demand](/en/guides/on-demand-rendering/), you will use dynamic routes to fetch the page data from Kontent.ai.
Create a new file `[slug].astro` in `/src/pages/blog/` and add the following code. The data fetching is very similar to previous use cases but adds an `equalsFilter` that lets us find the right blog post based on the used URL:
```astro title="src/pages/blog/[slug].astro"
---
import { deliveryClient } from '../../lib/kontent';
import type { BlogPost } from '../../models';
import { contentTypes } from '../../models/project/contentTypes';
const { slug } = Astro.params
let blogPost: BlogPost;
try {
const data = await deliveryClient
.items<BlogPost>()
.equalsFilter(contentTypes.blog_post.elements.url_slug.codename, slug ?? '')
.type(contentTypes.blog_post.codename)
.limitParameter(1)
.toPromise()
blogPost = data.data.items[0]
} catch (error) {
return Astro.redirect('/404')
}
---
```
If you're not using generated types, you can instead use string literals to define the content item type and the filtered element codename:
```ts
const data = await deliveryClient
.items()
.equalsFilter("url_slug", slug ?? '')
.type("blog_post")
.limitParameter(1)
.toPromise()
```
Lastly, add the HTML code to render the blog post. This part is the same as with static generation:
```astro title="src/pages/blog/[slug].astro" ins={20-33}
---
import { deliveryClient } from '../../lib/kontent';
import type { BlogPost } from '../../models';
import { contentTypes } from '../../models/project/contentTypes';
const { slug } = Astro.params
let blogPost: BlogPost;
try {
const data = await deliveryClient
.items<BlogPost>()
.equalsFilter(contentTypes.blog_post.elements.url_slug.codename, slug ?? '')
.type(contentTypes.blog_post.codename)
.limitParameter(1)
.toPromise()
blogPost = data.data.items[0]
} catch (error) {
return Astro.redirect('/404')
}
---
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>{blogPost.elements.title.value}</title>
</head>
<body>
<article>
<h1>{blogPost.elements.title.value}</h1>
<Fragment set:html={blogPost.elements.teaser.value} />
<Fragment set:html={blogPost.elements.content.value} />
<time>{new Date(blogPost.elements.date.value ?? '')}</time>
</body>
</html>
```
### Publishing your site
To deploy your website, visit the [deployment guides](/en/guides/deploy/) and follow the instructions for your preferred hosting provider.
#### Rebuild on Kontent.ai changes
If your project is using Astro's default static mode, you will need to set up a webhook to trigger a new build when your content changes. If you are using Netlify or Vercel as your hosting provider, you can use its webhook feature to trigger a new build from Kontent.ai events.
##### Netlify
To set up a webhook in Netlify:
<Steps>
1. Go to your site dashboard and click on **Build & deploy**.
2. Under the **Continuous Deployment** tab, find the **Build hooks** section and click on **Add build hook**.
3. Provide a name for your webhook and select the branch you want to trigger the build on. Click on **Save** and copy the generated URL.
</Steps>
##### Vercel
To set up a webhook in Vercel:
<Steps>
1. Go to your project dashboard and click on **Settings**.
2. Under the **Git** tab, find the **Deploy Hooks** section.
3. Provide a name for your webhook and the branch you want to trigger the build on. Click **Add** and copy the generated URL.
</Steps>
##### Adding a webhook to Kontent.ai
In the [Kontent.ai app](https://kontent.ai/learn/docs/webhooks/javascript), go to **Environment settings -> Webhooks**. Click on **Create new webhook** and provide a name for your new webhook. Paste in the URL you copied from Netlify or Vercel and select which events should trigger the webhook. By default, for rebuilding your site when published content changes, you only need **Publish** and **Unpublish** events under **Delivery API triggers**. When you're finished, click on Save.
Now, whenever you publish a new blog post in Kontent.ai, a new build will be triggered and your blog will be updated.
---
File: /src/content/docs/en/guides/cms/microcms.mdx
---
---
title: microCMS & Astro
description: Add content to your Astro project using microCMS
sidebar:
label: microCMS
type: cms
stub: true
service: microCMS
i18nReady: true
---
[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/)
---
File: /src/content/docs/en/guides/cms/payload.mdx
---
---
title: Payload CMS & Astro
description: Add content to your Astro project using Payload as a CMS
sidebar:
label: Payload CMS
type: cms
stub: true
service: Payload CMS
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
[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:
```ts title="src/collections/Posts.ts"
import { CollectionConfig } from "payload/types";
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,
},
],
};
export default Posts;
```
<Steps>
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.
```json
{
"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
}
```
</Steps>
:::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:
```astro title="src/pages/index.astro"
---
import HomeLayout from "../layouts/HomeLayout.astro";
const res = await fetch("http://localhost:5000/api/posts") // http://localhost:4321/api/posts by default
const posts = await res.json()
---
<HomeLayout title='Astro Blog'>
{
posts.docs.map((post) => (
<h2 set:html={post.title} />
<p set:html={post.content} />
))
}
</HomeLayout>
```
## 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`
```astro title="src/pages/index.astro"
---
import HomeLayout from "../layouts/HomeLayout.astro";
const res = await fetch("http://localhost:5000/api/posts") // http://localhost:4321/api/posts by default
const posts = await res.json()
---
<HomeLayout title='Astro Blog'>
<h1>Astro + PayloadCMS 🚀</h1>
<h2>Blog posts list:</h2>
<ul>
{
posts.docs.map((post) =>(
<li>
<a href={`posts/${post.slug}`} set:html={post.title} />
</li>
))
}
</ul>
</HomeLayout>
```
### 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.
```astro title="src/pages/posts/[slug].astro"
---
import PostLayout from "../../layouts/PostLayout.astro"
const {title, content} = Astro.props
// The getStaticPaths() is required for static Astro sites.
// If using SSR, you will not need this function.
export async function getStaticPaths() {
let data = await fetch("http://localhost:5000/api/posts")
let posts = await data.json()
return posts.docs.map((post) => {
return {
params: {slug: post.slug},
props: {title: post.title, content: post.content},
};
});
}
---
<PostLayout title={title}>
<article>
<h1 set:html={title} />
<p set:html={content} />
</article>
</PostLayout>
```
### 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.
---
File: /src/content/docs/en/guides/cms/preprcms.mdx
---
---
title: Prepr CMS & Astro
description: Add content to your Astro project using Prepr as a CMS
sidebar:
label: Prepr CMS
type: cms
service: Prepr CMS
stub: true
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
import { FileTree } from '@astrojs/starlight/components';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
[Prepr CMS](https://www.prepr.io/) is a headless CMS with built-in personalization.
## Integrating with Astro
Prepr CMS provides a [GraphQL API](https://docs.prepr.io/reference/graphql/v1/overview) to connect your data to Astro.
### Prerequisites
To get started, you will need the following:
- A new or existing Astro project configured for [on-demand rendering](/en/guides/on-demand-rendering/).
- [A free Prepr account](https://signup.prepr.io/)
- [A Prepr environment with existing blog posts](https://docs.prepr.io/account/set-up-environments#create-an-envirntonment). These posts must include a `title` and `content`. Other fields are optional.
### Setting up credentials
To add the Prepr endpoint to your Astro project, create a `.env file` in the root of your project if one does not already exist and add the following variable:
```ini title=".env"
PREPR_ENDPOINT=YOUR_PREPR_API_URL
```
You will find your `YOUR_PREPR_API_URL` value from your Prepr account settings:
<Steps>
1. Go to  **Settings > Access tokens** to view both your Preview and Production access tokens.
2. Use the **API URL** value from the **GraphQL Production** access token to only retrieve published content items for your Astro site.
</Steps>
### Configuring the Prepr endpoint
Create a new folder `src/lib/` and add a new file called `prepr.js`. This is where you will configure the Prepr endpoint. Add the following code to fetch your data from Prepr CMS:
```js title="src/lib/prepr.js"
export async function Prepr(query, variables) {
    const response = await fetch(import.meta.env.PREPR_ENDPOINT, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({ query, variables }),
    })
    return response
}
```
Your root directory should now include these files:
<FileTree title="Project Structure">
- lib/
- **prepr.js**
- src/
- **.env**
- astro.config.mjs
- package.json
</FileTree>
### Fetching data
You will fetch your data from Prepr by writing queries to interact with its GraphQL API.
#### Create a GraphQL query to retrieve your blog articles:
<Steps>
1. Create a new folder `src/queries/` and add a file named `get-articles.js`.
2. Add the following query to this file to retrieve all articles:
```js title="src/queries/get-articles.js"
const GetArticles = `
query {
Articles {
items {
       _id
        _slug
        title
}
}
}
`
export default GetArticles
```
3. To display a linked list of your blog posts on a page, import and execute your query, including the necessary Prepr endpoint. You will then have access to all your posts titles and their slugs to render to the page. (In the next step, you will [create individual pages for your blog posts](#creating-individual-blog-post-pages).)
```astro title="src/pages/index.astro" ins={3-4, 6-8, 15-23}
---
import Layout from '../layouts/Layout.astro';
import { Prepr } from '../lib/prepr.js';
import GetArticles from '../queries/get-articles.js';
const response = await Prepr(GetArticles)
const { data } = await response.json()
const articles = data.Articles
---
<Layout title="My blog site">
<h1>
My blog site
  </h1>
  <ul>
{
articles.items.map((post) => (
<li>
<a href={post._slug}>{post.title}</a>
</li>
))
}
</ul>
</Layout>
```
</Steps>
Your root directory should include these new files:
<FileTree title="Project Structure">
- lib/
- prepr.js
- **queries**/
- **get-articles.js**
- src/
- .env
- astro.config.mjs
- package.json
</FileTree>
#### Creating individual blog post pages
To create a page for each blog post, you will execute a new GraphQL query on a [dynamic routing](/en/guides/routing/#on-demand-dynamic-routes) server-ssr-mode) `.astro` page. This query will fetch a specific article by its slug and a new page will be created for each individual blog post.
<Steps>
1. Create a file called `get-article-by-slug.js` in the `queries` folder and add the following to query a specific article by its slug and return data such as the article `title` and `content`:
```js title="src/lib/queries/get-article-by-slug.js"
const GetArticleBySlug = `
query ($slug: String) {
   Article (slug: $slug) {
     _id
     title
     content {
       __typename
       ... on Text {
         body
         text
       }
       ... on Assets {
         items {
           url
         }
       }
     }
   }
}`
export default GetArticleBySlug
```
:::tip
You can create and [test GraphQL queries](https://docs.prepr.io/reference/graphql/v1/test-queries) using the [Apollo explorer](https://studio.apollographql.com/sandbox/explorer) in Prepr. Open the API Explorer from the *Article* content item page in Prepr.
The Article content is stored in a *Dynamic content field*. Check out the GraphQL docs for more details on [how to fetch the data within this field](https://docs.prepr.io/reference/graphql/v1/schema-field-types-dynamic-content-field).
:::
2. Inside the `src/pages` folder, create a file called `[…slug].astro`. Add the following code to import and execute the query from the previous step and display the retrieved article:
```astro title="src/pages/[...slug].astro"
---
import Layout from '../layouts/Layout.astro';
import {Prepr} from '../lib/prepr.js';
import GetArticleBySlug from '../queries/get-article-by-slug.js';
const { slug } = Astro.params;
const response = await Prepr(GetArticleBySlug, {slug})
const { data } = await response.json()
const article = data.Article
---
<Layout title={article.title}>
<main>
<h1>{article.title}</h1>
{
article.content.map((content) => (
<div>
{
content.__typename === "Assets" &&
<img src={content.items[0].url} width="300" height="250"/>
}
{
content.__typename === 'Text' &&
<div set:html={content.body}></div>
}
</div>
))
}
</main>
</Layout>
```
</Steps>
Your root directory should now include these new files:
<FileTree title="Project Structure">
- lib/
- prepr.js
- queries/
- get-articles.js
- **get-article-by-slug.js**
- src/
- pages/
- index.astro
- **[…slug].astro**
- .env
- astro.config.mjs
- package.json
</FileTree>
Now, when you click an article link from the main list of blog posts, you will be taken to a page with its individual content.
### Publishing your site
To deploy your Prepr blog, visit our [deployment guides](/en/guides/deploy/) and follow the instructions for your preferred hosting provider.
## Official Resources
- Follow the [Prepr CMS Astro quickstart](https://github.com/preprio/astro-quick-start) guide to make a simple blog with Astro and Prepr CMS. 
- Check out the [Connecting Prepr CMS to Astro](https://docs.prepr.io/connecting-front-end-apps/astro) for an overview of Astro and Prepr CMS resources.
---
File: /src/content/docs/en/guides/cms/prismic.mdx
---
---
title: Prismic & Astro
description: Add content to your Astro project using Prismic as a CMS
sidebar:
label: Prismic
type: cms
stub: true
service: Prismic
i18nReady: true
---
[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).
---
File: /src/content/docs/en/guides/cms/sanity.mdx
---
---
title: Sanity & Astro
description: Add content to your Astro project using Sanity as a CMS
sidebar:
label: Sanity
type: cms
stub: true
service: Sanity
i18nReady: true
---
import Grid from '~/components/FluidGrid.astro'
import Card from '~/components/ShowcaseCard.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)
## Themes
<Grid>
<Card title="The Balanced Chef" href="https://astro.build/themes/details/the-balanced-chef/" thumbnail="astro-chef-project.png"/>
</Grid>
---
File: /src/content/docs/en/guides/cms/sitecore.mdx
---
---
title: Sitecore Experience Manager & Astro
description: Add content to your project using Sitecore as your CMS.
sidebar:
label: Sitecore XM
type: cms
stub: true
service: Sitecore XM
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
[Sitecore Experience Manager (XM)](https://www.sitecore.com/products/experience-manager) is an enterprise-level content management system built on ASP.NET.
## Getting started
<Steps>
1. [Create a Sitecore Headless website](https://doc.sitecore.com/xp/en/developers/sxa/103/sitecore-experience-accelerator/create-a-headless-tenant-and-site.html) following Sitcore's official documentation.
2. Run the following project initialization command in your terminal:
```shell
npx @astro-sitecore-jss/create-astro-sitecore-jss@latest
```
3. Follow the instructions in the terminal to create your project.
</Steps>
## Community Resources
- [Sitecore JavaScript Software Development Kit for Astro](https://github.com/exdst/jss-astro-public) on GitHub
- [Introduction to Sitecore with Astro](https://exdst.com/posts/20231002-sitecore-astro)
- [Starting Your First Sitecore Astro Project](https://exdst.com/posts/20240103-first-sitecore-astro-project)
---
File: /src/content/docs/en/guides/cms/spinal.mdx
---
---
title: Spinal & Astro
description: Add content to your project using Spinal as your CMS.
sidebar:
label: Spinal
type: cms
stub: true
service: Spinal
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
[Spinal](https://spinalcms.com/cms-for-astro/) is a commercial, SaaS-focused, Git-based CMS.
## Getting started
<Steps>
1. [Create a Spinal account](https://spinalcms.com/signup/).
2. Connect your GitHub account to Spinal.
3. Select your Astro repository when prompted.
</Steps>
All Markdown content from the selected folder will be imported into your Spinal account and is ready to be edited.
## Official Resources
- [Documentation theme built for Astro with Tailwind CSS](https://spinalcms.com/resources/astro-documentation-theme-with-tailwind-css/)
## Production Sites
The following sites use Astro + Spinal in production:
- [spinalcms.com](https://spinalcms.com/) (all blog articles, documentation, changelog, feature pages, etc.)
---
File: /src/content/docs/en/guides/cms/statamic.mdx
---
---
title: Headless Statamic & Astro
description: Add content to your Astro project using Statamic as a CMS
sidebar:
label: Statamic
type: cms
stub: false
service: Statamic
i18nReady: true
---
import Grid from '~/components/FluidGrid.astro'
import Card from '~/components/ShowcaseCard.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):
```astro title="src/pages/index.astro
---
const res = await fetch("https://[YOUR-SITE]/api/collections/posts/entries?sort=-date")
const posts = await res.json()
---
<h1>Astro + Statamic 🚀</h1>
{
posts.map((post) => (
<h2 set:html={post.title} />
<p set:html={post.content} />
))
}
```
#### 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):
```astro title="src/pages/index.astro
---
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;
---
<h1>Astro + Statamic 🚀</h1>
{
entries.data.map((post) => (
<h2 set:html={post.title} />
<p set:html={post.content} />
))
}
```
### 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/)
## Themes
<Grid>
<Card title="Creek" href="https://astro.build/themes/details/creek/" thumbnail="creek.png"/>
</Grid>
---
File: /src/content/docs/en/guides/cms/storyblok.mdx
---
---
title: Storyblok & Astro
description: Add content to your Astro project using Storyblok as a CMS
sidebar:
label: Storyblok
type: cms
service: Storyblok
stub: false
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
import { FileTree } from '@astrojs/starlight/components';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
[Storyblok](https://www.storyblok.com/) is a component-based headless CMS that allows you to manage your content using reusable components called Bloks.
## Integrating with Astro
In this section, you will use the [Storyblok integration](https://github.com/storyblok/storyblok-astro) to connect Storyblok to Astro.
### 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 Storyblok account and space** - If you don't have an account yet, [sign up for free](https://app.storyblok.com/#/signup) and create a new space.
3. **Storyblok Preview token** - This token will be used to fetch drafts and published versions of your content. You can find and generate your API token in the Access Tokens tab of your Storyblok space settings.
### Setting up credentials
To add your Storyblok credentials to Astro, create a `.env` file in the root of your project with the following variable:
```ini title=".env"
STORYBLOK_TOKEN=YOUR_PREVIEW_TOKEN
```
Now, you should be able to use these environment variables in your project.
Your root directory should now include this new file:
<FileTree title="Project Structure">
- src/
- **.env**
- astro.config.mjs
- package.json
</FileTree>
### Installing dependencies
To connect Astro with your Storyblok space, install the official [Storyblok integration](https://github.com/storyblok/storyblok-astro) using the command below for your preferred package manager:
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install @storyblok/astro vite
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm add @storyblok/astro vite
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn add @storyblok/astro vite
```
</Fragment>
</PackageManagerTabs>
### Configuring Storyblok
Modify your Astro config file to include the Storyblok integration:
```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import { storyblok } from '@storyblok/astro';
import { loadEnv } from 'vite';
const env = loadEnv("", process.cwd(), 'STORYBLOK');
export default defineConfig({
integrations: [
storyblok({
accessToken: env.STORYBLOK_TOKEN,
components: {
// Add your components here
},
apiOptions: {
// Choose your Storyblok space region
region: 'us', // optional, or 'eu' (default)
},
})
],
});
```
The Storyblok integration requires an object with the following properties:
1. `accessToken` - This references the Storyblok API token that you added in the previous step.
:::tip
Since the Astro config file does not normally support environment variables, use the `loadEnv` function from Vite to load them.
:::
2. `components` - An object that maps Storyblok component names to paths to your local components. This is required to render your Storyblok Bloks in Astro.
:::note
The component paths are relative to the `src` directory. For example, if your component is located at `src/storyblok/MyComponent.astro`, the path would be `storyblok/MyComponent` (without the `.astro` extension).
:::
3. `apiOptions` - An object containing [Storyblok API options](https://github.com/storyblok/storyblok-astro#options).
:::caution
By default, the region is `eu`. If your Storyblok space was created in the US region, you will need to set the region to `us`.
:::
### Connecting Bloks to Astro components
To connect your Bloks to Astro, create a new folder named `storyblok` in the `src` directory. This folder will contain all the Astro components that will match your Bloks in your Storyblok Blok library.
In this example, you have a `blogPost` Blok content type in your Storyblok library with the following fields:
- `title` - A text field
- `description` - A text field
- `content` - A rich text field
Our goal is to create the equivalent Astro component that will use these fields to render its content. To do this, create a new file named `BlogPost.astro` inside `src/storyblok` with the following content:
```astro title="src/storyblok/BlogPost.astro"
---
import { storyblokEditable, renderRichText } from '@storyblok/astro'
const { blok } = Astro.props
const content = renderRichText(blok.content)
---
<article {...storyblokEditable(blok)}>
<h1>{blok.title}</h1>
<p>{blok.description}</p>
<Fragment set:html={content} />
</article>
```
The `blok` property contains the data that you will receive from Storyblok. It also contains the fields that were defined in the `blogPost` content type Blok in Storyblok.
To render our content, the integration provides utility functions such as:
- `storyblokEditable` - it adds the necessary attributes to the elements so that you can edit them in Storyblok.
- `renderRichText` - it transforms the rich text field into HTML.
Your root directory should include this new file:
<FileTree title="Project Structure">
- src/
- storyblok/
- **BlogPost.astro**
- .env
- astro.config.mjs
- package.json
</FileTree>
Finally, to connect the `blogPost` Blok to the `BlogPost` component, add a new property to your components object in your Astro config file.
- The key is the name of the Blok in Storyblok. In this case, it is `blogPost`.
- The value is the path to the component. In this case, it is `storyblok/BlogPost`.
:::caution
The `key` should exactly match your Blok name in Storyblok to be referenced correctly. If these don't match, or you're trying to reference a component that doesn't exist in Storyblok, you'll get an error.
:::
```js title="astro.config.mjs" ins={12}
import { defineConfig } from 'astro/config';
import { storyblok } from '@storyblok/astro';
import { loadEnv } from 'vite';
const env = loadEnv("", process.cwd(), 'STORYBLOK');
export default defineConfig({
integrations: [
storyblok({
accessToken: env.STORYBLOK_TOKEN,
components: {
blogPost: 'storyblok/BlogPost',
},
apiOptions: {
region: 'us',
},
})
],
});
```
### Fetching data
To test the setup, in Storyblok create a new story with the `blogPost` content type named `test-post`.
In Astro, create a new page in the `src/pages/` directory named `test-post.astro` with the following content:
```astro title="src/pages/test-post.astro"
---
import { useStoryblokApi } from '@storyblok/astro'
import StoryblokComponent from '@storyblok/astro/StoryblokComponent.astro'
const storyblokApi = useStoryblokApi()
const { data } = await storyblokApi.get("cdn/stories/test-post", {
version: import.meta.env.DEV ? "draft" : "published",
});
const content = data.story.content;
---
<StoryblokComponent blok={content} />
```
To query your data, use the `useStoryblokApi` hook. This will initialize a new client instance using your integration configuration.
To render your content, pass the `content` property of the Story to the `StoryblokComponent` as a `blok` prop. This component will render the Bloks that are defined inside the `content` property. In this case, it will render the `BlogPost` component.
## Making a blog with Astro and Storyblok
With the integration set up, you can now create a blog with Astro and Storyblok.
### Prerequisites
1. **A Storyblok space** - For this tutorial, we recommend using a new space. If you already have a space with Bloks, feel free to use them, but you will need to modify the code to match the Blok names and content types.
2. **An Astro project integrated with Storyblok** - See [integrating with Astro](#integrating-with-astro) for instructions on how to set up the integration.
### Creating a blok library
To create Bloks, go to the Storyblok app and click on the **Block Library** tab. Click on the <kbd>+ New blok</kbd> button and create the following Bloks:
1. `blogPost` - A content type Blok with the following fields:
- `title` - A text field
- `description` - A text field
- `content` - A rich text field
2. `blogPostList` - An empty nestable Blok
3. `page` - A content type Blok with the following fields:
- `body` - A nestable Blok
### Creating content
To add new content, go to the content section by clicking on the **Content** tab. Using the Blok library that you created in the previous step, create the following stories:
1. `home` - A content type story with the `page` Blok. Inside the `body` field, add a `blogPostList` Blok.
2. `blog/no-javascript` - A story with the `blogPost` content type inside the blog folder.
```yaml
title: No JavaScript
description: A sample blog post
content: Hi there! This blog post doesn't use JavaScript.
```
3. `blog/astro-is-amazing` - A story with the `blogPost` content type inside the blog folder.
```yaml
title: Astro is amazing
description: We love Astro
content: Hi there! This blog post was build with Astro.
```
Now that you have your content ready, return to your Astro project and start building your blog.
### Connecting Bloks to components
To connect your newly created Bloks to Astro components, create a new folder named `storyblok` in your `src` directory and add the following files:
`Page.astro` is a nestable Block content type component that will recursively render all the Bloks inside the `body` property of the `page` Blok. It also adds the `storyblokEditable` attributes to the parent element which will allow us to edit the page in Storyblok.
```astro title="src/storyblok/Page.astro"
---
import { storyblokEditable } from '@storyblok/astro'
import StoryblokComponent from "@storyblok/astro/StoryblokComponent.astro";
const { blok } = Astro.props
---
<main {...storyblokEditable(blok)}>
{
blok.body?.map((blok) => {
return <StoryblokComponent blok={blok} />
})
}
</main>
```
`BlogPost.astro` will render the `title`, `description` and `content` properties of the `blogPost` Blok.
To transform the `content` property from a rich text field to HTML, you can use the `renderRichText` helper function.
```astro title="src/storyblok/BlogPost.astro"
---
import { storyblokEditable, renderRichText } from '@storyblok/astro'
const { blok } = Astro.props
const content = renderRichText(blok.content)
---
<article {...storyblokEditable(blok)}>
<h1>{blok.title}</h1>
<p>{blok.description}</p>
<Fragment set:html={content} />
</article>
```
`BlogPostList.astro` is a nestable Blok content type component that will render a list of blog post previews.
It uses the `useStoryblokApi` hook to fetch all the stories with the content type of `blogPost`. It uses the `version` query parameter to fetch the draft versions of the stories when in development mode and the published versions when building for production.
`Astro.props` is used to set up the editor in Storyblok. Additional props can also be passed to your component here, if needed.
```astro title="src/storyblok/BlogPostList.astro"
---
import { storyblokEditable } from '@storyblok/astro'
import { useStoryblokApi } from '@storyblok/astro'
const storyblokApi = useStoryblokApi();
const { data } = await storyblokApi.get('cdn/stories', {
version: import.meta.env.DEV ? "draft" : "published",
content_type: 'blogPost',
})
const posts = data.stories.map(story => {
return {
title: story.content.title,
date: new Date(story.published_at).toLocaleDateString("en-US", {dateStyle: "full"}),
description: story.content.description,
slug: story.full_slug,
}
})
const { blok } = Astro.props
---
<ul {...storyblokEditable(blok)}>
{posts.map(post => (
<li>
<time>{post.date}</time>
<a href={post.slug}>{post.title}</a>
<p>{post.description}</p>
</li>
))}
</ul>
```
Finally, add your components to the `components` property of the `storyblok` config object in `astro.config.mjs`. The key is the name of the Blok in Storyblok, and the value is the path to the component relative to `src`.
```js title="astro.config.mjs" ins={12-14}
import { defineConfig } from 'astro/config';
import { storyblok } from '@storyblok/astro';
import { loadEnv } from 'vite';
const env = loadEnv("", process.cwd(), 'STORYBLOK');
export default defineConfig({
integrations: [
storyblok({
accessToken: env.STORYBLOK_TOKEN,
components: {
blogPost: 'storyblok/BlogPost',
blogPostList: 'storyblok/BlogPostList',
page: 'storyblok/Page',
},
apiOptions: {
region: 'us',
},
})
],
});
```
### Generating pages
To create a route for a specific `page`, you can fetch its content directly from the Storyblok API and pass it to the `StoryblokComponent` component. Remember to make sure you have added the `Page` component to your astro.config.mjs.
Create an `index.astro` file in `src/pages/` to render the `home` page:
```astro title="src/pages/index.astro" {3,7,8,9,17}
---
import { useStoryblokApi } from '@storyblok/astro'
import StoryblokComponent from '@storyblok/astro/StoryblokComponent.astro'
import BaseLayout from '../layouts/BaseLayout.astro'
const storyblokApi = useStoryblokApi();
const { data } = await storyblokApi.get('cdn/stories/home', {
version: import.meta.env.DEV ? "draft" : "published",
});
const content = data.story.content;
---
<html lang="en">
<head>
<title>Storyblok & Astro</title>
</head>
<body>
<StoryblokComponent blok={content} />
</body>
</html>
```
To generate pages for all of your blog posts, create a `.astro` page that will create dynamic routes. This approach varies depending on whether your routes are prerendered (the default in Astro) or [rendered on demand](/en/guides/on-demand-rendering/).
#### Static site generation
If you are using Astro's default static site generation, you will use [dynamic routes](/en/guides/routing/#dynamic-routes) and the `getStaticPaths` function to generate your project pages.
Create a new directory `src/pages/blog/` and add a new file called `[...slug].astro` with the following code:
```astro title="src/pages/blog/[...slug].astro"
---
import { useStoryblokApi } from '@storyblok/astro'
import StoryblokComponent from '@storyblok/astro/StoryblokComponent.astro'
export async function getStaticPaths() {
const sbApi = useStoryblokApi();
const { data } = await sbApi.get("cdn/stories", {
content_type: "blogPost",
version: import.meta.env.DEV ? "draft" : "published",
});
const stories = Object.values(data.stories);
return stories.map((story) => {
return {
params: { slug: story.slug },
};
});
}
const sbApi = useStoryblokApi();
const { slug } = Astro.params;
const { data } = await sbApi.get(`cdn/stories/blog/${slug}`, {
version: import.meta.env.DEV ? "draft" : "published",
});
const story = data.story;
---
<html lang="en">
<head>
<title>Storyblok & Astro</title>
</head>
<body>
<StoryblokComponent blok={story.content} />
</body>
</html>
```
This file will generate a page for each story, with the slug and content fetched from the Storyblok API.
:::note
When adding folders inside of Storyblok, include them in the slug when interacting with the Storyblok API. For example, in the GET request above we can use **cdn/stories/blog**, with a blog folder inside rather than using them at the root.
:::
#### On-demand rendering
If you are [rendering your routes on demand with an adapter](/en/guides/on-demand-rendering/), you will use dynamic routes to fetch the page data from Storyblok.
Create a new directory `src/pages/blog/` and add a new file called `[...slug].astro` with the following code:
```astro title="src/pages/blog/[...slug].astro"
---
import { useStoryblokApi } from '@storyblok/astro'
import StoryblokComponent from '@storyblok/astro/StoryblokComponent.astro'
const storyblokApi = useStoryblokApi()
const slug = Astro.params.slug;
let content;
try {
const { data } = await storyblokApi.get(`cdn/stories/blog/${slug}`, {
version: import.meta.env.DEV ? "draft" : "published",
});
content = data.story.content
} catch (error) {
return Astro.redirect('/404')
}
---
<html lang="en">
<head>
<title>Storyblok & Astro</title>
</head>
<body>
<StoryblokComponent blok={content} />
</body>
</html>
```
This file will fetch and render the page data from Storyblok that matches the dynamic `slug` parameter.
Since you are using a redirect to `/404`, create a 404 page in `src/pages`:
```astro title="src/pages/404.astro"
<html lang="en">
<head>
<title>Not found</title>
</head>
<body>
<p>Sorry, this page does not exist.</p>
</body>
</html>
```
If the story is not found, the request will be redirected to the 404 page.
### Publishing your site
To deploy your website, visit our [deployment guides](/en/guides/deploy/) and follow the instructions for your preferred hosting provider.
#### Rebuild on Storyblok changes
If your project is using Astro's default static mode, you will need to set up a webhook to trigger a new build when your content changes. If you are using Netlify or Vercel as your hosting provider, you can use its webhook feature to trigger a new build from Storyblok events.
##### Netlify
To set up a webhook in Netlify:
<Steps>
1. Go to your site dashboard and click on **Build & deploy**.
2. Under the **Continuous Deployment** tab, find the **Build hooks** section and click on **Add build hook**.
3. Provide a name for your webhook and select the branch you want to trigger the build on. Click on **Save** and copy the generated URL.
</Steps>
##### Vercel
To set up a webhook in Vercel:
<Steps>
1. Go to your project dashboard and click on **Settings**.
2. Under the **Git** tab, find the **Deploy Hooks** section.
3. Provide a name for your webhook and the branch you want to trigger the build on. Click **Add** and copy the generated URL.
</Steps>
##### Adding a webhook to Storyblok
In your Storyblok space **Settings**, click on the **Webhooks** tab. Paste the webhook URL you copied in the **Story published & unpublished** field and hit <kbd>Save</kbd> to create a webhook.
Now, whenever you publish a new story, a new build will be triggered and your blog will be updated.
## Official Resources
- Storyblok provides an [Astro Integration](https://www.storyblok.com/mp/announcing-storyblok-astro) to add Storyblok to your project.
## Community Resources
- [Getting the Visual Editor to work for Storyblok + Astro](https://dev.to/sandrarodgers/getting-the-visual-editor-to-work-for-storyblok-astro-2gja) by Sandra Rodgers
- [Astro + Storyblok: SSR preview for instant visual editing](https://dev.to/jgierer12/astro-storyblok-ssr-preview-for-instant-visual-editing-3g9m) by Jonas Gierer
- [Astro-Storyblok Previews Site with Netlify's Branch Deploys Feature](https://dev.to/sandrarodgers/astro-storyblok-previews-site-with-netlifys-branch-deploys-feature-44dh) by Sandra Rodgers
---
File: /src/content/docs/en/guides/cms/strapi.mdx
---
---
title: Strapi & Astro
description: Add content to your Astro project using Strapi Headless CMS
sidebar:
label: Strapi
type: cms
service: Strapi
stub: false
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
import { FileTree } from '@astrojs/starlight/components';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
[Strapi](https://strapi.io/) is an open-source, customizable, headless CMS.
## Integrating with Astro
This guide will build a wrapper function to connect Strapi with Astro.
### 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 Strapi CMS server** - You can [set up a Strapi server on a local environment](https://docs.strapi.io/dev-docs/quick-start).
### Adding the Strapi URL in `.env`
To add your Strapi URL to Astro, create a `.env` file in the root of your project (if one does not already exist) and add the following variable:
```ini title=".env"
STRAPI_URL="http://127.0.0.1:1337" # or use your IP address
```
Restart the dev server to use this environment variable in your Astro project.
If you would like to have IntelliSense for your environment variable, you can create a `env.d.ts` file in the `src/` directory and configure `ImportMetaEnv` like this:
```ts title="src/env.d.ts"
interface ImportMetaEnv {
readonly STRAPI_URL: string;
}
```
Your root directory should now include the new file(s):
<FileTree title="Project Structure">
- src/
- **env.d.ts**
- **.env**
- astro.config.mjs
- package.json
</FileTree>
### Creating the API wrapper
Create a new file at `src/lib/strapi.ts` and add the following wrapper function to interact with the Strapi API:
```ts title="src/lib/strapi.ts"
interface Props {
endpoint: string;
query?: Record<string, string>;
wrappedByKey?: string;
wrappedByList?: boolean;
}
/**
* Fetches data from the Strapi API
* @param endpoint - The endpoint to fetch from
* @param query - The query parameters to add to the url
* @param wrappedByKey - The key to unwrap the response from
* @param wrappedByList - If the response is a list, unwrap it
* @returns
*/
export default async function fetchApi<T>({
endpoint,
query,
wrappedByKey,
wrappedByList,
}: Props): Promise<T> {
if (endpoint.startsWith('/')) {
endpoint = endpoint.slice(1);
}
const url = new URL(`${import.meta.env.STRAPI_URL}/api/${endpoint}`);
if (query) {
Object.entries(query).forEach(([key, value]) => {
url.searchParams.append(key, value);
});
}
const res = await fetch(url.toString());
let data = await res.json();
if (wrappedByKey) {
data = data[wrappedByKey];
}
if (wrappedByList) {
data = data[0];
}
return data as T;
}
```
This function requires an object with the following properties:
1. `endpoint` - The endpoint you are fetching.
2. `query` - The query parameters to add to the end of URL
3. `wrappedByKey` - The `data` key in the object that wraps your `Response`.
4. `wrappedByList` - A parameter to "unwrap" the list returned by Strapi, and return only the first item.
### Optional: Creating the Article interface
If you are using TypeScript, create the following Article interface to correspond to the Strapi content types at `src/interfaces/article.ts`:
```ts title="src/interfaces/article.ts"
export default interface Article {
id: number;
attributes: {
title: string;
description: string;
content: string;
slug: string;
createdAt: string;
updatedAt: string;
publishedAt: string;
};
}
```
:::note
You can modify this interface, or create multiple interfaces, to correspond to your own project data.
:::
<FileTree title="Project Structure">
- src/
- interfaces/
- **article.ts**
- lib/
- strapi.ts
- env.d.ts
- .env
- astro.config.mjs
- package.json
</FileTree>
### Displaying a list of articles
<Steps>
1. Update your home page `src/pages/index.astro` to display a list of blog posts, each with a description and a link to its own page.
2. Import the wrapper function and the interface. Add the following API call to fetch your articles and return a list:
```astro title="src/pages/index.astro"
---
import fetchApi from '../lib/strapi';
import type Article from '../interfaces/article';
const articles = await fetchApi<Article[]>({
endpoint: 'articles', // the content type to fetch
wrappedByKey: 'data', // the key to unwrap the response
});
---
```
The API call requests data from `http://localhost:1337/api/articles` and returns `articles`: an array of json objects representing your data:
```json
[
{
id: 1,
attributes: {
title: "What's inside a Black Hole",
description: "Maybe the answer is in this article, or not...",
content: "Well, we don't know yet...",
slug: "what-s-inside-a-black-hole",
createdAt: "2023-05-28T13:19:46.421Z",
updatedAt: "2023-05-28T13:19:46.421Z",
publishedAt: "2023-05-28T13:19:45.826Z"
}
},
// ...
]
```
3. Using data from the `articles` array returned by the API, display your Strapi blog posts in a list. These posts will link to their own individual pages, which you will create in the next step.
```astro title="src/pages/index.astro"
---
import fetchApi from '../lib/strapi';
import type Article from '../interfaces/article';
const articles = await fetchApi<Article[]>({
endpoint: 'articles?populate=image',
wrappedByKey: 'data',
});
---
<!DOCTYPE html>
<html lang="en">
<head>
<title>Strapi & Astro</title>
</head>
<body>
<main>
<ul>
{
articles.map((article) => (
<li>
<a href={`/blog/${article.attributes.slug}/`}>
{article.attributes.title}
</a>
</li>
))
}
</ul>
</main>
</body>
</html>
```
</Steps>
### Generating article pages
Create the file `src/pages/blog/[slug].astro` to [dynamically generate a page](/en/guides/routing/#dynamic-routes) for each article.
<FileTree title="Project Structure">
- src/
- interfaces/
- article.ts
- lib/
- strapi.ts
- pages/
- index.astro
- blog/
- **[slug].astro**
- env.d.ts
- .env
- astro.config.mjs
- package.json
</FileTree>
#### Static site generation
In Astro's default static mode (SSG), use [`getStaticPaths()`](/en/reference/routing-reference/#getstaticpaths) to fetch your list of articles from Strapi.
```astro title="src/pages/blog/[slug].astro
---
import fetchApi from '../../lib/strapi';
import type Article from '../../interfaces/article';
export async function getStaticPaths() {
const articles = await fetchApi<Article[]>({
endpoint: 'articles',
wrappedByKey: 'data',
});
return articles.map((article) => ({
params: { slug: article.attributes.slug },
props: article,
}));
}
type Props = Article;
const article = Astro.props;
---
```
Create the template for each page using the properties of each post object.
```astro title="src/pages/blog/[slug].astro ins={21-43}
---
import fetchApi from '../../lib/strapi';
import type Article from '../../interfaces/article';
export async function getStaticPaths() {
const articles = await fetchApi<Article[]>({
endpoint: 'articles',
wrappedByKey: 'data',
});
return articles.map((article) => ({
params: { slug: article.attributes.slug },
props: article,
}));
}
type Props = Article;
const article = Astro.props;
---
<!DOCTYPE html>
<html lang="en">
<head>
<title>{article.attributes.title}</title>
</head>
<body>
<main>
<img src={import.meta.env.STRAPI_URL + article.attributes.image.data.attributes.url} />
<h1>{article.attributes.title}</h1>
<!-- Render plain text -->
<p>{article.attributes.content}</p>
<!-- Render markdown -->
<MyMarkdownComponent>
{article.attributes.content}
</MyMarkdownComponent>
<!-- Render html -->
<Fragment set:html={article.attributes.content} />
</main>
</body>
</html>
```
:::tip
Make sure to choose the right rendering for your content. For markdown check out our [markdown guide](/en/guides/markdown-content/). If you are rendering html refer to [this guide](/en/reference/directives-reference/#sethtml) for safety.
:::
#### On-demand rendering
If you've [opted into on-demand rendering with an adapter](/en/guides/on-demand-rendering/), [generate your dynamic routes](/en/guides/routing/#on-demand-dynamic-routes) using the following code:
Create the `src/pages/blog/[slug].astro` file:
```astro title="src/pages/blog/[slug].astro"
---
import fetchApi from '../../../lib/strapi';
import type Article from '../../../interfaces/article';
const { slug } = Astro.params;
let article: Article;
try {
article = await fetchApi<Article>({
endpoint: 'articles',
wrappedByKey: 'data',
wrappedByList: true,
query: {
'filters[slug][$eq]': slug || '',
},
});
} catch (error) {
return Astro.redirect('/404');
}
---
<!DOCTYPE html>
<html lang="en">
<head>
<title>{article.attributes.title}</title>
</head>
<body>
<main>
<img src={import.meta.env.STRAPI_URL + article.attributes.image.data.attributes.url} />
<h1>{article.attributes.title}</h1>
<!-- Render plain text -->
<p>{article.attributes.content}</p>
<!-- Render markdown -->
<MyMarkdownComponent>
{article.attributes.content}
</MyMarkdownComponent>
<!-- Render html -->
<Fragment set:html={article.attributes.content} />
</main>
</body>
</html>
```
This file will fetch and render the page data from Strapi that matches the dynamic `slug` parameter.
Since you are using a redirect to `/404`, create a 404 page in `src/pages`:
```astro title="src/pages/404.astro"
<html lang="en">
<head>
<title>Not found</title>
</head>
<body>
<p>Sorry, this page does not exist.</p>
<img src="https://http.cat/404" />
</body>
</html>
```
If the article is not found, the user will be redirected to this 404 page and be greeted by a lovely cat.
### Publishing your site
To deploy your website, visit our [deployment guides](/en/guides/deploy/) and follow the instructions for your preferred hosting provider.
#### Rebuild on changes
If your project is using Astro's default static mode, you will need to set up a webhook to trigger a new build when your content changes. If you are using Netlify or Vercel as your hosting provider, you can use its webhook feature to trigger a new build from Strapi.
##### Netlify
To set up a webhook in Netlify:
<Steps>
1. Go to your site dashboard and click on **Build & deploy**.
2. Under the **Continuous Deployment** tab, find the **Build hooks** section and click on **Add build hook**.
3. Provide a name for your webhook and select the branch you want to trigger the build on. Click on **Save** and copy the generated URL.
</Steps>
##### Vercel
To set up a webhook in Vercel:
<Steps>
1. Go to your project dashboard and click on **Settings**.
2. Under the **Git** tab, find the **Deploy Hooks** section.
3. Provide a name for your webhook and the branch you want to trigger the build on. Click **Add** and copy the generated URL.
</Steps>
##### Adding a webhook to Strapi
Follow [the Strapi webhooks guide](https://strapi.io/blog/webhooks) to create a webhook in your Strapi admin panel.
## Official Resources
- [Strapi Blog Guide For React](https://strapi.io/blog/build-a-blog-with-next-react-js-strapi) by Strapi
---
File: /src/content/docs/en/guides/cms/studiocms.mdx
---
---
title: StudioCMS & Astro
description: Build and manage content for your Astro project using StudioCMS, a headless CMS designed specifically for Astro.
sidebar:
label: StudioCMS
type: cms
service: StudioCMS
i18nReady: true
---
[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)
---
File: /src/content/docs/en/guides/cms/tina-cms.mdx
---
---
title: Tina CMS & Astro
description: Add content to your Astro project using Tina as a CMS
sidebar:
label: Tina CMS
type: cms
stub: false
service: Tina CMS
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import Grid from '~/components/FluidGrid.astro';
import Card from '~/components/ShowcaseCard.astro';
import { Steps } from '@astrojs/starlight/components';
[Tina CMS](https://tina.io/) is a Git-backed headless content management system.
## Integrating with Astro
To get started, you'll need an existing Astro project.
<Steps>
1. Run the following command to install Tina into your Astro project.
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npx @tinacms/cli@latest init
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm dlx @tinacms/cli@latest init
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn dlx @tinacms/cli@latest init
```
</Fragment>
</PackageManagerTabs>
- When prompted for a Cloud ID, press <kbd>Enter</kbd> to skip. You'll generate one later if you want to use Tina Cloud.
- When prompted "What framework are you using", choose **Other**.
- When asked where public assets are stored, press <kbd>Enter</kbd>.
After this has finished, you should now have a `.tina` folder in the root of your project and a generated `hello-world.md` file at `content/posts`.
2. Change the `dev` script in `package.json`:
<PackageManagerTabs>
<Fragment slot="npm">
```json del={4} ins={5}
// package.json
{
"scripts": {
"dev": "astro dev",
"dev": "tinacms dev -c \"astro dev\""
}
}
```
</Fragment>
<Fragment slot="pnpm">
```json del={4} ins={5}
// package.json
{
"scripts": {
"dev": "astro dev",
"dev": "tinacms dev -c \"astro dev\""
}
}
```
</Fragment>
<Fragment slot="yarn">
```json del={4} ins={5}
// package.json
{
"scripts": {
"dev": "astro dev",
"dev": "tinacms dev -c \"astro dev\""
}
}
```
</Fragment>
</PackageManagerTabs>
3. TinaCMS is now set up in local mode. Test this by running the `dev` script, then navigating to `/admin/index.html#/collections/post`.
Editing the “Hello, World!” post will update the `content/posts/hello-world.md` file in your project directory.
4. Set up your Tina collections by editing the `schema.collections` property in `.tina/config.ts`.
For example, you can add a required "date posted" frontmatter property to our posts:
```js title=".tina/config.ts" ins={35-40}
import { defineConfig } from "tinacms";
// Your hosting provider likely exposes this as an environment variable
const branch = process.env.HEAD || process.env.VERCEL_GIT_COMMIT_REF || "main";
export default defineConfig({
branch,
clientId: null, // Get this from tina.io
token: null, // Get this from tina.io
build: {
outputFolder: "admin",
publicFolder: "public",
},
media: {
tina: {
mediaRoot: "images",
publicFolder: "public",
},
},
schema: {
collections: [
{
name: "posts",
label: "Posts",
path: "src/content/posts",
format: 'mdx',
fields: [
{
type: "string",
name: "title",
label: "Title",
isTitle: true,
required: true,
},
{
type: "datetime",
name: "posted",
label: "Date Posted",
required: true,
},
{
type: "rich-text",
name: "body",
label: "Body",
isBody: true,
},
],
},
],
},
});
```
Learn more about Tina collections [in the Tina docs](https://tina.io/docs/reference/collections/).
5. In production, TinaCMS can commit changes directly to your GitHub repository. To set up TinaCMS for production, you can choose to use [Tina Cloud](https://tina.io/docs/tina-cloud/) or self-host the [Tina Data Layer](https://tina.io/docs/self-hosted/overview/). You can [read more about registering for Tina Cloud](https://app.tina.io/register) in the Tina Docs.
</Steps>
## Official Resources
- [TinaCMS Astro integration guide](https://tina.io/docs/frameworks/astro/).
## Community Resources
- [Astro Tina Starter with visual editing](https://github.com/dawaltconley/tina-astro) by Jeff See + Dylan Awalt-Conley
- [Astro Tina Starter with basic editing](https://github.com/tombennet/astro-tina-starter/tree/main) by Tom Bennet
- [Using Astro’s Image Optimization with Tina](https://joschua.io/posts/2023/08/16/how-to-use-astro-assets-with-tina-cms/)
## Themes
<Grid>
<Card title="Resume01" href="https://astro.build/themes/details/resume01/" thumbnail="resume01.png"/>
</Grid>
---
File: /src/content/docs/en/guides/cms/umbraco.mdx
---
---
title: Umbraco & Astro
description: Add content to your Astro project using Umbraco as a CMS
sidebar:
label: Umbraco
type: cms
stub: false
service: Umbraco
i18nReady: true
---
import { FileTree, Steps } from '@astrojs/starlight/components';
import ReadMore from '~/components/ReadMore.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:
```json title="appsettings.json"
{
"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.
```astro title="src/pages/index.astro"
---
const res = await fetch('http://localhost/umbraco/delivery/api/v2/content?filter=contentType:article');
const articles = await res.json();
---
<h1>Astro + Umbraco 🚀</h1>
{
articles.items.map((article) => (
<h1>{article.name}</h1>
<p>{article.properties.articleDate}</p>
<div set:html={article.properties.content?.markup}></div>
))
}
```
<ReadMore>Read more about [data fetching in Astro](/en/guides/data-fetching/).</ReadMore>
## 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'.
<Steps>
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.
</Steps>
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:
```astro title="src/layouts/Layout.astro"
---
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Blog with Astro and Umbraco</title>
</head>
<body>
<main>
<slot />
</main>
</body>
</html>
```
Your project should now contain the following files:
<FileTree>
- src/
- **layouts/**
- **Layout.astro**
- pages/
- index.astro
</FileTree>
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:
```astro title="src/pages/index.astro"
---
import Layout from '../layouts/Layout.astro';
const res = await fetch('http://localhost/umbraco/delivery/api/v2/content?filter=contentType:article');
const articles = await res.json();
---
<Layout>
<h2>Blog Articles</h2>
{
articles.items.map((article: any) => (
<div>
<h3>{article.properties.title}</h3>
<p>{article.properties.articleDate}</p>
<a href={article.route.path}>Read more</a>
</div>
))
}
</Layout>
```
### 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:
```astro title="[...slug].astro"
---
import Layout from '../layouts/Layout.astro';
export async function getStaticPaths() {
let data = await fetch("http://localhost/umbraco/delivery/api/v2/content?filter=contentType:article");
let articles = await data.json();
return articles.items.map((article: any) => ({
params: { slug: article.route.path },
props: { article: article },
}));
}
const { article } = Astro.props;
---
<Layout>
<h1>{article.properties.title}</h1>
<p>{article.properties.articleDate}</p>
<div set:html={article.properties.content?.markup}></div>
</Layout>
```
Your project should now contain the following files:
<FileTree>
- src/
- layouts/
- Layout.astro
- pages/
- index.astro
- **[...slug].astro**
</FileTree>
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:
```sh title=".env.development"
NODE_TLS_REJECT_UNAUTHORIZED=0
```
```astro title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import { loadEnv } from "vite";
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
export default defineConfig({});
```
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)
---
File: /src/content/docs/en/guides/cms/wordpress.mdx
---
---
title: Headless WordPress & Astro
description: Add content to your Astro project using WordPress as a CMS
sidebar:
label: Wordpress
type: cms
stub: false
service: WordPress
i18nReady: true
---
import { FileTree } from '@astrojs/starlight/components';
import Grid from '~/components/FluidGrid.astro'
import Card from '~/components/ShowcaseCard.astro'
import { LinkCard, CardGrid } from '@astrojs/starlight/components';
[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:
```astro title="src/pages/index.astro"
---
const res = await fetch("https://[YOUR-SITE]/wp-json/wp/v2/posts");
const posts = await res.json();
---
<h1>Astro + WordPress 🚀</h1>
{
posts.map((post) => (
<h2 set:html={post.title.rendered} />
<p set:html={post.content.rendered} />
))
}
```
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.
<FileTree title="Project Structure">
- src/
- pages/
- **index.astro**
- dinos/
- [slug].astro
- astro.config.mjs
- package.json
</FileTree>
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.)
```astro title="/src/pages/index.astro"
---
import Layout from "../layouts/Layout.astro";
let res = await fetch("https://norian.studio/wp-json/wp/v2/dinos");
let posts = await res.json();
---
<Layout title="Dinos!">
<section>
<h1>List of Dinosaurs</h1>
{
posts.map((post) => (
<article>
<h2>
<a href={`/dinos/${post.slug}/`} set:html={post.title.rendered} />
</h2>
<Fragment set:html={post.content.rendered} />
</article>
))
}
</section>
</Layout>
```
### 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.
```astro title="/src/pages/dinos/[slug].astro"
---
import Layout from '../../layouts/Layout.astro';
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.
export async function getStaticPaths() {
let data = await fetch("https://norian.studio/wp-json/wp/v2/dinos");
let posts = await data.json();
return posts.map((post) => ({
params: { slug: post.slug },
props: { post: post },
}));
}
---
<Layout title={post.title.rendered}>
<article>
<h1 set:html={post.title.rendered} />
<Fragment set:html={post.content.rendered} />
</article>
</Layout>
```
### Returning embedded resources
The `_embed` query parameter instructs the server to return related (embedded) resources.
```astro title="src/pages/dinos/[slug].astro" /&_embed/
---
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.)
```astro title="/src/pages/dinos/[slug].astro" {3}
<Layout title={post.title.rendered}>
<article>
<img src={post._embedded['wp:featuredmedia']['0'].media_details.sizes.medium.source_url} />
<h1 set:html={post.title.rendered} />
<Fragment set:html={post.content.rendered} />
</article>
</Layout>
```
### 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.
## Production Sites
The following sites use Astro + WordPress in production:
- [Dinos!](https://wc-dinos.netlify.app/) by Anindo Neel Dutta — [source code on GitHub](https://github.com/leen-neel/astro-wordpress)
## Themes
<Grid>
<Card title="Astro WordPress Starter" href="https://astro.build/themes/details/astro-wordpress-starter/" thumbnail="astro-wordpress-starter.png"/>
</Grid>
## Community Resources
<CardGrid>
<LinkCard href="https://dev.to/bngmnn/leveraging-wordpress-as-a-headless-cms-for-your-astro-website-a-comprehensive-guide-a4d" title="Introduction to Astro + WordPress"/>
<LinkCard title="Astro + WPGraphQL for more secure WordPress sites" href="https://www.youtube.com/watch?v=fWxn-r83ygQ" />
<LinkCard title="Shattering Headless WordPress Build Times with Astro's Content Layer API" href="https://andrewkepson.com/blog/headless-wordpress/build-time-astro-content-layer-api/"/>
<LinkCard title="How to Set Up a Headless WordPress Site with Astro" href="https://dev.to/mathiasahlgren/how-to-set-up-a-headless-wordpress-site-with-astro-3a2h" />
<LinkCard title="Build a static site with WordPress and Astro" href="https://kinsta.com/blog/wordpress-astro/" />
<LinkCard title="Going Headless WordPress with Astro" href="https://www.youtube.com/watch?v=MP2TR6Z_YTc" />
<LinkCard title="Leveraging WordPress as a Headless CMS for Your Astro Website: API Configuration & Data Fetching" href="https://medium.com/@bangemann.dev/configure-wordpress-rest-api-setup-data-fetching-4af5161095f6" />
<LinkCard title="WordPress Headless with Astro - Installing Astro and Fetching posts with WP-GraphQL" href="https://www.youtube.com/watch?v=2PSqABrME28" />
<LinkCard title="Make a Headless WordPress Site with Astro" href="https://www.youtube.com/watch?v=54U7dVmhyxE" />
<LinkCard title="WPEngine Astro Headless WordPress Starter Demo" href="https://www.youtube.com/watch?v=BcoxZZIfESI" />
<LinkCard title="Headless WordPress with Astro – Build a Simple Blog from Scratch with Tailwind CSS" href="https://fullstackdigital.io/blog/headless-wordpress-with-astro-build-a-simple-blog/" />
<LinkCard title="Building an E-commerce Website with Headless WordPress and Astro" href="https://shaxadd.medium.com/building-an-e-commerce-website-with-headless-wordpress-and-astro-2712d8c8b735" />
<LinkCard title="Building a Headless WordPress Site with Astro" href="https://wpengine.com/builders/building-headless-wordpress-site-astro/" />
<LinkCard title="Building an Astro Website with WordPress as a Headless CMS" href="https://blog.openreplay.com/building-an-astro-website-with-wordpress-as-a-headless-cms/" />
</CardGrid>
:::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)!
:::
---
File: /src/content/docs/en/guides/deploy/aws.mdx
---
---
title: Deploy your Astro Site to AWS
description: How to deploy your Astro site to the web using AWS.
sidebar:
label: AWS
type: deploy
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
[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.
<Steps>
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.
<PackageManagerTabs>
<Fragment slot="pnpm">
```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/**/*
```
</Fragment>
<Fragment slot="npm">
```yaml
version: 1
frontend:
phases:
preBuild:
commands:
- npm ci
build:
commands:
- npm run build
artifacts:
baseDirectory: /dist
files:
- '**/*'
cache:
paths:
- node_modules/**/*
```
</Fragment>
<Fragment slot="yarn">
```yaml
version: 1
frontend:
phases:
preBuild:
commands:
- yarn install
build:
commands:
- yarn build
artifacts:
baseDirectory: /dist
files:
- '**/*'
cache:
paths:
- node_modules/**/*
```
</Fragment>
</PackageManagerTabs>
</Steps>
Amplify will automatically deploy your website and update it when you push a commit to your repository.
## 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/).
<Steps>
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://<BUCKET_NAME>/ --recursive
```
4. Update your bucket policy to allow public access. You can find this setting in the bucket's **Permissions > Bucket policy**.
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<BUCKET_NAME>/*"
}
]
}
```
:::caution
Do not forget to replace `<BUCKET_NAME>` 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`.
:::
</Steps>
## 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.
<Steps>
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": [
"<DISTRIBUTION_ARN>",
"arn:aws:s3:::<BUCKET_NAME>/*",
"arn:aws:s3:::<BUCKET_NAME>"
]
}
]
}
```
:::caution
Do not forget to replace `<DISTRIBUTION_ARN>` and `<BUCKET_NAME>`. 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 <kbd>New repository secret</kbd> to add each one.
```yaml
name: Deploy Website
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- 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**
:::
</Steps>
## 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)
---
File: /src/content/docs/en/guides/deploy/azion.mdx
---
---
title: Deploy your Astro Site to Azion
description: How to deploy your Astro site to the web using Azion.
sidebar:
label: Azion
type: deploy
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
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:
<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.
</Steps>
## How to Deploy a Static Site Using the Azion CLI
<Steps>
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.
```bash
azion login
```
3. **Set Up Your Application:**
- Use the following commands to initialize and configure your project:
```bash
azion init
```
4. **Build Your Astro Project:**
- Run your build command locally:
```bash
azion build
```
5. **Deploy Your Static Files:**
- Deploy your static files using the Azion CLI:
```bash
azion deploy
```
</Steps>
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:
```bash
azion dev
```
Once you've initialized the local development server, the application goes through the `build` process.
```bash
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:
```bash
[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.
---
File: /src/content/docs/en/guides/deploy/buddy.mdx
---
---
title: Deploy your Astro Site with Buddy
description: How to deploy your Astro site to the web using Buddy.
sidebar:
label: Buddy
type: deploy
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
You can deploy your Astro project using [Buddy](https://buddy.works/), a CI/CD solution that can build your site and push it to many different deploy targets including FTP servers and cloud hosting providers.
:::note
Buddy itself will not host your site. Instead, it helps you manage the build process and deliver the result to a deploy platform of your choice.
:::
## How to deploy
<Steps>
1. [Create a **Buddy** account](https://buddy.works/sign-up).
2. Create a new project and connect it with a git repository (GitHub, GitLab, BitBucket, any private Git Repository or you can use Buddy Git Hosting).
3. Add a new pipeline.
4. In the newly created pipeline add a **[Node.js](https://buddy.works/actions/node-js)** action.
5. In this action add:
```bash
npm install
npm run build
```
6. Add a deployment action — there are many to choose from, you can browse them in [Buddy’s actions catalog](https://buddy.works/actions). Although their settings can differ, remember to set the **Source path** to `dist`.
7. Press the **Run** button.
</Steps>
---
File: /src/content/docs/en/guides/deploy/cleavr.mdx
---
---
title: Deploy your Astro Site with Cleavr
description: How to deploy your Astro site to your VPS server using Cleavr.
sidebar:
label: Cleavr
type: deploy
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
You can deploy your Astro project to your own Virtual Private Server (VPS) using [Cleavr](https://cleavr.io/), a server and app deployment management tool.
:::tip
Check out [the Astro guide in Cleavr's docs](https://docs.cleavr.io/guides/astro)!
:::
## Prerequisites
To get started, you will need:
- A Cleavr account
- A server on your VPS provider using Cleavr
## Add your site
<Steps>
1. In Cleavr, navigate to the server you want to add your Astro project to.
2. Select **Add Site** and fill in the details for your application, such as domain name.
3. For **App Type**, select 'NodeJS Static' or 'NodeJS SSR' according to how you are setting up your Astro app.
4. For Static apps, set **Artifact Folder** to `dist`.
5. For SSR apps:
- Set **Entry Point** to `entry.mjs`.
- Set **Artifact Folder** to `dist/server`.
6. Select **Add** to add the site to your server.
</Steps>
## Setup and deploy
<Steps>
1. Once your new site is added, click **Setup and deploy**.
2. Select the **VC Profile**, **Repo**, and **Branch** for your Astro Project.
3. Make any additional configurations necessary for your project.
4. Click on the **Deployments** tab and then click on **Deploy**.
</Steps>
Congratulations, you've just deployed your Astro app!
---
File: /src/content/docs/en/guides/deploy/clever-cloud.mdx
---
---
title: Deploy your Astro Site to Clever Cloud
description: How to deploy your Astro site to the web on Clever Cloud.
sidebar:
label: Clever Cloud
type: deploy
i18nReady: true
---
import { Tabs, TabItem, Steps } from '@astrojs/starlight/components';
[Clever Cloud](https://clever-cloud.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**.
```json title="package.json"
"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.
<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.clever-cloud.com/developers/doc/addons/) unless you're using a database or Keycloak.
7. Inject **environment variables**:
- For **Node.js**, no specific environment variable is needed to deploy Astro if you're using **npm**. If you're using **yarn** or **pnpm**, set the following environment variables:
<Tabs>
<TabItem label="pnpm">
```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"
```
</TabItem>
<TabItem label="yarn">
```shell
CC_NODE_BUILD_TOOL="yarn"
CC_PRE_BUILD_HOOK="yarn && yarn run astro telemetry disable && yarn build"
```
</TabItem>
</Tabs>
- For a **static** application, add these variables:
<Tabs>
<TabItem label="npm">
```shell
CC_POST_BUILD_HOOK="npm run build"
CC_PRE_BUILD_HOOK="npm install && npm run astro telemetry disable"
CC_WEBROOT="/dist"
```
</TabItem>
<TabItem label="pnpm">
```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"
```
</TabItem>
<TabItem label="yarn">
```shell
CC_POST_BUILD_HOOK="yarn build"
CC_PRE_BUILD_HOOK="yarn && yarn run astro telemetry disable"
CC_WEBROOT="/dist"
```
</TabItem>
</Tabs>
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.
</Steps>
:::tip[Other Branches]
To deploy from branches other than `master`, use `git push clever <branch>: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.clever-cloud.com/developers/doc/applications/javascript/nodejs/)
- [Clever Cloud documentation for deploying Astro as a static application](https://www.clever-cloud.com/developers/guides/astro/)
---
File: /src/content/docs/en/guides/deploy/cloudflare.mdx
---
---
title: Deploy your Astro Site to Cloudflare
description: How to deploy your Astro site to the web using Cloudflare
sidebar:
label: Cloudflare
type: deploy
i18nReady: true
---
import ReadMore from '~/components/ReadMore.astro';
import { Steps } from '@astrojs/starlight/components';
import StaticSsrTabs from '~/components/tabs/StaticSsrTabs.astro';
You can deploy full-stack applications, including front-end static assets and back-end APIs, as well as on-demand rendered sites, to both [Cloudflare Workers](https://developers.cloudflare.com/workers/static-assets/) and [Cloudflare Pages](https://pages.cloudflare.com/).
This guide includes:
- [How to deploy to Cloudflare Workers](#cloudflare-workers)
- [How to deploy to Cloudflare Pages](#cloudflare-pages)
<ReadMore>Read more about [using the Cloudflare runtime](/en/guides/integrations-guide/cloudflare/) in your Astro project.</ReadMore>
## Prerequisites
To get started, you will need:
- A Cloudflare account. If you don’t already have one, you can create a free Cloudflare account during the process.
## Cloudflare Workers
### How to deploy with Wrangler
<Steps>
1. Install [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/get-started/).
```bash
npm install wrangler@latest --save-dev
```
2. If your site uses on demand rendering, install the [`@astrojs/cloudflare` adapter](/en/guides/integrations-guide/cloudflare/).
This will install the adapter and make the appropriate changes to your `astro.config.mjs` file in one step.
```bash
npx astro add cloudflare
```
Then, create a `.assetsignore` file in your `public/` folder, and add the following lines to it:
```txt title="public/.assetsignore"
_worker.js
_routes.json
```
<ReadMore>Read more about [on-demand rendering in Astro](/en/guides/on-demand-rendering/).</ReadMore>
3. Create a [Wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/).
<StaticSsrTabs>
<Fragment slot="static">
```jsonc
// wrangler.jsonc
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-astro-app",
// Update to today's date
"compatibility_date": "2025-03-25",
"assets": {
"directory": "./dist"
}
}
```
</Fragment>
<Fragment slot="ssr">
```jsonc
// wrangler.jsonc
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-astro-app",
"main": "./dist/_worker.js/index.js",
// Update to today's date
"compatibility_date": "2025-03-25",
"compatibility_flags": ["nodejs_compat"],
"assets": {
"binding": "ASSETS",
"directory": "./dist"
},
"observability": {
"enabled": true
}
}
```
</Fragment>
</StaticSsrTabs>
4. Preview your project locally with Wrangler.
```bash
npx astro build && npx wrangler dev
```
5. Deploy using `npx wrangler deploy`.
```bash
npx astro build && npx wrangler deploy
```
</Steps>
After your assets are uploaded, Wrangler will give you a preview URL to inspect your site.
<ReadMore>Read more about using [Cloudflare runtime APIs](/en/guides/integrations-guide/cloudflare/) such as bindings.</ReadMore>
### How to deploy with CI/CD
You can also use a CI/CD system such as [Workers Builds (BETA)](https://developers.cloudflare.com/workers/ci-cd/builds/) to automatically build and deploy your site on push.
If you're using Workers Builds:
<Steps>
1. Follow Steps 1-3 from the Wrangler section above.
2. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/) and navigate to `Workers & Pages`. Select `Create`.
3. Under `Import a repository`, select a Git account and then the repository containing your Astro project.
4. Configure your project with:
- Build command: `npx astro build`
- Deploy command: `npx wrangler deploy`
5. Click `Save and Deploy`. You can now preview your Worker at its provided `workers.dev` subdomain.
</Steps>
## Cloudflare Pages
### How to deploy with Wrangler
<Steps>
1. Install [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/get-started/).
```bash
npm install wrangler@latest --save-dev
```
2. If your site uses on demand rendering, install the [`@astrojs/cloudflare` adapter](/en/guides/integrations-guide/cloudflare/).
This will install the adapter and make the appropriate changes to your `astro.config.mjs` file in one step.
```bash
npx astro add cloudflare
```
<ReadMore>Read more about [on demand rendering in Astro](/en/guides/on-demand-rendering/).</ReadMore>
3. Preview your project locally with Wrangler.
```bash
npx astro build && npx wrangler pages dev ./dist
```
4. Deploy using `npx wrangler deploy`.
```bash
npx astro build && npx wrangler pages deploy ./dist
```
</Steps>
After your assets are uploaded, Wrangler will give you a preview URL to inspect your site.
### How to deploy a site with Git
<Steps>
1. Push your code to your git repository (e.g. GitHub, GitLab).
2. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/) and navigate to `Workers & Pages`. Select `Create` and then select the `Pages` tab. Connect your git repository.
3. Configure your project with:
- **Framework preset**: `Astro`
- **Build command:** `npm run build`
- **Build output directory:** `dist`
4. Click the **Save and Deploy** button.
</Steps>
## Troubleshooting
### Client-side hydration
Client-side hydration may fail as a result of Cloudflare's Auto Minify setting. If you see `Hydration completed but contains mismatches` in the console, make sure to disable Auto Minify under Cloudflare settings.
### Node.js runtime APIs
If you are building a project that is using on-demand rendering with [the Cloudflare adapter](/en/guides/integrations-guide/cloudflare/) and the server 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 in the server-side environment is not compatible with the [Cloudflare runtime APIs](https://developers.cloudflare.com/workers/runtime-apis/nodejs/).
- If you are directly importing a Node.js runtime API, please refer to the Astro documentation on Cloudflare's [Node.js compatibility](/en/guides/integrations-guide/cloudflare/#nodejs-compatibility) 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.
---
File: /src/content/docs/en/guides/deploy/deno.mdx
---
---
title: Deploy your Astro Site with Deno
description: How to deploy your Astro site to the web using Deno.
sidebar:
label: Deno
type: deploy
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
import StaticSsrTabs from '~/components/tabs/StaticSsrTabs.astro';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
You can deploy a static or on-demand rendered Astro site using Deno, either on your own server, or to [Deno Deploy](https://deno.com/deploy), a distributed system that runs JavaScript, TypeScript, and WebAssembly at the edge, worldwide.
This guide includes instructions for running your Astro site on your own server with Deno, and deploying to Deno Deploy through GitHub Actions or the Deno Deploy CLI.
## Requirements
This guide assumes you already have [Deno](https://deno.com/) installed.
## Project Configuration
Your Astro project can be deployed as a static site, or as an on-demand 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 with Deno, or to Deno Deploy.
### Adapter for on-demand rendering
To enable on-demand rendering in your Astro project using Deno, and to deploy on Deno Deploy:
<Steps>
1. Install [the `@deno/astro-adapter` adapter][Deno adapter] to your project’s dependencies using your preferred package manager:
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install @deno/astro-adapter
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm install @deno/astro-adapter
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn add @deno/astro-adapter
```
</Fragment>
</PackageManagerTabs>
2. Update your `astro.config.mjs` project configuration file with the changes below.
```js ins={3,6-7}
// astro.config.mjs
import { defineConfig } from 'astro/config';
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.
```json del={8} ins={9}
// 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.
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm run preview
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm run preview
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn run preview
```
</Fragment>
</PackageManagerTabs>
</Steps>
## 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
<Steps>
1. Copy your project onto your server.
2. Install the project dependencies using your preferred package manager:
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm install
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn
```
</Fragment>
</PackageManagerTabs>
3. Build your Astro site with your preferred package manager:
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm run build
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm run build
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn run build
```
</Fragment>
</PackageManagerTabs>
4. Start your application with the following command:
<StaticSsrTabs>
<Fragment slot="static">
```bash
deno run -A jsr:@std/http/file-server dist
```
</Fragment>
<Fragment slot="ssr">
```bash
deno run -A ./dist/server/entry.mjs
```
</Fragment>
</StaticSsrTabs>
</Steps>
### 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.
<Steps>
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.
<StaticSsrTabs>
<Fragment slot="static">
```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@v4
# Not using npm? Change `npm ci` to `yarn install` or `pnpm i`
- name: Install dependencies
run: npm ci
# Not using npm? Change `npm run build` to `yarn build` or `pnpm 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
```
</Fragment>
<Fragment slot="ssr">
```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@v4
# Not using npm? Change `npm ci` to `yarn install` or `pnpm i`
- name: Install dependencies
run: npm ci
# Not using npm? Change `npm run build` to `yarn build` or `pnpm 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
```
</Fragment>
</StaticSsrTabs>
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).
</Steps>
### CLI Deployment
<Steps>
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:
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm run build
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm run build
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn run build
```
</Fragment>
</PackageManagerTabs>
3. Run `deployctl` to deploy!
<StaticSsrTabs>
<Fragment slot="static">
```bash
cd dist && deployctl deploy jsr:@std/http/file-server
```
</Fragment>
<Fragment slot="ssr">
```bash
deployctl deploy ./dist/server/entry.mjs
```
</Fragment>
</StaticSsrTabs>
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`.
<StaticSsrTabs>
<Fragment slot="static">
```json ins={9}
// 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"
}
}
```
</Fragment>
<Fragment slot="ssr">
```json ins={9}
// 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"
}
}
```
</Fragment>
</StaticSsrTabs>
Then you can use this command to build and deploy your Astro site in one step.
```bash
npm run deno-deploy
```
</Steps>
[Deno adapter]: https://github.com/denoland/deno-astro-adapter
---
File: /src/content/docs/en/guides/deploy/edgio.mdx
---
---
title: Deploy your Astro Site to Edgio
description: How to deploy your Astro site to the web using Edgio.
sidebar:
label: Edgio
type: deploy
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
You can deploy your Astro project to [Edgio](https://www.edg.io/), an edge and CDN platform to deploy, protect and accelerate websites and APIs.
:::tip
Check out [the Astro guide in Edgio’s docs](https://docs.edg.io/guides/astro)!
:::
## How to deploy
<Steps>
1. Install [the Edgio CLI](https://docs.edg.io/guides/cli) globally from the Terminal, if you haven’t already.
```bash
npm install -g @edgio/cli
```
2. Add Edgio to your Astro site
```bash
edgio init
```
3. (Optional) Enable Server Side Rendering
After you’ve set up [@astrojs/node with Astro](/en/guides/integrations-guide/node/), specify the server file path in `edgio.config.js` as below:
```js ins={2,5-9}
// edgio.config.js
import { join } from 'path'
module.exports = {
astro: {
// The path of the standalone server that runs Astro SSR.
// The dependencies for this file are automatically bundled.
appPath: join(process.cwd(), 'dist', 'server', 'entry.mjs'),
},
};
```
4. Deploy to Edgio
```bash
edgio deploy
```
</Steps>
---
File: /src/content/docs/en/guides/deploy/fleek.mdx
---
---
title: Deploy your Astro Site to Fleek
description: How to deploy your Astro site to the web on Fleek.
sidebar:
label: Fleek
type: deploy
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
import ReadMore from '~/components/ReadMore.astro';
You can use [Fleek](http://fleek.xyz/) to deploy a static Astro site to their edge-optimized decentralized network.
This guide gives a complete walkthrough of deploying your Astro site to Fleek using the Fleek UI and CLI.
## Project Configuration
Your Astro project can be deployed to Fleek as a static site.
## How to deploy
You can deploy to Fleek through the website UI or using Fleek’s CLI (command line interface).
### Platform UI Deployment
<Steps>
1. Create a [Fleek](https://app.fleek.xyz) account.
2. Push your code to your online Git repository (GitHub).
3. Import your project into Fleek.
4. Fleek will automatically detect Astro and then you can configure the correct settings.
5. Your application is deployed!
</Steps>
### Fleek CLI
<Steps>
1. Install the Fleek CLI.
```bash
# You need to have Nodejs >= 18.18.2
npm install -g @fleek-platform/cli
```
2. Log in to your Fleek account from your terminal.
```bash
fleek login
```
3. Run the build command to generate the static files. By default, these will be located in the `dist/` directory.
```bash
npm run build
```
4. Initialize your project. This will generate a configuration file.
```bash
fleek sites init
```
5. You will be prompted to either create a new Fleek Site or use an existing one. Give the site a name and select the directory where your project is located.
6. Deploy your site.
```bash
fleek sites deploy
```
</Steps>
## Learn more
<ReadMore>[Deploy site from Fleek UI](https://fleek.xyz/docs/platform/deployments/)</ReadMore>
<ReadMore>[Deploy site from Fleek CLI](https://fleek.xyz/docs/cli/hosting/)</ReadMore>
---
File: /src/content/docs/en/guides/deploy/flightcontrol.mdx
---
---
title: Deploy your Astro Site to AWS with Flightcontrol
description: How to deploy your Astro site to AWS with Flightcontrol
sidebar:
label: Flightcontrol
type: deploy
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
You can deploy an Astro site using [Flightcontrol](https://www.flightcontrol.dev?ref=astro), which provides fully-automated deployments to your AWS account.
Supports both static and SSR Astro sites.
## How to Deploy
<Steps>
1. Create a Flightcontrol account at [app.flightcontrol.dev/signup](https://app.flightcontrol.dev/signup?ref=astro)
2. Go to [app.flightcontrol.dev/projects/new/1](https://app.flightcontrol.dev/projects/new/1)
3. Connect your GitHub account and select your repo
4. Select your desired "Config Type":
- `GUI` (all config managed through Flightcontrol dashboard) where you will select the `Astro Static` or `Astro SSR` preset
- `flightcontrol.json` ("infrastructure as code" option where all config is in your repo) where you will select an Astro example config, then add it to your codebase as `flightcontrol.json`
5. Adjust any configuration as needed
6. Click "Create Project" and complete any required steps (like linking your AWS account).
</Steps>
### SSR Setup
To deploy with SSR support, make sure you first set up the [`@astrojs/node`](/en/guides/integrations-guide/node/) adapter. Then, follow the steps above, choosing the appropriate configurations for Astro SSR.
---
File: /src/content/docs/en/guides/deploy/flyio.mdx
---
---
title: Deploy your Astro Site to Fly.io
description: How to deploy your Astro site to the web using Fly.io.
sidebar:
label: Fly.io
type: deploy
i18nReady: true
stub: true
---
import { Steps } from '@astrojs/starlight/components';
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
<Steps>
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.
```bash
fly launch
```
`flyctl` will automatically detect Astro, configure the correct settings, build your image, and deploy it to the Fly.io platform.
</Steps>
## 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/)
---
File: /src/content/docs/en/guides/deploy/github.mdx
---
---
title: Deploy your Astro Site to GitHub Pages
description: How to deploy your Astro site to the web using GitHub Pages.
sidebar:
label: GitHub Pages
type: deploy
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
You can use [GitHub Pages](https://pages.github.com/) to host an Astro website directly from a repository on [GitHub.com](https://github.com/).
## How to deploy
You can deploy an Astro site to GitHub Pages by using [GitHub Actions](https://github.com/features/actions) to automatically build and deploy your site. To do this, your source code must be hosted on GitHub.
Astro maintains the official `withastro/action` to deploy your project with very little configuration. Follow the instructions below to deploy your Astro site to GitHub pages, and see [the package README](https://github.com/withastro/action) if you need more information.
## Configure Astro for GitHub Pages
### Deploying to a `github.io` URL
Set the [`site`](/en/reference/configuration-reference/#site) and [`base`](/en/reference/configuration-reference/#base) options in `astro.config.mjs`.
```js title="astro.config.mjs" ins={4-5}
import { defineConfig } from 'astro/config'
export default defineConfig({
site: 'https://astronaut.github.io',
base: 'my-repo',
})
```
#### `site`
The value for `site` must be one of the following:
- The following URL based on your username: `https://<username>.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://<random-string>.pages.github.io/`
#### `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.
- Your repository is located at `https://github.com/<USERNAME>/<USERNAME>.github.io`.
:::
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:
```astro ins="/my-repo"
<a href="/my-repo/about">About</a>
```
See more about [configuring a `base` value](/en/reference/configuration-reference/#base)
:::
### Using GitHub pages with a custom domain
:::tip[Set up a custom domain]
You can set up a custom domain by adding the following `./public/CNAME` file to your project:
```js title="public/CNAME"
sub.mydomain.com
```
This will deploy your site at your custom domain instead of `user.github.io`. Don't forget to also [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).
:::
To configure Astro for using GitHub pages with a custom domain, set your domain as the value for `site`. Do not set a value for `base`:
```js title="astro.config.mjs" ins={4}
import { defineConfig } from 'astro/config'
export default defineConfig({
site: 'https://example.com',
})
```
## Configure a GitHub Action
<Steps>
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@v4
- name: Install, build, and upload your site
uses: withastro/action@v3
# with:
# path: . # The root location of your Astro project inside the repository. (optional)
# node-version: 20 # The specific version of Node that should be used to build your site. Defaults to 20. (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)
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@v4
```
:::note
The astro action takes a few optional inputs. These can be provided by uncommenting the `with:` line and the input you want to use.
:::
:::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. On GitHub, go to your repository’s **Settings** tab and find the **Pages** section of the settings.
3. Choose **GitHub Actions** as the **Source** of your site.
4. Commit the new workflow file and push it to GitHub.
</Steps>
Your site should now be published! When you push changes to your Astro project’s repository, the GitHub Action will automatically deploy them for you.
## Examples
- [Github Pages Deployment](https://github.com/hkbertoson/github-pages)
---
File: /src/content/docs/en/guides/deploy/gitlab.mdx
---
---
title: Deploy your Astro Site to GitLab Pages
description: How to deploy your Astro site to the web using GitLab Pages.
sidebar:
label: GitLab Pages
type: deploy
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
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:
<Steps>
1. Set up [`site`](/en/reference/configuration-reference/#site) and [`base`](/en/reference/configuration-reference/#base) options in `astro.config.mjs`.
```js title="astro.config.mjs" ins={4-5}
import { defineConfig } from 'astro/config';
export default defineConfig({
site: 'https://<username>.gitlab.io',
base: '/<my-repo>',
outDir: 'public',
publicDir: 'static',
});
```
`site`
The value for `site` must be one of the following:
- The following URL based on your username: `https://<username>.gitlab.io`
- The following URL based on your group name: `https://<groupname>.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:
```astro ins="/my-repo"
<a href="/my-repo/about">About</a>
```
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/`:
```js title="astro.config.mjs" ins={4-5}
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/`:
```diff title=".gitignore"
# 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:
```yaml title=".gitlab-ci.yml"
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.
</Steps>
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.
---
File: /src/content/docs/en/guides/deploy/google-cloud.mdx
---
---
title: Deploy your Astro Site to Google Cloud
description: How to deploy your Astro site to the web using Google Cloud.
sidebar:
label: Google Cloud
type: deploy
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
[Google Cloud](https://cloud.google.com/) is a full-featured web app hosting platform that can be used to deploy an Astro site.
## How to deploy
### Cloud Storage (static only)
<Steps>
1. [Create a new GCP project](https://console.cloud.google.com/projectcreate), or select one you already have.
2. Create a new bucket under [Cloud Storage](https://cloud.google.com/storage).
3. Give it a name and the other required settings.
4. Upload your `dist` folder into it or upload using [Cloud Build](https://cloud.google.com/build).
5. Enable public access by adding a new permission to `allUsers` called `Storage Object Viewer`.
6. Edit the website configuration and add `ìndex.html` as the entrypoint and `404.html` as the error page.
</Steps>
### Cloud Run (SSR and static)
Cloud Run is a serverless platform that allows you to run a container without having to manage any infrastructure. It can be used to deploy both static and SSR sites.
#### Prepare the Service
<Steps>
1. [Create a new GCP project](https://console.cloud.google.com/projectcreate), or select one you already have.
2. Make sure the [Cloud Run API](https://console.cloud.google.com/apis/library/run.googleapis.com) is enabled.
3. Create a new service.
</Steps>
#### Create Dockerfile & Build the Container
Before you can deploy your Astro site to Cloud Run, you need to create a Dockerfile that will be used to build the container. Find more information about [how to use Docker with Astro](/en/recipes/docker/#creating-a-dockerfile) in our recipe section.
Once the Dockerfile is created, build it into an image and push it to Google Cloud. There are a few ways to accomplish this:
**Build locally using Docker**:
Use the `docker build` command to build the image, `docker tag` to give it a tag, then `docker push` to push it to a registry. In the case of Google Cloud, [`Artifact Registry`](https://cloud.google.com/artifact-registry/docs/docker/pushing-and-pulling) is the easiest option, but you can also use [Docker Hub](https://hub.docker.com/).
```bash
# build your container
docker build .
docker tag SOURCE_IMAGE HOSTNAME/PROJECT-ID/TARGET-IMAGE:TAG
# Push your image to a registry
docker push HOSTNAME/PROJECT-ID/IMAGE:TAG
```
Change the following values in the commands above to match your project:
- `SOURCE_IMAGE`: the local image name or image ID.
- `HOSTNAME`: the registry host (`gcr.io`, `eu.gcr.io`, `asia.gcr.io`, `us.gcr.io`, `docker.io`).
- `PROJECT`: your Google Cloud project ID.
- `TARGET-IMAGE`: the name for the image when it's stored in the registry.
- `TAG` is the version associated with the image.
[Read more in the Google Cloud docs.](https://cloud.google.com/artifact-registry/docs/docker/pushing-and-pulling)
**Using another tool**:
You can use a CI/CD tool that supports Docker, like [GitHub Actions](https://github.com/marketplace/actions/push-to-gcr-github-action).
**Build using [Cloud Build](https://cloud.google.com/build)**:
Instead of building the Dockerfile locally, you can instruct Google Cloud to build the image remotely. See the [Google Cloud Build documentation here](https://cloud.google.com/build/docs/build-push-docker-image).
#### Deploying the container
Deployment can be handled manually in your terminal [using `gcloud`](https://cloud.google.com/run/docs/deploying#service) or automatically using [Cloud Build](https://cloud.google.com/build) or any other CI/CD system.
:::note[Need public access?]
Don't forget to add the permission `Cloud Run Invoker` to the `allUsers` group in the Cloud Run permissions settings!
:::
---
File: /src/content/docs/en/guides/deploy/google-firebase.mdx
---
---
title: Deploy your Astro Site to Google’s Firebase Hosting
description: How to deploy your Astro site to the web using Google’s Firebase Hosting.
sidebar:
label: Google Firebase
type: deploy
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
[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/google-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
<Steps>
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.
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install firebase-tools
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm add firebase-tools
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn add firebase-tools
```
</Fragment>
</PackageManagerTabs>
2. Authenticate the Firebase CLI with your Google account. This will open a browser window where you can log in to your Google account.
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npx firebase login
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm exec firebase login
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn firebase login
```
</Fragment>
</PackageManagerTabs>
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.
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npx firebase experiments:enable webframeworks
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm exec firebase experiments:enable webframeworks
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn firebase experiments:enable webframeworks
```
</Fragment>
</PackageManagerTabs>
4. Initialize Firebase Hosting in your project. This will create a `firebase.json` and `.firebaserc` file in your project root.
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npx firebase init hosting
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm exec firebase init hosting
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn firebase init hosting
```
</Fragment>
</PackageManagerTabs>
5. Deploy your site to Firebase Hosting. This will build your Astro site and deploy it to Firebase.
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npx firebase deploy --only hosting
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm exec firebase deploy --only hosting
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn firebase deploy --only hosting
```
</Fragment>
</PackageManagerTabs>
</Steps>
---
File: /src/content/docs/en/guides/deploy/heroku.mdx
---
---
title: Deploy your Astro Site to Heroku
description: How to deploy your Astro site to the web using Heroku.
sidebar:
label: Heroku
type: deploy
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
[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.
:::danger
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
<Steps>
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:
```bash
$ heroku login
```
4. Create a file called `static.json` in the root of your project with the below content:
```json title="static.json"
{
"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:
```bash
# 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:
```bash
# publish site
$ git push heroku master
# opens a browser to view the Dashboard version of Heroku CI
$ heroku open
```
</Steps>
---
File: /src/content/docs/en/guides/deploy/index.mdx
---
---
title: Deploy your Astro Site
description: How to deploy your Astro site to the web.
sidebar:
label: Deployment overview
i18nReady: true
---
import DeployGuidesNav from '~/components/DeployGuidesNav.astro';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import { Steps } from '@astrojs/starlight/components'
**Ready to build and deploy your Astro site?** Follow one of our guides to different deployment services or scroll down for general guidance about deploying an Astro site.
## Deployment Guides
<DeployGuidesNav />
## Quick Deploy Options
You can build and deploy an Astro site to a number of hosts quickly using either their website's dashboard UI or a CLI.
### Website UI
A quick way to deploy your website is to connect your Astro project's online Git repository (e.g. GitHub, GitLab, Bitbucket) to a host provider and take advantage of continuous deployment using Git.
These host platforms automatically detect pushes to your Astro project’s source repository, build your site and deploy it to the web at a custom URL or your personal domain. Often, setting up a deployment on these platforms will follow steps something like the following:
<Steps>
1. Add your repository to an online Git provider (e.g. in GitHub, GitLab, Bitbucket)
2. Choose a host that supports **continuous deployment** (e.g. [Netlify](/en/guides/deploy/netlify/) or [Vercel](/en/guides/deploy/vercel/)) and import your Git repository as a new site/project.
Many common hosts will recognize your project as an Astro site, and should choose the appropriate configuration settings to build and deploy your site as shown below. (If not, these settings can be changed.)
:::note[Deploy settings]
- **Build Command:** `astro build` or `npm run build`
- **Publish directory:** `dist`
:::
3. Click "Deploy" and your new website will be created at a unique URL for that host (e.g. `new-astro-site.netlify.app`).
</Steps>
The host will be automatically configured to watch your Git provider's main branch for changes, and to rebuild and republish your site at each new commit. These settings can typically be configured in your host provider's dashboard UI.
### CLI Deployment
Some hosts will have their own command line interface (CLI) you can install globally to your machine using npm. Often, using a CLI to deploy looks something like the following:
<Steps>
1. Install your host's CLI globally, for example:
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install --global netlify-cli
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm add --global netlify-cli
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn global add netlify-cli
```
</Fragment>
</PackageManagerTabs>
2. Run the CLI and follow any instructions for authorization, setup etc.
3. Build your site and deploy to your host
Many common hosts will build and deploy your site for you. They will usually recognize your project as an Astro site, and should choose the appropriate configuration settings to build and deploy as shown below. (If not, these settings can be changed.)
:::note[Deploy settings]
- **Build Command:** `astro build` or `npm run build`
- **Publish directory:** `dist`
:::
Other hosts will require you to [build your site locally](#building-your-site-locally) and deploy using the command line.
</Steps>
## Building Your Site Locally
Many hosts like Netlify and Vercel will build your site for you and then publish that build output to the web. But, some sites will require you to build locally and then run a deploy command or upload your build output.
You may also wish to build locally to preview your site, or to catch any potential errors and warnings in your own environment.
Run the command `npm run build` to build your Astro site.
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm run build
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm run build
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn run build
```
</Fragment>
</PackageManagerTabs>
By default, the build output will be placed at `dist/`. This location can be changed using the [`outDir` configuration option](/en/reference/configuration-reference/#outdir).
## Adding an Adapter for on-demand rendering
:::note
Before deploying your Astro site with [on-demand rendering](/en/guides/on-demand-rendering/) enabled, make sure you have:
- Installed the [appropriate adapter](/en/guides/on-demand-rendering/) to your project dependencies (either manually, or using the adapter's `astro add` command, e.g. `npx astro add netlify`).
- [Added the adapter](/en/reference/configuration-reference/#integrations) to your `astro.config.mjs` file's import and default export when installing manually. (The `astro add` command will take care of this step for you!)
:::
---
File: /src/content/docs/en/guides/deploy/kinsta.mdx
---
---
title: Deploy your Astro Site to Kinsta Application Hosting
description: How to deploy your Astro site to the web on Kinsta Application Hosting.
sidebar:
label: Kinsta
type: deploy
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
You can use [Kinsta Application Hosting](https://kinsta.com/application-hosting/) to host an Astro site on their cloud hosting.
## Configuring your Astro project
### Static hosting
:::tip[Looking for an example?]
Check out [the official Kinsta Application Hosting Starter project for Astro](https://github.com/kinsta/hello-world-astro)!
:::
To host your project on **Kinsta Application Hosting**, you need to:
- Include a `name` field in your `package.json`. (This can be anything, and will not affect your deployment.)
- Include a `build` script in your `package.json`. (Your Astro project should already include this.)
- Install the [`serve`](https://www.npmjs.com/package/serve) package and set the `start` script to `serve dist/`.
Here are the necessary lines in your `package.json` file:
```json title="package.json" {2,5,6} ins={12} "serv dist/"
{
"name": "anything", // This is required, but the value does not matter.
"scripts": {
"dev": "astro dev",
"start": "serve dist/",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"astro": "^2.2.0",
"serve": "^14.0.1"
},
}
```
### SSR
:::tip[Looking for an example?]
Check out [the official Kinsta Application Hosting Starter project for Astro SSR](https://github.com/kinsta/hello-world-astro-ssr)!
:::
To host your project on **Kinsta Application Hosting**, you need to:
- Include a `name` field in your `package.json`. (This can be anything, and will not affect your deployment.)
- Include a `build` script in your `package.json`. (Your Astro project should already include this.)
- Install the [`@astrojs/node`](https://www.npmjs.com/package/@astrojs/node) package and set the `start` script to `node ./dist/server/entry.mjs`.
- Set the `astro.config.mjs` to use `@astrojs/node` and to use `host: true`.
Here are the necessary lines in your `package.json` file:
```json title="package.json" {2,5,6} ins={12} "node ./dist/server/entry.mjs"
{
"name": "anything", // This is required, but the value does not matter.
"scripts": {
"dev": "astro dev",
"start": "node ./dist/server/entry.mjs",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"astro": "^2.2.0",
"@astrojs/node": "^5.1.1"
},
}
```
Here are the necessary lines in your `astro.config.mjs` file:
```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import node from "@astrojs/node";
export default defineConfig({
output: 'server',
adapter: node({
mode: "standalone"
}),
server: {
host: true
}
});
```
## How to deploy
Once your project's GitHub repository is connected, you can trigger manual deploys to Kinsta Application Hosting in the **MyKinsta Admin Panel**. You can also set up automatic deployments in your admin panel.
### Configuring a new Kinsta application
<Steps>
1. Go to the [My Kinsta](https://my.kinsta.com/) admin panel.
2. Go to the **Applications** tab.
3. Connect your GitHub repository.
4. Press the **Add service** > **Application** button.
5. Follow the wizard steps.
6. Your application is deployed.
</Steps>
---
File: /src/content/docs/en/guides/deploy/microsoft-azure.mdx
---
---
title: Deploy your Astro Site to Microsoft Azure
description: How to deploy your Astro site to the web using Microsoft Azure.
sidebar:
label: Microsoft Azure
type: deploy
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
[Azure](https://azure.microsoft.com/) is a cloud platform from Microsoft. You can deploy your Astro site with Microsoft Azure’s [Static Web Apps](https://aka.ms/staticwebapps) service.
This guide takes you through deploying your Astro site stored in GitHub using Visual Studio Code. Please see Microsoft guides for using an [Azure Pipelines Task](https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/reference/azure-static-web-app-v0?view=azure-pipelines) for other setups.
## Prerequisites
To follow this guide, you will need:
- An Azure account and a subscription key. You can create a [free Azure account here](https://azure.microsoft.com/free).
- Your app code pushed to [GitHub](https://github.com/).
- The [SWA Extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurestaticwebapps) in [Visual Studio Code](https://code.visualstudio.com/).
## How to deploy
<Steps>
1. Open your project in VS Code.
2. Open the Static Web Apps extension, sign in to Azure, and click the **+** button to create a new Static Web App. You will be prompted to designate which subscription key to use.
3. Follow the wizard started by the extension to give your app a name, choose a framework preset, and designate the app root (usually `/`) and built file location (use `/dist`). Astro is not listed in the built-in templates in Azure so you will need to select `custom`. The wizard will run and will create a [GitHub Action](https://github.com/features/actions) in the `.github` folder of your repo. (This folder will be automatically created if it does not already exist.)
</Steps>
The GitHub Action will deploy your app (you can see its progress in your repo’s Actions tab on GitHub). When successfully completed, you can view your app at the address shown in the SWA Extension’s progress window by clicking the **Browse Website** button (this will appear after the GitHub Action has run).
## Known Issues
The GitHub action yaml that is created for you assumes the use of node 14. This means the Astro build fails. To resolve this update your projects package.json file with this snippet.
```
"engines": {
"node": ">=18.0.0"
},
```
## Official Resources
- [Microsoft Azure Static Web Apps documentation](https://learn.microsoft.com/en-us/azure/static-web-apps/)
## Community Resources
- [Deploying an Astro Website to Azure Static Web Apps](https://www.blueboxes.co.uk/deploying-an-astro-website-to-azure-static-web-apps)
- [Deploying a Static Astro Site to Azure Static Web Apps using GitHub Actions](https://agramont.net/blog/create-static-site-astro-azure-ssg/#automate-deployment-with-github-actions)
- [Astro site deployment to Azure Static Web Apps with the CLI from GitHub Actions](https://www.eliostruyf.com/deploy-astro-azure-static-web-apps-github-cli/)
---
File: /src/content/docs/en/guides/deploy/netlify.mdx
---
---
title: Deploy your Astro Site to Netlify
description: How to deploy your Astro site to the web on Netlify.
sidebar:
label: Netlify
type: deploy
i18nReady: true
---
import ReadMore from '~/components/ReadMore.astro';
import { Steps } from '@astrojs/starlight/components';
[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.
```bash
npx astro add netlify
```
<ReadMore>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.</ReadMore>
## 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.
<Steps>
1. Click <kbd>Add a new site</kbd> in your [Netlify dashboard](https://app.netlify.com/)
2. Choose <kbd>Import an existing project</kbd>
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 <kbd>Deploy</kbd> 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.
</Steps>
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:
```toml
[build]
command = "npm run build"
publish = "dist"
```
<ReadMore>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</ReadMore>
### 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/).
<Steps>
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`.
</Steps>
<ReadMore>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)</ReadMore>
### Set a Node.js version
If you are using a legacy [build image](https://docs.netlify.com/configure-builds/get-started/#build-image-selection) (Xenial) on Netlify, make sure that your Node.js version is set. Astro requires `v18.17.1` or `v20.3.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
---
File: /src/content/docs/en/guides/deploy/render.mdx
---
---
title: Deploy your Astro Site to Render
description: How to deploy your Astro site to the web using Render.
sidebar:
label: Render
type: deploy
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
You can deploy your Astro project to [Render](https://render.com/), a service to build websites with free TLS certificates, a global CDN, DDoS protection, private networks, and auto deploys from Git.
## How to deploy
<Steps>
1. Create a [render.com account](https://dashboard.render.com/) and sign in
2. Click the **New +** button from your dashboard and select **Static Site**
3. Connect your [GitHub](https://github.com/) or [GitLab](https://about.gitlab.com/) repository or alternatively enter the public URL of a public repository
4. Give your website a name, select the branch and specify the build command and publish directory
- **Build Command:** `npm run build`
- **Publish Directory:** `dist`, for static sites; `dist/client` if you have any pages rendered on demand.
5. Click the **Create Static Site** button
</Steps>
---
File: /src/content/docs/en/guides/deploy/sst.mdx
---
---
title: Deploy your Astro Site to AWS with SST
description: How to deploy your Astro site to AWS with SST
sidebar:
label: SST
type: deploy
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
You can deploy an Astro site to AWS using [SST](https://sst.dev), an open-source framework for deploying modern full-stack applications with SSG and SSR support.
You can also use any additional SST components like cron jobs, Buckets, Queues, etc while maintaining type-safety.
## Quickstart
<Steps>
1. Create an astro project.
2. Run `npx sst@latest init`.
3. It should detect that you are using Astro and ask you to confirm.
4. Once you're ready for deployment you can run `npx sst deploy --stage production`.
</Steps>
You can also read [the full Astro on AWS with SST tutorial](https://sst.dev/docs/start/aws/astro) that will guide you through the steps.
### SST components
To use any [additional SST components](https://sst.dev/docs/), add them to `sst.config.ts`.
```ts {1} {5} title="sst.config.ts"
const bucket = new sst.aws.Bucket("MyBucket", {
access: "public",
});
new sst.aws.Astro("MyWeb", {
link: [bucket],
});
```
And then access them in your `.astro` file.
```astro
---
import { Resource } from "sst"
console.log(Resource.MyBucket.name)
---
```
Consult the [SST docs on linking resources](https://sst.dev/docs/linking) to learn more.
If you have any questions, you can [ask in the SST Discord](https://discord.gg/sst).
---
File: /src/content/docs/en/guides/deploy/stormkit.mdx
---
---
title: Deploy your Astro Site to Stormkit
description: Deploy your Astro site to Stormkit
sidebar:
label: Stormkit
type: deploy
i18nReady: true
---
import ReadMore from '~/components/ReadMore.astro';
import { Steps } from '@astrojs/starlight/components';
You can deploy your Astro project to [Stormkit](https://stormkit.io/), a deployment platform for static websites, single-page applications (SPAs), and serverless functions.
## How to deploy
<Steps>
1. [Log in to Stormkit](https://app.stormkit.io/auth).
2. Using the user interface, import your Astro project from one of the three supported Git providers (GitHub, GitLab, or Bitbucket).
3. Navigate to the project's production environment in Stormkit or create a new environment if needed.
4. Verify the build command in your [Stormkit configuration](https://stormkit.io/docs/deployments/configuration). By default, Stormkit CI will run `npm run build` but you can specify a custom build command on this page.
5. Click the "Deploy Now" button to deploy your site.
</Steps>
<ReadMore>Read more in the [Stormkit Documentation](https://stormkit.io/docs).</ReadMore>
---
File: /src/content/docs/en/guides/deploy/surge.mdx
---
---
title: Deploy your Astro Site to Surge
description: How to deploy your Astro site to the web using Surge
sidebar:
label: Surge
type: deploy
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
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
<Steps>
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.
```shell
npm run build
```
3. Deploy to Surge using the CLI.
```shell
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`.
</Steps>
---
File: /src/content/docs/en/guides/deploy/vercel.mdx
---
---
title: Deploy your Astro Site to Vercel
description: How to deploy your Astro site to the web on Vercel.
sidebar:
label: Vercel
type: deploy
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import ReadMore from '~/components/ReadMore.astro';
import { Steps } from '@astrojs/starlight/components';
You can use [Vercel](http://vercel.com/) to deploy an Astro site to their global edge network with zero configuration.
This guide includes instructions for deploying to Vercel through the website UI or Vercel's CLI.
## Project configuration
Your Astro project can be deployed to Vercel as a static site, or a server-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 Vercel.
### Adapter for on-demand rendering
Add [the Vercel adapter](/en/guides/integrations-guide/vercel/) to enable [on-demand rendering](/en/guides/on-demand-rendering/) in your Astro project 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.
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npx astro add vercel
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm astro add vercel
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn astro add vercel
```
</Fragment>
</PackageManagerTabs>
<ReadMore>See the [Vercel adapter guide](/en/guides/integrations-guide/vercel/) to install manually instead, or for more configuration options, such as deploying your project’s Astro middleware using Vercel Edge Functions.</ReadMore>
## How to deploy
You can deploy to Vercel through the website UI or using Vercel’s CLI (command line interface). The process is the same for both static and on-demand rendered Astro sites.
### Website UI deployment
<Steps>
1. Push your code to your online Git repository (GitHub, GitLab, BitBucket).
2. [Import your project](https://vercel.com/new) into Vercel.
3. Vercel will automatically detect Astro and configure the right settings.
4. Your application is deployed! (e.g. [astro.vercel.app](https://astro.vercel.app/))
</Steps>
After your project has been imported and deployed, all subsequent pushes to branches will generate [Preview Deployments](https://vercel.com/docs/concepts/deployments/preview-deployments), and all changes made to the Production Branch (commonly “main”) will result in a [Production Deployment](https://vercel.com/docs/concepts/deployments/environments#production).
<ReadMore>Learn more about Vercel’s [Git Integration](https://vercel.com/docs/concepts/git).</ReadMore>
### CLI deployment
<Steps>
1. Install the [Vercel CLI](https://vercel.com/cli) and run `vercel` to deploy.
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install -g vercel
vercel
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm add -g vercel
vercel
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn global add vercel
vercel
```
</Fragment>
</PackageManagerTabs>
2. Vercel will automatically detect Astro and configure the right settings.
3. When asked `Want to override the settings? [y/N]`, choose `N`.
4. Your application is deployed! (e.g. [astro.vercel.app](https://astro.vercel.app/))
</Steps>
### Project config with `vercel.json`
You can use `vercel.json` to override the default behavior of Vercel and to configure additional settings. For example, you may wish to attach headers to HTTP responses from your Deployments.
<ReadMore>Learn more about [Vercel’s project configuration](https://vercel.com/docs/project-configuration).</ReadMore>
---
File: /src/content/docs/en/guides/deploy/zeabur.mdx
---
---
title: Deploy your Astro Site to Zeabur
description: How to deploy your Astro site to the web on Zeabur.
sidebar:
label: Zeabur
type: deploy
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
[Zeabur](https://zeabur.com) offers hosting for full-stack web applications. Astro sites can be hosted as both SSR or static output.
This guide includes instructions for deploying to Zeabur through the website UI.
## Project Configuration
### Static Site
Astro outputs a static site by default. There is no need for any extra configuration to deploy a static Astro site to Zeabur.
### Adapter for SSR
To enable SSR in your Astro project and deploy on Zeabur:
<Steps>
1. Install [the `@zeabur/astro-adapter` adapter](https://www.npmjs.com/package/@zeabur/astro-adapter) to your project’s dependencies using your preferred package manager. If you’re using npm or aren’t sure, run this in the terminal:
```bash
npm install @zeabur/astro-adapter
```
2. Add two new lines to your `astro.config.mjs` project configuration file.
```js title="astro.config.mjs" ins={2, 5-6}
import { defineConfig } from 'astro/config';
import zeabur from '@zeabur/astro-adapter/serverless';
export default defineConfig({
output: 'server',
adapter: zeabur(),
});
```
</Steps>
## How to deploy
You can deploy your Astro site to Zeabur if the project is stored in GitHub.
<Steps>
1. Click <kbd>Create new project</kbd> in the [Zeabur dashboard](https://dash.zeabur.com).
2. Configure GitHub installation and import the repository.
3. Zeabur will automatically detect that your project is an Astro project and will build it using the `astro build` command.
4. Once the build is complete, you can bind a domain to your site and visit it.
</Steps>
After your project has been imported and deployed, all subsequent pushes to branches will generate new builds.
Learn more about Zeabur's [Deployment Guide](https://zeabur.com/docs/get-started/).
---
File: /src/content/docs/en/guides/deploy/zerops.mdx
---
---
title: Deploy your Astro Site to Zerops
description: How to deploy your Astro site to the web using Zerops.
sidebar:
label: Zerops
type: deploy
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
import { Steps } from '@astrojs/starlight/components';
[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:
```yaml
# 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:
<PackageManagerTabs>
<Fragment slot="npm">
```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
```
</Fragment>
<Fragment slot="pnpm">
```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
```
</Fragment>
<Fragment slot="yarn">
```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
```
</Fragment>
</PackageManagerTabs>
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.
```json title="package.json"
"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:
```yaml
# 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:
<PackageManagerTabs>
<Fragment slot="npm">
```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
```
</Fragment>
<Fragment slot="pnpm">
```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
```
</Fragment>
<Fragment slot="yarn">
```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
```
</Fragment>
</PackageManagerTabs>
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)
<Steps>
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 <token>
```
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
```
</Steps>
## 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)
---
File: /src/content/docs/en/guides/integrations-guide/alpinejs.mdx
---
---
type: integration
title: '@astrojs/alpinejs'
description: Learn how to use the @astrojs/alpinejs framework integration to extend component support in your Astro project.
sidebar:
label: Alpine.js
githubIntegrationURL: 'https://github.com/withastro/astro/tree/main/packages/integrations/alpinejs/'
category: renderer
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
This **[Astro integration][astro-integration]** 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:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npx astro add alpinejs
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm astro add alpinejs
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn astro add alpinejs
```
</Fragment>
</PackageManagerTabs>
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.
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm install @astrojs/alpinejs
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm add @astrojs/alpinejs
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn add @astrojs/alpinejs
```
</Fragment>
</PackageManagerTabs>
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:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm install alpinejs @types/alpinejs
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm add alpinejs @types/alpinejs
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn add alpinejs @types/alpinejs
```
</Fragment>
</PackageManagerTabs>
Then, apply the integration to your `astro.config.*` file using the `integrations` property:
```js ins="alpinejs()" title="astro.config.mjs" ins={2}
import { defineConfig } from 'astro/config';
import alpinejs from '@astrojs/alpinejs';
export default defineConfig({
// ...
integrations: [alpinejs()],
});
```
## Configuration Options
### `entrypoint`
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.
```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import alpine from '@astrojs/alpinejs';
export default defineConfig({
// ...
integrations: [alpine({ entrypoint: '/src/entrypoint' })],
});
```
```js title="src/entrypoint.ts"
import type { Alpine } from 'alpinejs'
import intersect from '@alpinejs/intersect'
export default (Alpine: Alpine) => {
Alpine.plugin(intersect)
}
```
## Usage
Once the integration is installed, you can use [Alpine.js](https://alpinejs.dev/) directives and syntax inside any Astro component. The Alpine.js script is automatically added and enabled on every page of your website so no client directives are needed. Add plugin scripts to the page `<head>`.
The following example adds [Alpine's Collapse plugin](https://alpinejs.dev/plugins/collapse) to expand and collapse paragraph text:
```astro title="src/pages/index.astro" ins={6} ins="x-collapse"
---
---
<html>
<head>
<!-- ... -->
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/[email protected]/dist/cdn.min.js"></script>
</head>
<body>
<!-- ... -->
<div x-data="{ expanded: false }">
<button @click="expanded = ! expanded">Toggle Content</button>
<p id="foo" x-show="expanded" x-collapse>
Lorem ipsum
</p>
</div>
</body>
</html>
```
## Intellisense for TypeScript
The `@astrojs/alpine` integration adds `Alpine` to the global window object. For IDE autocompletion, add the following to your `src/env.d.ts`:
```ts title="src/env.d.ts"
interface Window {
Alpine: import('alpinejs').Alpine;
}
```
## 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.
[astro-integration]: /en/guides/integrations-guide/
[astro-ui-frameworks]: /en/guides/framework-components/#using-framework-components
---
File: /src/content/docs/en/guides/integrations-guide/cloudflare.mdx
---
---
type: integration
title: '@astrojs/cloudflare'
description: Learn how to use the @astrojs/cloudflare adapter to deploy your Astro project.
sidebar:
label: Cloudflare
githubIntegrationURL: 'https://github.com/withastro/astro/tree/main/packages/integrations/cloudflare/'
category: adapter
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
import ReadMore from '~/components/ReadMore.astro';
import Since from '~/components/Since.astro';
import { Tabs, TabItem, Steps } from '@astrojs/starlight/components';
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/).
## 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.
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npx astro add cloudflare
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm astro add cloudflare
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn astro add cloudflare
```
</Fragment>
</PackageManagerTabs>
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
First, add the `@astrojs/cloudflare` adapter to your project's dependencies using your preferred package manager.
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm install @astrojs/cloudflare
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm add @astrojs/cloudflare
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn add @astrojs/cloudflare
```
</Fragment>
</PackageManagerTabs>
Then, add the adapter to your `astro.config.mjs` file:
```js title="astro.config.mjs" ins={2,5}
import { defineConfig } from 'astro/config';
import cloudflare from '@astrojs/cloudflare';
export default defineConfig({
adapter: cloudflare(),
});
```
## Options
The Cloudflare adapter accepts the following options:
### `cloudflareModules`
<p>
**Type:** `boolean`<br />
**Default:** `true`
</p>
Enables [imports of `.wasm`, `.bin`, and `.txt` modules](#cloudflare-module-imports).
This functionality is enabled by default. If you'd like to disable, set `cloudflareModules` to `false`.
### `imageService`
<p>
**Type:** `'passthrough' | 'cloudflare' | 'compile' | 'custom'`<br />
**Default:** `'compile'`
</p>
Determines which image service is used by the adapter. The adapter will default to `compile` mode when an incompatible image service is configured. Otherwise, it will use the globally configured image service:
* **`cloudflare`:** Uses the [Cloudflare Image Resizing](https://developers.cloudflare.com/images/image-resizing/) service.
* **`passthrough`:** Uses the existing [`noop`](/en/guides/images/#configure-no-op-passthrough-service) service.
* **`compile`:** Uses Astro's default service (sharp), but only on pre-rendered routes at build time. For pages rendered on-demand, all `astro:assets` features are disabled.
* **`custom`:** Always uses the image service configured in [Image Options](/en/reference/configuration-reference/#image-options). **This option will not check to see whether the configured image service works in Cloudflare's `workerd` runtime.**
```js title="astro.config.mjs" ins={6}
import { defineConfig } from "astro/config";
import cloudflare from '@astrojs/cloudflare';
export default defineConfig({
adapter: cloudflare({
imageService: 'cloudflare'
}),
})
```
### `platformProxy`
Determines whether and how the Cloudflare runtime is added to `astro dev`. It contains proxies to local `workerd` bindings and emulations of Cloudflare specific values, allowing the emulation of the runtime in the Node.js dev process. Read more about the [Cloudflare Runtime](#cloudflare-runtime).
:::note
Proxies provided by this are a best effort emulation of the real production. Although they are designed to be as close as possible to the real thing, there might be a slight differences and inconsistencies between the two.
:::
#### `platformProxy.enabled`
<p>
**Type:** `boolean`<br />
**Default:** `true`
</p>
Determines whether to enable the Cloudflare runtime in development mode.
#### `platformProxy.configPath`
<p>
**Type:** `string`<br />
**Default:** `undefined`
</p>
Defines the path to the Wrangler configuration file. If no value is set, it tracks `wrangler.toml`, `wrangler.json`, and `wrangler.jsonc` in the project root.
#### `platformProxy.environment`
<p>
**Type:** `string`<br />
**Default:** `undefined`
</p>
Sets the [Cloudflare environment](https://developers.cloudflare.com/workers/wrangler/environments/) to use. You must select an environment defined in the Wrangler configuration file, otherwise an error occurs.
#### `platformProxy.persist`
<p>
**Type:** `boolean | { path: string }`<br />
**Default:** `true`
</p>
Sets whether and where to save binding data locally to the file system.
- If set to `true`, binding data is stored in `.wrangler/state/v3/`. It is the same as the default setting for wrangler.
- If set to `false`, binding data is not stored in file system.
- If set to `{ path: string }`, binding data is stored in the specified path.
:::note
`wrangler`'s `--persist-to` option adds a sub directory called `v3` under the hood while the `@astrojs/cloudflare` `persist` property does not. For example, to reuse the same location as running `wrangler dev --persist-to ./my-directory`, you must specify: `persist: { path: "./my-directory/v3" }`.
:::
The following configuration shows an example of enabling the Cloudflare runtime when running the development server, as well as using a `wrangler.json` config file. It also specifies a custom location for persisting data to the filesystem:
```js
import cloudflare from '@astrojs/cloudflare';
import { defineConfig } from 'astro/config';
export default defineConfig({
adapter: cloudflare({
platformProxy: {
enabled: true,
configPath: 'wrangler.json',
persist: {
path: './.cache/wrangler/v3'
},
},
}),
});
```
### `routes.extend`
On Cloudflare Workers, this option is not applicable. Refer to [Routing on Cloudflare Workers](#routing-on-cloudflare-workers) for more information.
On Cloudflare Pages, this option allows you to add or exclude custom patterns (e.g. `/fonts/*`) to the generated `_routes.json` file that determines which routes are generated on-demand. This can be useful if you need to add route patterns which cannot be automatically generated, or exclude prerendered routes.
More information about the custom route patterns can be found in [Cloudflare's routing docs](https://developers.cloudflare.com/pages/functions/routing/#functions-invocation-routes). Any routes specified are not automatically deduplicated and will be appended to the existing routes as is.
#### `routes.extend.include`
<p>
**Type:** `{ pattern: string }[]`<br />
**Default:** `undefined`
</p>
Configures additional routes to be generated on demand by the Cloudflare adapter in the `routes.extend.include` array.
#### `routes.extend.exclude`
<p>
**Type:** `{ pattern: string }[]`<br />
**Default:** `undefined`
</p>
Configures routes to be excluded from on-demand rendering in the `routes.extend.exclude` array. These routes will be prerendered and served statically instead, and will not invoke the server function. Additionally you can use this option to serve any static asset (e.g. images, fonts, css, js, html, txt, json, etc.) files directly without routing the request through the server function.
```js title="astro.config.mjs"
export default defineConfig({
adapter: cloudflare({
routes: {
extend: {
include: [{ pattern: '/static' }], // Route a prerended page to the server function for on-demand rendering
exclude: [{ pattern: '/pagefind/*' }], // Use Starlight's pagefind search, which is generated statically at build time
}
},
}),
});
```
### `sessionKVBindingName`
<p>
**Type:** `string`<br />
**Default:** `SESSION`
<Since v="5.6.0" />
</p>
The `sessionKVBindingName` option allows you to specify the name of the KV binding used for session storage. By default, this is set to `SESSION`, but you can change it to match your own KV binding name. See [Sessions](#sessions) for more information.
```js title="astro.config.mjs" "MY_SESSION_BINDING"
export default defineConfig({
adapter: cloudflare({
sessionKVBindingName: 'MY_SESSION_BINDING',
}),
});
```
## Cloudflare runtime
### Usage
The Cloudflare runtime gives you access to environment variables and bindings to Cloudflare resources.
The Cloudflare runtime uses bindings found in the `wrangler.toml`/`wrangler.json` configuration file.
You can access the bindings from `Astro.locals.runtime`:
```astro title="src/pages/index.astro"
---
const { env } = Astro.locals.runtime;
---
```
You can access the runtime from API endpoints through `context.locals`:
```js title="src/pages/api/someFile.js"
export function GET(context) {
const runtime = context.locals.runtime;
return new Response('Some body');
}
```
See the [list of all supported bindings](https://developers.cloudflare.com/workers/wrangler/api/#supported-bindings) in the Cloudflare documentation.
### Environment variables and secrets
The Cloudflare runtime treats environment variables as a type of binding.
For example, you can define an [environment variable](https://developers.cloudflare.com/workers/configuration/environment-variables/#add-environment-variables-via-wrangler) in `wrangler.json` as follows:
```json title="wrangler.json"
{
"vars" : {
"MY_VARIABLE": "test"
}
}
```
Secrets are a special type of environment variable that allow you to attach encrypted text values to your Worker. They need to be defined differently to ensure they are not visible after you set them.
To define `secrets`, add them through the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/) rather than in your Wrangler config file.
```bash
npx wrangler secret put <KEY>
```
To set secrets for local development, you also need to add a `.dev.vars` file to the root of the Astro project:
```ini title=".dev.vars"
DB_PASSWORD=myPassword
```
You can then access environment variables, including secrets, from the `env` object available from `Astro.locals.runtime`:
```astro title="src/pages/index.astro"
---
const { env } = Astro.locals.runtime;
const myVariable = env.MY_VARIABLE;
const secret = env.DB_PASSWORD;
---
```
Cloudflare environment variables and secrets are compatible with the [`astro:env` API](/en/guides/environment-variables/#type-safe-environment-variables).
### Typing
`wrangler` provides a `types` command to generate TypeScript types for the bindings. This allows you to type locals without the need to manually type them. Refer to the [Cloudflare documentation](https://developers.cloudflare.com/workers/wrangler/commands/#types) for more information.
Every time you change your configuration files (e.g. `wrangler.toml`, `.dev.vars`) you need to run `wrangler types`.
:::note
You can create a pnpm script to run `wrangler types` automatically before other commands.
```json title="package.json"
{
"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"
}
}
```
:::
You can type the `runtime` object using `Runtime`:
```ts title="src/env.d.ts"
type Runtime = import('@astrojs/cloudflare').Runtime<Env>;
declare namespace App {
interface Locals extends Runtime {
otherLocals: {
test: string;
};
}
}
```
## Cloudflare Platform
### Headers
You can attach [custom headers](https://developers.cloudflare.com/pages/platform/headers/) to your responses by adding a `_headers` file in your Astro project's `public/` folder. This file will be copied to your build output directory.
This is available on Cloudflare Workers and Pages.
### 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
You can declare [custom redirects](https://developers.cloudflare.com/pages/platform/redirects/) to redirect requests to a different URL. To do so, add a `_redirects` file in your Astro project's `public/` folder. This file will be copied to your build output directory.
This is available on Cloudflare Workers and Pages.
### Routes
#### Routing on Cloudflare Workers
Routing for static assets is based on the file structure in the build directory (e.g. `./dist`). If no match is found, this will fall back to the Worker for on-demand rendering. Read more about [static asset routing with Cloudflare Workers](https://developers.cloudflare.com/workers/static-assets/routing/).
Unlike [Cloudflare Pages](#routing-on-cloudflare-pages), with Workers, you do not need a `_routes.json` file.
Currently, the Cloudflare adapter always generates this file. To work around this, create a `.assetsignore` file in your `public/` folder, and add the following lines to it:
```txt title="public/.assetsignore"
_worker.js
_routes.json
```
#### Routing on Cloudflare Pages
For Cloudflare Pages, [routing](https://developers.cloudflare.com/pages/platform/functions/routing/#functions-invocation-routes) uses a `_routes.json` file to determine which requests are routed to the server function and which are served as static assets. By default, a `_routes.json` file will be automatically generated for your project based on its files and configuration.
You can [specify additional routing patterns to follow](#routesextend) in your adapter config, or create your own custom `_routes.json` file to fully override the automatic generation.
Creating a custom `public/_routes.json` will override the automatic generation. See [Cloudflare's documentation on creating a custom `_routes.json`](https://developers.cloudflare.com/pages/platform/functions/routing/#create-a-_routesjson-file) for more details.
## Sessions
The Astro [Sessions API](/en/guides/sessions/) allows you to easily store user data between requests. This can be used for things like user data and preferences, shopping carts, and authentication credentials. Unlike cookie storage, there are no size limits on the data, and it can be restored on different devices.
Astro automatically configures [Workers KV](https://developers.cloudflare.com/kv/) for session storage when using the Cloudflare adapter. Before using sessions, you need to create a KV namespace to store the data and configure a KV binding in your Wrangler config file. By default, Astro expects the KV binding to be named `SESSION`, but you can choose a different name if you prefer by setting the [`sessionKVBindingName`](#sessionkvbindingname) option in the adapter config.
<Steps>
1. Create a KV namespace using the Wrangler CLI and make note of the ID of the new namespace:
```sh
npx wrangler kv namespace create "SESSION"
```
2. Declare the KV namespace in your Wrangler config, setting the namespace ID to the one returned by the previous command:
<Tabs>
<TabItem label="wrangler.json">
```json title="wrangler.json" "<KV_NAMESPACE_ID>"
{
"kv_namespaces": [
{
"binding": "SESSION",
"id": "<KV_NAMESPACE_ID>"
}
]
}
```
</TabItem>
<TabItem label="wrangler.toml">
```toml title="wrangler.toml" "<KV_NAMESPACE_ID>"
kv_namespaces = [
{ binding = "SESSION", id = "<KV_NAMESPACE_ID>" }
]
```
</TabItem>
</Tabs>
3. You can then use sessions in your server code:
```astro title="src/components/CartButton.astro" "Astro.session?.get('cart')"
---
export const prerender = false;
const cart = await Astro.session?.get('cart');
---
<a href="/checkout">🛒 {cart?.length ?? 0} items</a>
```
</Steps>
:::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.
```js title="pages/add/[a]/[b].js"
// Import the WebAssembly module
import mod from '../util/add.wasm';
// Instantiate first in order to use it
const addModule: any = new WebAssembly.Instance(mod);
export async function GET(context) {
const a = Number.parseInt(context.params.a);
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
Out of the box, Cloudflare does not support the Node.js runtime APIs. With some configuration, Cloudflare does support a subset of the Node.js runtime APIs. You can find supported Node.js runtime APIs in Cloudflare's [documentation](https://developers.cloudflare.com/workers/runtime-apis/nodejs).
To use these APIs, your page or endpoint must be server-side rendered (not pre-rendered) and must use the `import {} from 'node:*'` import syntax.
```js title="pages/api/endpoint.js"
export const prerender = false;
import { Buffer } from 'node:buffer';
```
You'll also need to modify the `vite` configuration in your Astro config to allow for the `node:*` import syntax:
```js title="astro.config.mjs" ins={6-10}
import {defineConfig} from "astro/config";
import cloudflare from '@astrojs/cloudflare';
export default defineConfig({
adapter: cloudflare({}),
vite: {
ssr: {
external: ['node:buffer'],
},
},
})
```
Additionally, you'll need to follow Cloudflare's documentation on how to enable support. For detailed guidance, please refer to the [Cloudflare documentation on enabling Node.js compatibility](https://developers.cloudflare.com/workers/runtime-apis/nodejs/).
:::note[Package Compatibility Implications]
If a project imports a package into the server that uses the Node.js runtime APIs, this can cause issues when deploying to Cloudflare. This issue arises with package that do not use the `node:*` import syntax. It is recommended that you contact the authors of the package to determine if the package supports the above import syntax. If the package does not support this, you may need to use a different package.
:::
## Preview with Wrangler
To use [`wrangler`](https://developers.cloudflare.com/workers/wrangler/) to run your application locally, update the preview script.
For Workers:
```json title="package.json"
"preview": "wrangler dev ./dist"
```
For Pages:
```json title="package.json"
"preview": "wrangler pages dev ./dist"
```
Developing with [`wrangler`](https://developers.cloudflare.com/workers/wrangler/) gives you access to [Cloudflare bindings](https://developers.cloudflare.com/pages/platform/functions/bindings), [environment variables](https://developers.cloudflare.com/pages/platform/functions/bindings/#environment-variables), and the [cf object](https://developers.cloudflare.com/workers/runtime-apis/request/#incomingrequestcfproperties). Getting hot reloading of the Astro dev server to work with Wrangler might require custom setup. See [community examples](https://github.com/withastro/roadmap/discussions/590).
### Meaningful error messages
Currently, errors during running your application in Wrangler are not very useful, due to the minification of your code. For better debugging, you can add `vite.build.minify = false` setting to your `astro.config.mjs`.
```js title="astro.config.mjs" ins={3-7}
export default defineConfig({
adapter: cloudflare(),
vite: {
build: {
minify: false,
},
},
});
```
[astro-integration]: /en/guides/integrations-guide/
---
File: /src/content/docs/en/guides/integrations-guide/db.mdx
---
---
type: integration
title: '@astrojs/db'
description: Learn how to use the @astrojs/db integration in your Astro project.
sidebar:
label: DB
githubIntegrationURL: 'https://github.com/withastro/astro/tree/main/packages/db/'
category: other
i18nReady: true
---
import { FileTree } from '@astrojs/starlight/components';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import ReadMore from '~/components/ReadMore.astro';
Astro DB is a fully-managed SQL database designed for the Astro ecosystem: develop locally in Astro and deploy to any [libSQL-compatible database](/en/guides/astro-db/).
With Astro DB you have a powerful, local, type-safe tool to query and model content as a relational database.
<ReadMore>See the [Astro DB guide](/en/guides/astro-db/) for full usage and examples.</ReadMore>
## Installation
Astro includes an `astro add` command to automate the setup of official integrations. If you prefer, you can [install integrations manually](#manual-installation) instead.
Run one of the following commands in a new terminal window.
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npx astro add db
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm astro add db
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn astro add db
```
</Fragment>
</PackageManagerTabs>
#### Manual Installation
If you prefer to set things up from scratch yourself, skip `astro add` and follow these instructions to install Astro DB yourself.
##### 1. Install the integration from npm via a package manager
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install @astrojs/db
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm add @astrojs/db
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn add @astrojs/db
```
</Fragment>
</PackageManagerTabs>
##### 2. Add the integration to `astro.config.mjs`
```js title="astro.config.mjs" ins={2,6}
import { defineConfig } from 'astro/config';
import db from '@astrojs/db';
export default defineConfig({
integrations: [
db()
]
});
```
##### 3. Configure your database
Create a `db/config.ts` file at the root of your project. This is a special file that Astro will automatically load and use to configure your database tables.
```ts
// db/config.ts
import { defineDb } from 'astro:db';
export default defineDb({
tables: {},
})
```
## Table configuration reference
### `columns`
Table columns are configured using the `columns` object:
```ts
import { defineTable, column, NOW } from 'astro:db';
const Comment = defineTable({
columns: {
id: column.number({ primaryKey: true }),
author: column.text(),
content: column.text({ optional: true }),
published: column.date({ default: NOW }),
},
});
```
Columns are configured using the `column` utility. `column` supports the following types:
- **`column.text(...)`** - store either plain or rich text content
- **`column.number(...)`** - store integer and floating point values
- **`column.boolean(...)`** - store true / false values
- **`column.date(...)`** - store `Date` objects, parsed as ISO strings for data storage
- **`column.json(...)`** - store arbitrary JSON blobs, parsed as stringified JSON for data storage
There are a few shared configuration values across all columns:
- `primaryKey` - Set a `number` or `text` column as the unique identifier.
- `optional` - Astro DB uses `NOT NULL` for all columns by default. Set `optional` to `true` to allow null values.
- `default` - Set the default value for newly inserted entries. This accepts either a static value or a string of `sql` for generated values like timestamps.
- `unique` - Mark a column as unique. This prevents duplicate values across entries in the table.
- `references` - Reference a related table by column. This establishes a foreign key constraint, meaning each column value must have a matching value in the referenced table.
### `indexes`
Table indexes are used to improve lookup speeds on a given column or combination of columns. The `indexes` property accepts an array of configuration objects specifying the columns to index:
```ts title="db/config.ts" {9-11}
import { defineTable, column } from 'astro:db';
const Comment = defineTable({
columns: {
authorId: column.number(),
published: column.date(),
body: column.text(),
},
indexes: [
{ on: ["authorId", "published"], unique: true },
]
});
```
This will generate a unique index on the `authorId` and `published` columns with the name `Comment_authorId_published_idx`.
The following configuration options are available for each index:
- `on`: `string | string[]` - A single column or array of column names to index.
- `unique`: `boolean` - Set to `true` to enforce unique values across the indexed columns.
- `name`: `string` (optional) - A custom name for the unique index. This will override Astro's generated name based on the table and column names being indexed (e.g. `Comment_authorId_published_idx`). Custom names are global, so ensure index names do not conflict between tables.
### `foreignKeys`
:::tip
`foreignKeys` is an advanced API for relating multiple table columns. If you only need to reference a single column, try using [the column `references` property.](#columns)
:::
Foreign keys are used to establish a relationship between two tables. The `foreignKeys` property accepts an array of configuration objects that may relate one or more columns between tables:
```ts title="db/config.ts" {12-20}
import { defineTable, column } from 'astro:db';
const Author = defineTable({
columns: {
firstName: column.text(),
lastName: column.text(),
},
});
const Comment = defineTable({
columns: {
authorFirstName: column.text(),
authorLastName: column.text(),
body: column.text(),
},
foreignKeys: [
{
columns: ["authorFirstName", "authorLastName"],
references: () => [Author.columns.firstName, Author.columns.lastName],
},
],
});
```
Each foreign key configuration object accepts the following properties:
- `columns`: `string[]` - An array of column names to relate to the referenced table.
- `references`: `() => Column[]` - A function that returns an array of columns from the referenced table.
## Astro DB CLI reference
Astro DB includes a set of CLI commands to interact with your local and libSQL-compatible database.
These commands are called automatically when using a GitHub CI action, and can be called manually using the `astro db` CLI.
### `astro db push`
**Flags:**
- `--force-reset` Reset all production data if a breaking schema change is required.
Safely push database configuration changes to your project database. This will check for any risk of data loss and guide you on any recommended migration steps. If a breaking schema change must be made, use the `--force-reset` flag to reset all production data.
### `astro db verify`
Check for any differences between your local and remote database configurations. This is automatically run by `astro db push`. `verify` will compare your local `db/config.ts` file with the remote database and warn if changes are detected.
### `astro db execute <file-path>`
**Flags:**
- `--remote` Run against your libSQL-compatible database. Omit to run against your development server.
Execute a `.ts` or `.js` file to read or write to your database. This accepts a file path as an argument, and supports usage of the `astro:db` module to write type-safe queries. Use the `--remote` flag to run against your libSQL-compatible database, or omit the flag to run against your development server. See how to [seed development data](/en/guides/astro-db/#seed-your-database-for-development) for an example file.
### `astro db shell --query <sql-string>`
**Flags:**
- `--query` Raw SQL query to execute.
- `--remote` Run against your libSQL-compatible database. Omit to run against your development server.
Execute a raw SQL query against your database. Use the `--remote` flag to run against your libSQL-compatible database, or omit the flag to run against your development server.
## Astro DB utility reference
### `isDbError()`
The `isDbError()` function checks if an error is a libSQL database exception. This may include a foreign key constraint error when using references, or missing fields when inserting data. You can combine `isDbError()` with a try / catch block to handle database errors in your application:
```ts title="src/pages/api/comment/[id].ts" "idDbError"
import { db, Comment, isDbError } from 'astro:db';
import type { APIRoute } from 'astro';
export const POST: APIRoute = (ctx) => {
try {
await db.insert(Comment).values({
id: ctx.params.id,
content: 'Hello, world!'
});
} catch (e) {
if (isDbError(e)) {
return new Response(`Cannot insert comment with id ${id}\n\n${e.message}`, { status: 400 });
}
return new Response('An unexpected error occurred', { status: 500 });
}
return new Response(null, { status: 201 });
};
```
---
File: /src/content/docs/en/guides/integrations-guide/deno.mdx
---
---
title: '@deno/astro-adapter'
description: The Deno Astro adapter
sidebar:
label: Deno
i18nReady: true
---
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.
---
File: /src/content/docs/en/guides/integrations-guide/index.mdx
---
---
title: Add Integrations
description: Learn how to add integrations to your Astro project.
sidebar:
label: Integrations overview
i18nReady: true
---
import IntegrationsNav from '~/components/IntegrationsNav.astro';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import { Steps } from '@astrojs/starlight/components';
**Astro integrations** add new functionality and behaviors for your project with only a few lines of code. You can use an official integration, [integrations built by the community](#finding-more-integrations) or even [build a custom integration yourself](#building-your-own-integration).
Integrations can…
- Unlock React, Vue, Svelte, Solid, and other popular UI frameworks with a [renderer](/en/guides/framework-components/).
- Enable on-demand rendering with an [SSR adapter](/en/guides/on-demand-rendering/).
- Integrate tools like MDX, and Partytown with a few lines of code.
- Add new features to your project, like automatic sitemap generation.
- Write custom code that hooks into the build process, dev server, and more.
:::tip[Integrations directory]
Browse or search the complete set of hundreds of official and community integrations in our [integrations directory](https://astro.build/integrations/). Find packages to add to your Astro project for authentication, analytics, performance, SEO, accessibility, UI, developer tools, and more.
:::
## Official Integrations
The following integrations are maintained by Astro.
<IntegrationsNav />
## Automatic Integration Setup
Astro includes an `astro add` command to automate the setup of official integrations. Several community plugins can also be added using this command. Please check each integration's own documentation to see whether `astro add` is supported, or whether you must [install manually](#manual-installation).
Run the `astro add` command using the package manager of your choice and our automatic integration wizard will update your configuration file and install any necessary dependencies.
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npx astro add react
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm astro add react
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn astro add react
```
</Fragment>
</PackageManagerTabs>
It's even possible to add multiple integrations at the same time!
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npx astro add react sitemap partytown
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm astro add react sitemap partytown
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn astro add react sitemap partytown
```
</Fragment>
</PackageManagerTabs>
:::note[Handling integration dependencies]
If you see any warnings like `Cannot find package '[package-name]'` after adding an integration, your package manager may not have installed [peer dependencies](https://nodejs.org/en/blog/npm/peer-dependencies/) for you. To install these missing packages, run the following command:
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install [package-name]
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm add [package-name]
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn add [package-name]
```
</Fragment>
</PackageManagerTabs>
:::
### Manual Installation
Astro integrations are always added through the `integrations` property in your `astro.config.mjs` file.
There are three common ways to import an integration into your Astro project:
1. [Install an npm package integration](#installing-an-npm-package).
2. Import your own integration from a local file inside your project.
3. Write your integration inline, directly in your config file.
```js
// astro.config.mjs
import { defineConfig } from 'astro/config';
import installedIntegration from '@astrojs/vue';
import localIntegration from './my-integration.js';
export default defineConfig({
integrations: [
// 1. Imported from an installed npm package
installedIntegration(),
// 2. Imported from a local JS file
localIntegration(),
// 3. An inline object
{name: 'namespace:id', hooks: { /* ... */ }},
]
});
```
Check out the [Integration API](/en/reference/integrations-reference/) reference to learn all of the different ways that you can write an integration.
#### Installing an NPM package
Install an NPM package integration using a package manager, and then update `astro.config.mjs` manually.
For example, to install the `@astrojs/sitemap` integration:
<Steps>
1. Install the integration to your project dependencies using your preferred package manager:
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install @astrojs/sitemap
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm add @astrojs/sitemap
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn add @astrojs/sitemap
```
</Fragment>
</PackageManagerTabs>
2. Import the integration to your `astro.config.mjs` file, and add it to your `integrations[]` array, along with any configuration options:
```js title="astro.config.mjs" ins={2} ins="sitemap()"
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
// ...
integrations: [sitemap()],
// ...
});
```
Note that different integrations may have different configuration settings. Read each integration's documentation, and apply any necessary config options to your chosen integration in `astro.config.mjs`.
</Steps>
### Custom Options
Integrations are almost always authored as factory functions that return the actual integration object. This lets you pass arguments and options to the factory function that customize the integration for your project.
```js
integrations: [
// Example: Customize your integration with function arguments
sitemap({filter: true})
]
```
### Toggle an Integration
Falsy integrations are ignored, so you can toggle integrations on & off without worrying about left-behind `undefined` and boolean values.
```js
integrations: [
// Example: Skip building a sitemap on Windows
process.platform !== 'win32' && sitemap()
]
```
## Upgrading Integrations
To upgrade all official integrations at once, run the `@astrojs/upgrade` command. This will upgrade both Astro and all official integrations to their latest versions.
### Automatic Upgrading
<PackageManagerTabs>
<Fragment slot="npm">
```shell
# Upgrade Astro and official integrations together to latest
npx @astrojs/upgrade
```
</Fragment>
<Fragment slot="pnpm">
```shell
# Upgrade Astro and official integrations together to latest
pnpm dlx @astrojs/upgrade
```
</Fragment>
<Fragment slot="yarn">
```shell
# Upgrade Astro and official integrations together to latest
yarn dlx @astrojs/upgrade
```
</Fragment>
</PackageManagerTabs>
### Manual Upgrading
To upgrade one or more integrations manually, use the appropriate command for your package manager.
<PackageManagerTabs>
<Fragment slot="npm">
```shell
# Example: upgrade React and Partytown integrations
npm install @astrojs/react@latest @astrojs/partytown@latest
```
</Fragment>
<Fragment slot="pnpm">
```shell
# Example: upgrade React and Partytown integrations
pnpm add @astrojs/react@latest @astrojs/partytown@latest
```
</Fragment>
<Fragment slot="yarn">
```shell
# Example: upgrade React and Partytown integrations
yarn add @astrojs/react@latest @astrojs/partytown@latest
```
</Fragment>
</PackageManagerTabs>
## Removing an Integration
<Steps>
1. To remove an integration, first uninstall the integration from your project.
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm uninstall @astrojs/react
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm remove @astrojs/react
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn remove @astrojs/react
```
</Fragment>
</PackageManagerTabs>
2. Next, remove the integration from your `astro.config.*` file:
```js title="astro.config.mjs" del={3,7}
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
export default defineConfig({
integrations: [
react()
]
});
```
</Steps>
## Finding More Integrations
You can find many integrations developed by the community in the [Astro Integrations Directory](https://astro.build/integrations/). Follow links there for detailed usage and configuration instructions.
## Building Your Own Integration
Astro's Integration API is inspired by Rollup and Vite, and designed to feel familiar to anyone who has ever written a Rollup or Vite plugin before.
Check out the [Integration API](/en/reference/integrations-reference/) reference to learn what integrations can do and how to write one yourself.
---
File: /src/content/docs/en/guides/integrations-guide/lit.mdx
---
---
title: 'Lit'
description: Using Lit to extend component support in your Astro project.
i18nReady: true
---
:::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:
```astro
<script>
import "../components/MyTabs";
</script>
<my-tabs title="These are my tabs">...</my-tabs>
```
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.
---
File: /src/content/docs/en/guides/integrations-guide/markdoc.mdx
---
---
type: integration
title: '@astrojs/markdoc'
description: Learn how to use the @astrojs/markdoc integration in your Astro project.
sidebar:
label: Markdoc
githubIntegrationURL: 'https://github.com/withastro/astro/tree/main/packages/integrations/markdoc/'
category: other
i18nReady: true
---
import { FileTree } from '@astrojs/starlight/components';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import { Steps } from '@astrojs/starlight/components';
import ReadMore from '~/components/ReadMore.astro';
This **[Astro integration][astro-integration]** 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][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.
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npx astro add markdoc
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm astro add markdoc
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn astro add markdoc
```
</Fragment>
</PackageManagerTabs>
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:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm install @astrojs/markdoc
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm add @astrojs/markdoc
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn add @astrojs/markdoc
```
</Fragment>
</PackageManagerTabs>
Then, apply the integration to your `astro.config.*` file using the `integrations` property:
```js ins="markdoc()" title="astro.config.mjs" ins={2}
import { defineConfig } from 'astro/config';
import markdoc from '@astrojs/markdoc';
export default defineConfig({
// ...
integrations: [markdoc()],
});
```
### 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:
```json title="markdoc.config.json"
[
{
"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:
<FileTree>
- src/
- content/
- docs/
- why-markdoc.mdoc
- quick-start.mdoc
</FileTree>
Then, query your collection using the [Content Collection APIs](/en/guides/content-collections/#querying-collections):
```astro title="src/pages/why-markdoc.astro"
---
import { getEntry, render } from 'astro:content';
const entry = await getEntry('docs', 'why-markdoc');
const { Content } = await render(entry);
---
<!--Access frontmatter properties with `data`-->
<h1>{entry.data.title}</h1>
<!--Render Markdoc contents with the Content component-->
<Content />
```
<ReadMore>See the [Astro Content Collection docs][astro-content-collections] for more information.</ReadMore>
## Pass Markdoc variables
You may need to pass [variables][markdoc-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:
```astro title="src/pages/why-markdoc.astro"
---
import { getEntry, render } from 'astro:content';
const entry = await getEntry('docs', 'why-markdoc');
const { Content } = await render(entry);
---
<!--Pass the `abTest` param as a variable-->
<Content abTestGroup={Astro.params.abTestGroup} />
```
Now, `abTestGroup` is available as a variable in `docs/why-markdoc.mdoc`:
```md title="src/content/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`:
```js title="markdoc.config.mjs"
import { defineMarkdocConfig } from '@astrojs/markdoc/config';
export default defineMarkdocConfig({
variables: {
environment: process.env.IS_PROD ? 'prod' : 'dev',
},
});
```
### Access frontmatter from your Markdoc content
To access frontmatter, you can pass the entry `data` property as a variable where you render your content:
```astro title="src/pages/why-markdoc.astro"
---
import { getEntry, render } from 'astro:content';
const entry = await getEntry('docs', 'why-markdoc');
const { Content } = await render(entry);
---
<Content frontmatter={entry.data} />
```
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][markdoc-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:
```js title="markdoc.config.mjs"
import { defineMarkdocConfig, component } from '@astrojs/markdoc/config';
export default defineMarkdocConfig({
tags: {
aside: {
render: component('./src/components/Aside.astro'),
attributes: {
// Markdoc requires type defs for each attribute.
// These should mirror the `Props` type of the component
// you are rendering.
// See Markdoc's documentation on defining attributes
// https://markdoc.dev/docs/attributes#defining-attributes
type: { type: 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:
```md
# 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:
```astro title="src/components/ClientAside.astro"
---
import Aside from './Aside';
---
<Aside {...Astro.props} client:load />
```
This Astro component can now be passed to the `render` prop for any [tag][markdoc-tags] or [node][markdoc-nodes] in your config:
```js title="markdoc.config.mjs"
import { defineMarkdocConfig, component } from '@astrojs/markdoc/config';
export default defineMarkdocConfig({
tags: {
aside: {
render: component('./src/components/ClientAside.astro'),
attributes: {
type: { type: String },
},
},
},
});
```
### Use Astro components from npm packages and TypeScript files
You may need to use Astro components exposed as named exports from TypeScript or JavaScript files. This is common when using npm packages and design systems.
You can pass the import name as the second argument to the `component()` function:
```js title="markdoc.config.mjs"
import { defineMarkdocConfig, component } from '@astrojs/markdoc/config';
export default defineMarkdocConfig({
tags: {
tabs: {
render: component('@astrojs/starlight/components', 'Tabs'),
},
},
});
```
This generates the following import statement internally:
```ts
import { Tabs } from '@astrojs/starlight/components';
```
## Markdoc Partials
The `{% partial /%}` tag allows you to render other `.mdoc` files inside your Markdoc content.
This is useful for reusing content across multiple documents, and allows you to have `.mdoc` content files that do not follow your collection schema.
:::tip
Use an underscore `_` prefix for partial files or directories. This excludes partials from content collection queries.
:::
This example shows a Markdoc partial for a footer to be used inside blog collection entries:
```md title="src/content/blog/_footer.mdoc"
Social links:
- [Twitter / X](https://twitter.com/astrodotbuild)
- [Discord](https://astro.build/chat)
- [GitHub](https://github.com/withastro/astro)
```
Use the `{% partial /%}` tag with to render the footer at the bottom of a blog post entry. Apply the `file` attribute with the path to the file, using either a relative path or an import alias:
```md title="src/content/blog/post.mdoc" ins='file="_footer.mdoc"'
# My Blog Post
{% partial file="./_footer.mdoc" /%}
```
## Syntax highlighting
`@astrojs/markdoc` provides [Shiki](https://shiki.style) and [Prism](https://github.com/PrismJS) extensions to highlight your code blocks.
### Shiki
Apply the `shiki()` extension to your Markdoc config using the `extends` property. You can optionally pass a shiki configuration object:
```js title="markdoc.config.mjs"
import { defineMarkdocConfig } from '@astrojs/markdoc/config';
import shiki from '@astrojs/markdoc/shiki';
export default defineMarkdocConfig({
extends: [
shiki({
// Choose from Shiki's built-in themes (or add your own)
// Default: 'github-dark'
// https://shiki.style/themes
theme: 'dracula',
// Enable word wrap to prevent horizontal scrolling
// Default: false
wrap: true,
// Pass custom languages
// Note: Shiki has countless langs built-in, including `.astro`!
// https://shiki.style/languages
langs: [],
}),
],
});
```
### Prism
Apply the `prism()` extension to your Markdoc config using the `extends` property.
```js title="markdoc.config.mjs" ins={5}
import { defineMarkdocConfig } from '@astrojs/markdoc/config';
import prism from '@astrojs/markdoc/prism';
export default defineMarkdocConfig({
extends: [prism()],
});
```
<ReadMore>To learn about configuring Prism stylesheets, [see our syntax highlighting guide](/en/guides/syntax-highlighting/#add-a-prism-stylesheet).</ReadMore>
## Custom Markdoc nodes / elements
You may want to render standard Markdown elements, such as paragraphs and bolded text, as Astro components. For this, you can configure a [Markdoc node][markdoc-nodes]. If a given node receives attributes, they will be available as component props.
This example renders blockquotes with a custom `Quote.astro` component:
```js title="markdoc.config.mjs"
import { defineMarkdocConfig, nodes, component } from '@astrojs/markdoc/config';
export default defineMarkdocConfig({
nodes: {
blockquote: {
...nodes.blockquote, // Apply Markdoc's defaults for other options
render: component('./src/components/Quote.astro'),
},
},
});
```
<ReadMore>See the [Markdoc nodes documentation](https://markdoc.dev/docs/nodes#built-in-nodes) to learn about all the built-in nodes and attributes.</ReadMore>
### Custom headings
`@astrojs/markdoc` automatically adds anchor links to your headings, and [generates a list of `headings` via the content collections API](/en/guides/content-collections/#rendering-body-content). To further customize how headings are rendered, you can apply an Astro component [as a Markdoc node][markdoc-nodes].
This example renders a `Heading.astro` component using the `render` property:
```js title="markdoc.config.mjs"
import { defineMarkdocConfig, nodes, component } from '@astrojs/markdoc/config';
export default defineMarkdocConfig({
nodes: {
heading: {
...nodes.heading, // Preserve default anchor link generation
render: component('./src/components/Heading.astro'),
},
},
});
```
All Markdown headings will render the `Heading.astro` component and pass the following `attributes` as component props:
* `level: number` The heading level 1 - 6
* `id: string` An `id` generated from the heading's text contents. This corresponds to the `slug` generated by the [content `render()` function](/en/guides/content-collections/#rendering-body-content).
For example, the heading `### Level 3 heading!` will pass `level: 3` and `id: 'level-3-heading'` as component props.
### Custom image components
Astro's `<Image />` component cannot be used directly in Markdoc. However, you can configure an Astro component to override the default image node every time the native `![]()` image syntax is used, or as a custom Markdoc tag to allow you to specify additional image attributes.
#### Override Markdoc's default image node
To override the default image node, you can configure an `.astro` component to be rendered in place of a standard `<img>`.
<Steps>
1. Build a custom `MarkdocImage.astro` component to pass the required `src` and `alt` properties from your image to the `<Image />` component:
```astro title="src/components/MarkdocImage.astro"
---
import { Image } from "astro:assets";
interface Props {
src: ImageMetadata;
alt: string;
}
const { src, alt } = Astro.props;
---
<Image src={src} alt={alt} />
```
2. The `<Image />` component requires a `width` and `height` for remote images which cannot be provided using the `![]()` syntax. To avoid errors when using remote images, update your component to render a standard HTML `<img>` tag when a remote URL `src` is found:
```astro title="src/components/MarkdocImage.astro" ins="| string" del={9} ins={10-12}
---
import { Image } from "astro:assets";
interface Props {
src: ImageMetadata | string;
alt: string;
}
const { src, alt } = Astro.props;
---
<Image src={src} alt={alt} />
{
typeof src === 'string' ? <img src={src} alt={alt} /> : <Image src={src} alt={alt} />
}
```
3. Configure Markdoc to override the default image node and render `MarkdocImage.astro`:
```js title="markdoc.config.mjs"
import { defineMarkdocConfig, nodes, component } from '@astrojs/markdoc/config';
export default defineMarkdocConfig({
nodes: {
image: {
...nodes.image, // Apply Markdoc's defaults for other options
render: component('./src/components/MarkdocImage.astro'),
},
},
});
```
4. The native image syntax in any `.mdoc` file will now use the `<Image />` component to optimize your local images. Remote images may still be used, but will not be rendered by Astro's `<Image />` component.
```md title="src/content/blog/post.mdoc"
<!-- Optimized by <Image /> -->
![A picture of a cat](/cat.jpg)
<!-- Unoptimized <img> -->
![A picture of a dog](https://example.com/dog.jpg)
```
</Steps>
#### Create a custom Markdoc image tag
A Markdoc `image` tag allows you to set additional attributes on your image that are not possible with the `![]()` syntax. For example, custom image tags allow you to use Astro's `<Image />` component for remote images that require a `width` and `height`.
The following steps will create a custom Markdoc image tag to display a `<figure>` element with a caption, using the Astro `<Image />` component to optimize the image.
<Steps>
1. Create a `MarkdocFigure.astro` component to receive the necessary props and render an image with a caption:
```astro title="src/components/MarkdocFigure.astro"
---
// src/components/MarkdocFigure.astro
import { Image } from "astro:assets";
interface Props {
src: ImageMetadata | string;
alt: string;
width: number;
height: number;
caption: string;
}
const { src, alt, width, height, caption } = Astro.props;
---
<figure>
<Image {src} {alt} {width} {height} />
{caption && <figcaption>{caption}</figcaption>}
</figure>
```
2. Configure your custom image tag to render your Astro component:
```ts title="markdoc.config.mjs"
import { component, defineMarkdocConfig, nodes } from '@astrojs/markdoc/config';
export default defineMarkdocConfig({
tags: {
image: {
attributes: {
width: {
type: String,
},
height: {
type: String,
},
caption: {
type: String,
},
...nodes.image.attributes
},
render: component('./src/components/MarkdocFigure.astro'),
},
},
});
```
3. Use the `image` tag in Markdoc files to display a figure with caption, providing all the necessary attributes for your component:
```md
{% image src="./astro-logo.png" alt="Astro Logo" width="100" height="100" caption="a caption!" /%}
```
</Steps>
## Advanced Markdoc configuration
The `markdoc.config.mjs|ts` file accepts [all Markdoc configuration options](https://markdoc.dev/docs/config), including [tags](https://markdoc.dev/docs/tags) and [functions](https://markdoc.dev/docs/functions).
You can pass these options from the default export in your `markdoc.config.mjs|ts` file:
```js title="markdoc.config.mjs"
import { defineMarkdocConfig } from '@astrojs/markdoc/config';
export default defineMarkdocConfig({
functions: {
getCountryEmoji: {
transform(parameters) {
const [country] = Object.values(parameters);
const countryToEmojiMap = {
japan: '🇯🇵',
spain: '🇪🇸',
france: '🇫🇷',
};
return countryToEmojiMap[country] ?? '🏳';
},
},
},
});
```
Now, you can call this function from any Markdoc content entry:
```md
¡Hola {% getCountryEmoji("spain") %}!
```
<ReadMore>[See the Markdoc documentation](https://markdoc.dev/docs/functions#creating-a-custom-function) for more on using variables or functions in your content.</ReadMore>
### Set the root HTML element
Markdoc wraps documents with an `<article>` tag by default. This can be changed from the `document` Markdoc node. This accepts an HTML element name or `null` if you prefer to remove the wrapper element:
```js title="markdoc.config.mjs"
import { defineMarkdocConfig, nodes } from '@astrojs/markdoc/config';
export default defineMarkdocConfig({
nodes: {
document: {
...nodes.document, // Apply defaults for other options
render: null, // default 'article'
},
},
});
```
## Integration config options
The Astro Markdoc integration handles configuring Markdoc options and capabilities that are not available through the `markdoc.config.js` file.
### `allowHTML`
Enables writing HTML markup alongside Markdoc tags and nodes.
By default, Markdoc will not recognize HTML markup as semantic content.
To achieve a more Markdown-like experience, where HTML elements can be included alongside your content, set `allowHTML:true` as a `markdoc` integration option. This will enable HTML parsing in Markdoc markup.
```js ins="allowHTML: true" title="astro.config.mjs" ins={6}
import { defineConfig } from 'astro/config';
import markdoc from '@astrojs/markdoc';
export default defineConfig({
// ...
integrations: [markdoc({ allowHTML: true })],
});
```
:::caution
When `allowHTML` is enabled, HTML markup inside Markdoc documents will be rendered as actual HTML elements (including `<script>`), making attack vectors like XSS possible. Ensure that any HTML markup comes from trusted sources.
:::
### `ignoreIndentation`
By default, any content that is indented by four spaces is treated as a code block. Unfortunately, this behavior makes it difficult to use arbitrary levels of indentation to improve the readability of documents with complex structure.
When using nested tags in Markdoc, it can be helpful to indent the content inside of tags so that the level of depth is clear. To support arbitrary indentation, we have to disable the indent-based code blocks and modify several other markdown-it parsing rules that account for indent-based code blocks. These changes can be applied by enabling the ignoreIndentation option.
```js "ignoreIndentation: true" title="astro.config.mjs" ins={6}
import { defineConfig } from 'astro/config';
import markdoc from '@astrojs/markdoc';
export default defineConfig({
// ...
integrations: [markdoc({ ignoreIndentation: true })],
});
```
```md
# Welcome to Markdoc with indented tags 👋
# Note: Can use either spaces or tabs for indentation
{% custom-tag %}
{% custom-tag %} ### Tags can be indented for better readability
{% another-custom-tag %}
This is easier to follow when there is a lot of nesting
{% /another-custom-tag %}
{% /custom-tag %}
{% /custom-tag %}
```
## Examples
* The [Astro Markdoc starter template](https://github.com/withastro/astro/tree/latest/examples/with-markdoc) shows how to use Markdoc files in your Astro project.
[astro-integration]: /en/guides/integrations-guide/
[astro-components]: /en/basics/astro-components/
[astro-content-collections]: /en/guides/content-collections/
[markdoc-tags]: https://markdoc.dev/docs/tags
[markdoc-nodes]: https://markdoc.dev/docs/nodes
[markdoc-variables]: https://markdoc.dev/docs/variables
---
File: /src/content/docs/en/guides/integrations-guide/mdx.mdx
---
---
type: integration
title: '@astrojs/mdx'
description: Learn how to use the @astrojs/mdx integration in your Astro project.
sidebar:
label: MDX
githubIntegrationURL: 'https://github.com/withastro/astro/tree/main/packages/integrations/mdx/'
category: other
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
import ReadMore from '~/components/ReadMore.astro'
import Since from '~/components/Since.astro'
This **[Astro integration][astro-integration]** enables the usage of [MDX](https://mdxjs.com/) components and allows you to create pages as `.mdx` files.
## Why MDX?
MDX allows you to use variables, JSX expressions and components within Markdown content in Astro. If you have existing content authored in MDX, this integration allows you to bring those files to your Astro project.
## 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.
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npx astro add mdx
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm astro add mdx
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn astro add mdx
```
</Fragment>
</PackageManagerTabs>
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/mdx` package:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm install @astrojs/mdx
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm add @astrojs/mdx
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn add @astrojs/mdx
```
</Fragment>
</PackageManagerTabs>
Then, apply the integration to your `astro.config.*` file using the `integrations` property:
```js title="astro.config.mjs" ins={2} ins="mdx()"
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
export default defineConfig({
// ...
integrations: [mdx()],
});
```
### Editor Integration
For editor support in [VS Code](https://code.visualstudio.com/), install the [official MDX extension](https://marketplace.visualstudio.com/items?itemName=unifiedjs.vscode-mdx).
For other editors, use the [MDX language server](https://github.com/mdx-js/mdx-analyzer/tree/main/packages/language-server).
## Usage
Visit the [MDX docs](https://mdxjs.com/docs/what-is-mdx/) to learn about using standard MDX features.
## MDX in Astro
Adding the MDX integration enhances your Markdown authoring with JSX variables, expressions and components.
It also adds extra features to standard MDX, including support for Markdown-style frontmatter in MDX. This allows you to use most of [Astro's built-in Markdown features](/en/guides/markdown-content/).
`.mdx` files must be written in [MDX syntax](https://mdxjs.com/docs/what-is-mdx/#mdx-syntax) rather than Astro’s HTML-like syntax.
### Using MDX with content collections
To include MDX files in a content collection, make sure that your [collection loader](/en/guides/content-collections/#defining-the-collection-loader) is configured to load content from `.mdx` files:
```js title="src/content.config.ts" ins="mdx"
import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';
const blog = defineCollection({
loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/blog" }),
schema: z.object({
title: z.string(),
description: z.string(),
pubDate: z.coerce.date(),
})
});
export const collections = { blog };
```
### Using Exported Variables in MDX
MDX supports using `export` statements to add variables to your MDX content or to export data to a component that imports it.
For example, you can export a `title` field from an MDX page or component to use as a heading with `{JSX expressions}`:
```mdx title="/src/blog/posts/post-1.mdx"
export const title = 'My first MDX post'
# {title}
```
Or you can use that exported `title` in your page using `import` and `import.meta.glob()` statements:
```astro title="src/pages/index.astro"
---
const matches = import.meta.glob('./posts/*.mdx', { eager: true });
const posts = Object.values(matches);
---
{posts.map(post => <p>{post.title}</p>)}
```
#### Exported Properties
The following properties are available to a `.astro` component when using an `import` statement or `import.meta.glob()`:
- **`file`** - The absolute file path (e.g. `/home/user/projects/.../file.mdx`).
- **`url`** - The URL of the page (e.g. `/en/guides/markdown-content`).
- **`frontmatter`** - Contains any data specified in the file’s YAML/TOML frontmatter.
- **`getHeadings()`** - An async function that returns an array of all headings (`<h1>` to `<h6>`) in the file with the type: `{ depth: number; slug: string; text: string }[]`. Each heading’s `slug` corresponds to the generated ID for a given heading and can be used for anchor links.
- **`<Content />`** - A component that returns the full, rendered contents of the file.
- **(any `export` value)** - MDX files can also export data with an `export` statement.
### Using Frontmatter Variables in MDX
The Astro MDX integration includes support for using frontmatter in MDX by default. Add frontmatter properties just as you would in Markdown files, and these variables are available to use in the template, and as named properties when importing the file somewhere else.
```mdx title="/src/blog/posts/post-1.mdx"
---
title: 'My first MDX post'
author: 'Houston'
---
# {frontmatter.title}
Written by: {frontmatter.author}
```
### Using Components in MDX
After installing the MDX integration, you can import and use both [Astro components](/en/basics/astro-components/) and [UI framework components](/en/guides/framework-components/#using-framework-components) in MDX (`.mdx`) files just as you would use them in any other Astro component.
Don't forget to include a `client:directive` on your UI framework components, if necessary!
See more examples of using import and export statements in the [MDX docs](https://mdxjs.com/docs/what-is-mdx/#esm).
```mdx title="src/blog/post-1.mdx" {4,9}
---
title: My first post
---
import ReactCounter from '../components/ReactCounter.jsx';
I just started my new Astro blog!
Here is my counter component, working in MDX:
<ReactCounter client:load />
```
#### Custom components with imported MDX
When rendering imported MDX content, [custom components](#assigning-custom-components-to-html-elements) can be passed via the `components` prop.
```astro title="src/pages/page.astro" "components={{...components, h1: Heading }}"
---
import { Content, components } from '../content.mdx';
import Heading from '../Heading.astro';
---
<!-- Creates a custom <h1> for the # syntax, _and_ applies any custom components defined in `content.mdx` -->
<Content components={{...components, h1: Heading }} />
```
:::note
Custom components defined and exported in an MDX file must be imported and then passed back to the `<Content />` component via the `components` property.
:::
#### Assigning Custom Components to HTML elements
With MDX, you can map Markdown syntax to custom components instead of their standard HTML elements. This allows you to write in standard Markdown syntax, but apply special component styling to selected elements.
Import your custom component into your `.mdx` file, then export a `components` object that maps the standard HTML element to your custom component:
```mdx title="src/blog/posts/post-1.mdx"
import Blockquote from '../components/Blockquote.astro';
export const components = {blockquote: Blockquote}
> This quote will be a custom Blockquote
```
```astro title="src/components/Blockquote.astro"
---
const props = Astro.props;
---
<blockquote {...props} class="bg-blue-50 p-4">
<span class="text-4xl text-blue-600 mb-2">“</span>
<slot /> <!-- Be sure to add a `<slot/>` for child content! -->
</blockquote>
```
Visit the [MDX website](https://mdxjs.com/table-of-components/) for a full list of HTML elements that can be overwritten as custom components.
## Configuration
Once the MDX integration is installed, no configuration is necessary to use `.mdx` files in your Astro project.
You can configure how your MDX is rendered with the following options:
* [Options inherited from Markdown config](#options-inherited-from-markdown-config)
* [`extendMarkdownConfig`](#extendmarkdownconfig)
* [`recmaPlugins`](#recmaplugins)
* [`optimize`](#optimize)
### Options inherited from Markdown config
All [`markdown` configuration options](/en/reference/configuration-reference/#markdown-options) can be configured separately in the MDX integration. This includes remark and rehype plugins, syntax highlighting, and more. Options will default to those in your Markdown config ([see the `extendMarkdownConfig` option](#extendmarkdownconfig) to modify this).
```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
import remarkToc from 'remark-toc';
import rehypePresetMinify from 'rehype-preset-minify';
export default defineConfig({
// ...
integrations: [
mdx({
syntaxHighlight: 'shiki',
shikiConfig: { theme: 'dracula' },
remarkPlugins: [remarkToc],
rehypePlugins: [rehypePresetMinify],
remarkRehype: { footnoteLabel: 'Footnotes' },
gfm: false,
}),
],
});
```
:::caution
MDX does not support passing remark and rehype plugins as a string. You should install, import, and apply the plugin function instead.
:::
<ReadMore>See the [Markdown Options reference](/en/reference/configuration-reference/#markdown-options) for a complete list of options.</ReadMore>
### `extendMarkdownConfig`
* **Type:** `boolean`
* **Default:** `true`
MDX will extend [your project's existing Markdown configuration](/en/reference/configuration-reference/#markdown-options) by default. To override individual options, you can specify their equivalent in your MDX configuration.
For example, say you need to disable GitHub-Flavored Markdown and apply a different set of remark plugins for MDX files. You can apply these options like so, with `extendMarkdownConfig` enabled by default:
```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
export default defineConfig({
// ...
markdown: {
syntaxHighlight: 'prism',
remarkPlugins: [remarkPlugin1],
gfm: true,
},
integrations: [
mdx({
// `syntaxHighlight` inherited from Markdown
// Markdown `remarkPlugins` ignored,
// only `remarkPlugin2` applied.
remarkPlugins: [remarkPlugin2],
// `gfm` overridden to `false`
gfm: false,
}),
],
});
```
You may also need to disable `markdown` config extension in MDX. For this, set `extendMarkdownConfig` to `false`:
```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
export default defineConfig({
// ...
markdown: {
remarkPlugins: [remarkPlugin1],
},
integrations: [
mdx({
// Markdown config now ignored
extendMarkdownConfig: false,
// No `remarkPlugins` applied
}),
],
});
```
### `recmaPlugins`
These are plugins that modify the output [estree](https://github.com/estree/estree) directly. This is useful for modifying or injecting JavaScript variables in your MDX files.
We suggest [using AST Explorer](https://astexplorer.net/) to play with estree outputs, and trying [`estree-util-visit`](https://unifiedjs.com/explore/package/estree-util-visit/) for searching across JavaScript nodes.
### `optimize`
* **Type:** `boolean | { ignoreElementNames?: string[] }`
This is an optional configuration setting to optimize the MDX output for faster builds and rendering via an internal rehype plugin. This may be useful if you have many MDX files and notice slow builds. However, this option may generate some unescaped HTML, so make sure your site's interactive parts still work correctly after enabling it.
This is disabled by default. To enable MDX optimization, add the following to your MDX integration configuration:
```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
export default defineConfig({
// ...
integrations: [
mdx({
optimize: true,
}),
],
});
```
#### `ignoreElementNames`
* **Type:** `string[]`
<p><Since pkg="@astrojs/mdx" v="3.0.0" /></p>
Previously known as `customComponentNames`.
An optional property of `optimize` to prevent the MDX optimizer from handling certain element names, like [custom components passed to imported MDX content via the components prop](/en/guides/integrations-guide/mdx/#custom-components-with-imported-mdx).
You will need to exclude these components from optimization as the optimizer eagerly converts content into a static string, which will break custom components that needs to be dynamically rendered.
For example, the intended MDX output of the following is `<Heading>...</Heading>` in place of every `"<h1>...</h1>"`:
```astro
---
import { Content, components } from '../content.mdx';
import Heading from '../Heading.astro';
---
<Content components={{ ...components, h1: Heading }} />
```
To configure optimization for this using the `ignoreElementNames` property, specify an array of HTML element names that should be treated as custom components:
```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
export default defineConfig({
// ...
integrations: [
mdx({
optimize: {
// Prevent the optimizer from handling `h1` elements
ignoreElementNames: ['h1'],
},
}),
],
});
```
Note that if your MDX file [configures custom components using `export const components = { ... }`](/en/guides/integrations-guide/mdx/#assigning-custom-components-to-html-elements), then you do not need to manually configure this option. The optimizer will automatically detect them.
## Examples
* The [Astro MDX starter template](https://github.com/withastro/astro/tree/latest/examples/with-mdx) shows how to use MDX files in your Astro project.
[astro-integration]: /en/guides/integrations-guide/
[astro-ui-frameworks]: /en/guides/framework-components/#using-framework-components
---
File: /src/content/docs/en/guides/integrations-guide/netlify.mdx
---
---
type: integration
title: '@astrojs/netlify'
description: Learn how to use the @astrojs/netlify adapter to deploy your Astro project.
sidebar:
label: Netlify
githubIntegrationURL: 'https://github.com/withastro/astro/tree/main/packages/integrations/netlify/'
category: adapter
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
import Since from '~/components/Since.astro';
This adapter allows Astro to deploy your [on-demand rendered routes and features](/en/guides/on-demand-rendering/) to [Netlify](https://www.netlify.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 only need this adapter if you are using additional Netlify services that require a server (e.g. [Netlify Image CDN](#netlify-image-cdn-support)). Otherwise, you do not need an adapter to deploy your static site.
Learn how to deploy your Astro site in our [Netlify deployment guide](/en/guides/deploy/netlify/).
## Why Astro Netlify
[Netlify](https://www.netlify.com/) is a deployment platform that allows you to host your site by connecting directly to your GitHub repository. This adapter enhances the Astro build process to prepare your project for deployment through Netlify.
## 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 Netlify adapter to enable on-demand rendering in your Astro project with the `astro add` command.
This will install `@astrojs/netlify` and make the appropriate changes to your `astro.config.mjs` file in one step.
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npx astro add netlify
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm astro add netlify
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn astro add netlify
```
</Fragment>
</PackageManagerTabs>
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
First, install the Netlify adapter to your project’s dependencies using your preferred package manager:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm install @astrojs/netlify
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm add @astrojs/netlify
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn add @astrojs/netlify
```
</Fragment>
</PackageManagerTabs>
Then, add the adapter to your `astro.config.*` file:
```js title="astro.config.mjs" ins={2, 6}
import { defineConfig } from 'astro/config';
import netlify from '@astrojs/netlify';
export default defineConfig({
// ...
adapter: netlify(),
});
```
## Usage
[Read the full deployment guide here.](/en/guides/deploy/netlify/)
Follow the instructions to [build your site locally](/en/guides/deploy/#building-your-site-locally). After building, you will have a `.netlify/` folder containing both [Netlify Functions](https://docs.netlify.com/functions/overview/) in the `.netlify/functions-internal/` folder and [Netlify Edge Functions](https://docs.netlify.com/edge-functions/overview/) in the`.netlify/edge-functions/` folder.
To deploy your site, install the [Netlify CLI](https://docs.netlify.com/cli/get-started/) and run:
```sh
netlify deploy
```
The [Netlify Blog post on Astro](https://www.netlify.com/blog/how-to-deploy-astro/) and the [Netlify Docs](https://docs.netlify.com/integrations/frameworks/astro/) provide more information on how to use this integration to deploy to Netlify.
### Running Astro middleware on Netlify Edge Functions
Any Astro middleware is applied to pre-rendered pages at build-time, and to on-demand-rendered pages at runtime.
To implement redirects, access control, or custom response headers for pre-rendered pages, run your middleware on Netlify Edge Functions by enabling the [`edgeMiddleware` option](/en/reference/adapter-reference/#edgemiddleware):
```js title="astro.config.mjs" ins={7}
import { defineConfig } from 'astro/config';
import netlify from '@astrojs/netlify';
export default defineConfig({
// ...
adapter: netlify({
edgeMiddleware: true,
}),
});
```
When `edgeMiddleware` is enabled, an edge function will execute your middleware code for all requests including static assets, prerendered pages, and on-demand rendered pages.
For on-demand rendered pages, the `context.locals` object is serialized using JSON and sent in a header for the serverless function, which performs the rendering. As a security measure, the serverless function will refuse to serve requests with a `403 Forbidden` response unless they come from the generated edge function.
### Accessing edge context from your site
Netlify Edge Functions provide a [context object](https://docs.netlify.com/edge-functions/api/#netlify-specific-context-object) that includes metadata about the request such as a user’s IP, geolocation data, and cookies.
This can be accessed through the `Astro.locals.netlify.context` object:
```astro
---
const {
geo: { city },
} = Astro.locals.netlify.context;
---
<h1>Hello there, friendly visitor from {city}!</h1>
```
If you're using TypeScript, you can get proper typings by updating `src/env.d.ts` to use `NetlifyLocals`:
```ts title="src/env.d.ts"
type NetlifyLocals = import('@astrojs/netlify').NetlifyLocals
declare namespace App {
interface Locals extends NetlifyLocals {
// ...
}
}
```
This is not available on prerendered pages.
### Netlify Image CDN support
This adapter by default uses the [Netlify Image CDN](https://docs.netlify.com/image-cdn/overview/) to transform images on-the-fly without impacting build times.
It's implemented using an [Astro Image Service](/en/reference/image-service-reference/) under the hood.
To opt out of Netlify's Image CDN remote image optimization, use the `imageCDN` option:
```js title="astro.config.mjs" ins={7}
import { defineConfig } from 'astro/config';
import netlify from '@astrojs/netlify';
export default defineConfig({
// ...
adapter: netlify({
imageCDN: false,
}),
});
```
If you are using images hosted on another domain, you must authorize the domain or URL patterns using the [`image.domains`](/en/reference/configuration-reference/#imagedomains) or [`image.remotePatterns`](/en/reference/configuration-reference/#imageremotepatterns) configuration options:
```js title="astro.config.mjs" ins={7-9}
import { defineConfig } from 'astro/config';
import netlify from '@astrojs/netlify';
export default defineConfig({
// ...
adapter: netlify(),
image: {
domains: ['example.com'],
},
});
```
For more information, see [the guide to authorizing remote images](/en/guides/images/#authorizing-remote-images). This is not required for images hosted on the same domain as your site.
### Static sites with the Netlify Adapter
For static sites (`output: 'static'`) hosted on Netlify, you usually don't need an adapter. However, some deployment features are only available through an adapter.
Static sites will need to install this adapter to use and configure Netlify's [image service](#netlify-image-cdn-support).
If you use `redirects` configuration in your Astro config, the Netlify adapter can be used to translate this to the proper `_redirects` format.
```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import netlify from '@astrojs/netlify';
export default defineConfig({
// ...
adapter: netlify(),
redirects: {
'/blog/old-post': '/blog/new-post',
},
});
```
Once you run `astro build` there will be a `dist/_redirects` file. Netlify will use that to properly route pages in production.
:::note
You can still include a `public/_redirects` file for manual redirects. Any redirects you specify in the redirects config are appended to the end of your own.
:::
### 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 [Netlify Blobs](https://docs.netlify.com/blobs/overview/) for session storage when using the Netlify adapter. If you would prefer to use a different session storage driver, you can specify it in your Astro config. See [the `session` configuration reference](/en/reference/configuration-reference/#sessiondriver) for more details.
### Caching Pages
On-demand rendered pages without any dynamic content can be cached to improve performance and lower resource usage.
Enabling the `cacheOnDemandPages` option in the adapter will cache all server-rendered pages for up to one year:
```ts title="astro.config.mjs" ins={4}
export default defineConfig({
// ...
adapter: netlify({
cacheOnDemandPages: true,
}),
});
```
This can be changed on a per-page basis by adding caching headers to your response:
```astro title="pages/index.astro"
---
import Layout from '../components/Layout.astro';
Astro.response.headers.set('CDN-Cache-Control', 'public, max-age=45, must-revalidate');
---
<Layout title="Astro on Netlify">
{new Date()}
</Layout>
```
With [fine-grained cache control](https://www.netlify.com/blog/swr-and-fine-grained-cache-control/), Netlify supports
standard caching headers like `CDN-Cache-Control` or `Vary`.
Refer to the docs to learn about implementing e.g. time to live (TTL) or stale while revalidate (SWR) caching: https://docs.netlify.com/platform/caching
### Including or excluding files from Netlify Functions
When deploying an Astro site with on-demand rendering to Netlify, the generated functions automatically trace and include server dependencies. However, you may need to customize which files are included in your Netlify Functions.
#### `includeFiles`
<p>
**Type:** `string[]`<br />
**Default:** `[]`<br />
<Since v="5.3.0" />
</p>
The `includeFiles` property allows you to explicitly specify additional files that should be bundled with your function. This is useful for files that aren't automatically detected as dependencies, such as:
- Data files loaded using `fs` operations
- Configuration files
- Template files
Provide an array of additional files to include with file paths relative to your project's [`root`](/en/reference/configuration-reference/#root). Absolute paths may not work as expected.
```js title="astro.config.mjs" ins={7}
import { defineConfig } from 'astro/config';
import netlify from '@astrojs/netlify';
export default defineConfig({
// ...
adapter: netlify({
includeFiles: ['./my-data.json'], // relative to `root`
}),
});
```
#### `excludeFiles`
<p>
**Type:** `string[]`<br />
**Default:** `[]`<br />
<Since v="5.3.0" />
</p>
You can use the `excludeFiles` property to prevent specific files from being bundled that would otherwise be included. This is helpful for:
- Reducing bundle size
- Excluding large binaries
- Preventing unwanted files from being deployed
Provide an array of specific files to exclude with file paths relative to your project's [`root`](/en/reference/configuration-reference/#root). Absolute paths may not work as expected.
```js title="astro.config.mjs" ins={7}
import { defineConfig } from 'astro/config';
import netlify from '@astrojs/netlify';
export default defineConfig({
// ...
adapter: netlify({
excludeFiles: ['./src/some_big_file.jpg'], // relative to `root`
}),
});
```
#### Using glob patterns
Both `includeFiles` and `excludeFiles` support [glob patterns](/en/guides/imports/#glob-patterns) for matching multiple files:
```js title="astro.config.mjs" ins={7, 10-11}
import { defineConfig } from 'astro/config';
import netlify from '@astrojs/netlify';
export default defineConfig({
adapter: netlify({
includeFiles: [
'./data/**/*.json'
],
excludeFiles: [
'./node_modules/package/**/*',
'./src/**/*.test.js'
]
}),
});
```
## Examples
* The [Astro Netlify Edge Starter](https://github.com/sarahetter/astro-netlify-edge-starter) provides an example and a guide in the README.
* [Browse Astro Netlify projects on GitHub](https://github.com/search?q=path%3A**%2Fastro.config.mjs+%40astrojs%2Fnetlify\&type=code) for more examples!
[astro-integration]: /en/guides/integrations-guide/
---
File: /src/content/docs/en/guides/integrations-guide/node.mdx
---
---
type: integration
title: '@astrojs/node'
description: Learn how to use the @astrojs/node adapter to deploy your Astro project.
sidebar:
label: Node
githubIntegrationURL: 'https://github.com/withastro/astro/tree/main/packages/integrations/node/'
category: adapter
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
This adapter allows Astro to deploy your [on-demand rendered routes and features](/en/guides/on-demand-rendering/) to Node targets, 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.
## Why Astro Node.js
[Node.js](https://nodejs.org/en/) is a JavaScript runtime for server-side code. @astrojs/node can be used either in standalone mode or as middleware for other http servers, such as [Express](https://expressjs.com/).
## 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 Node adapter to enable on-demand rendering in your Astro project with the `astro add` command.
This will install `@astrojs/node` and make the appropriate changes to your `astro.config.*` file in one step.
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npx astro add node
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm astro add node
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn astro add node
```
</Fragment>
</PackageManagerTabs>
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
First, add the Node adapter to your project’s dependencies using your preferred package manager.
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm install @astrojs/node
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm add @astrojs/node
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn add @astrojs/node
```
</Fragment>
</PackageManagerTabs>
Then, add the adapter to your `astro.config.*` file:
```js title="astro.config.mjs" ins={2,5-7}
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';
export default defineConfig({
adapter: node({
mode: 'standalone',
}),
});
```
## Configuration
@astrojs/node can be configured by passing options into the adapter function. The following options are available:
### `mode`
<p>
**Type:** `'middleware' | 'standalone'` <br />
</p>
Controls whether the adapter builds to `middleware` or `standalone` mode.
* `middleware` mode allows the built output to be used as middleware for another Node.js server, like Express.js or Fastify.
* `standalone` mode builds a server that automatically starts when the entry module is run. This allows you to more easily deploy your build to a host without needing additional code.
```js title="astro.config.mjs" {6}
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';
export default defineConfig({
adapter: node({
mode: 'middleware',
}),
});
```
## Usage
First, [performing a build](/en/guides/deploy/#building-your-site-locally). Depending on which `mode` selected (see above) follow the appropriate steps below:
### Middleware
The server entrypoint is built to `./dist/server/entry.mjs` by default. This module exports a `handler` function that can be used with any framework that supports the Node `request` and `response` objects.
For example, with Express:
```js title="run-server.mjs"
import express from 'express';
import { handler as ssrHandler } from './dist/server/entry.mjs';
const app = express();
// Change this based on your astro.config.mjs, `base` option.
// They should match. The default value is "/".
const base = '/';
app.use(base, express.static('dist/client/'));
app.use(ssrHandler);
app.listen(8080);
```
Or, with Fastify (>4):
```js title="run-server.mjs"
import Fastify from 'fastify';
import fastifyMiddie from '@fastify/middie';
import fastifyStatic from '@fastify/static';
import { fileURLToPath } from 'node:url';
import { handler as ssrHandler } from './dist/server/entry.mjs';
const app = Fastify({ logger: true });
await app
.register(fastifyStatic, {
root: fileURLToPath(new URL('./dist/client', import.meta.url)),
})
.register(fastifyMiddie);
app.use(ssrHandler);
app.listen({ port: 8080 });
```
Additionally, you can also pass in an object to be accessed with `Astro.locals` or in Astro middleware:
```js title="run-server.mjs"
import express from 'express';
import { handler as ssrHandler } from './dist/server/entry.mjs';
const app = express();
app.use(express.static('dist/client/'));
app.use((req, res, next) => {
const locals = {
title: 'New title',
};
ssrHandler(req, res, next, locals);
});
app.listen(8080);
```
Note that middleware mode does not do file serving. You'll need to configure your HTTP framework to do that for you. By default the client assets are written to `./dist/client/`.
### Standalone
In standalone mode a server starts when the server entrypoint is run. By default it is built to `./dist/server/entry.mjs`. You can run it with:
```sh
node ./dist/server/entry.mjs
```
For standalone mode the server handles file serving in addition to the page and API routes.
#### Custom host and port
You can override the host and port the standalone server runs on by passing them as environment variables at runtime:
```sh
HOST=0.0.0.0 PORT=4321 node ./dist/server/entry.mjs
```
#### HTTPS
By default the standalone server uses HTTP. This works well if you have a proxy server in front of it that does HTTPS. If you need the standalone server to run HTTPS itself you need to provide your SSL key and certificate.
You can pass the path to your key and certification via the environment variables `SERVER_CERT_PATH` and `SERVER_KEY_PATH`. This is how you might pass them in bash:
```bash
SERVER_KEY_PATH=./private/key.pem SERVER_CERT_PATH=./private/cert.pem node ./dist/server/entry.mjs
```
#### Runtime environment variables
If an `.env` file containing environment variables is present when the build process is run, these values will be hard-coded in the output, just as when generating a static website.
During the build, the runtime variables must be absent from the `.env` file, and you must provide Astro with every environment variable to expect at run-time: `VARIABLE_1=placeholder astro build`. This signals to Astro that the actual value will be available when the built application is run. The placeholder value will be ignored by the build process, and Astro will use the value provided at run-time.
In the case of multiple run-time variables, store them in a separate file (e.g. `.env.runtime`) from `.env`. Start the build with the following command:
```sh
export $(cat .env.runtime) && astro build
```
#### Assets
In standalone mode, assets in your `dist/client/` folder are served via the standalone server. You might be deploying these assets to a CDN, in which case the server will never actually be serving them. But in some cases, such as intranet sites, it's fine to serve static assets directly from the application server.
Assets in the `dist/client/_astro/` folder are the ones that Astro has built. These assets are all named with a hash and therefore can be given long cache headers. Internally the adapter adds this header for these assets:
```
Cache-Control: public, max-age=31536000, immutable
```
## 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 uses the local filesystem for session storage when using the Node adapter. If you would prefer to use a different session storage driver, you can specify it in your Astro config. See [the `session` configuration reference](/en/reference/configuration-reference/#sessiondriver) for more details.
---
File: /src/content/docs/en/guides/integrations-guide/partytown.mdx
---
---
type: integration
title: '@astrojs/partytown'
description: Learn how to use the @astrojs/partytown integration in your Astro project.
sidebar:
label: Partytown
githubIntegrationURL: 'https://github.com/withastro/astro/tree/main/packages/integrations/partytown/'
category: other
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
This **[Astro integration][astro-integration]** enables [Partytown](https://partytown.builder.io/) in your Astro project.
## Why Astro Partytown
Partytown is a lazy-loaded library to help relocate resource intensive scripts into a [web worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API), and off of the [main thread](https://developer.mozilla.org/en-US/docs/Glossary/Main_thread).
If you're using third-party scripts for things like analytics or ads, Partytown is a great way to make sure that they don't slow down your site.
The Astro Partytown integration installs Partytown for you and makes sure it's enabled on all of your pages.
## 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.
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npx astro add partytown
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm astro add partytown
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn astro add partytown
```
</Fragment>
</PackageManagerTabs>
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/partytown` package:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm install @astrojs/partytown
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm add @astrojs/partytown
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn add @astrojs/partytown
```
</Fragment>
</PackageManagerTabs>
Then, apply the integration to your `astro.config.*` file using the `integrations` property:
```js title="astro.config.mjs" ins={2} ins="partytown()"
import { defineConfig } from 'astro/config';
import partytown from '@astrojs/partytown';
export default defineConfig({
// ...
integrations: [partytown()],
});
```
## Usage
Partytown should be ready to go with zero config. If you have an existing 3rd party script on your site, try adding the `type="text/partytown"` attribute:
```html ins="type="text/partytown""
<script type="text/partytown" src="fancy-analytics.js"></script>
```
If you open the "Network" tab from [your browser's dev tools](https://developer.chrome.com/docs/devtools/open/), you should see the `partytown` proxy intercepting this request.
## Configuration
To configure this integration, pass a 'config' object to the `partytown()` function call in `astro.config.mjs`.
```js title="astro.config.mjs" {5-7}
export default defineConfig({
// ...
integrations: [
partytown({
config: {
// options go here
},
}),
],
});
```
This mirrors the [Partytown config object](https://partytown.builder.io/configuration).
### config.debug
Partytown ships with a `debug` mode; enable or disable it by passing `true` or `false` to `config.debug`. If [`debug` mode](https://partytown.builder.io/debugging) is enabled, it will output detailed logs to the browser console.
If this option isn't set, `debug` mode will be on by default in [dev](/en/reference/cli-reference/#astro-dev) or [preview](/en/reference/cli-reference/#astro-preview) mode.
```js title="astro.config.mjs" {6}
export default defineConfig({
// ...
integrations: [
partytown({
// Example: Disable debug mode.
config: { debug: false },
}),
],
});
```
### config.forward
Third-party scripts typically add variables to the `window` object so that you can communicate with them throughout your site. But when a script is loaded in a web-worker, it doesn't have access to that global `window` object.
To solve this, Partytown can "patch" variables to the global window object and forward them to the appropriate script.
You can specify which variables to forward with the `config.forward` option. [Read more in Partytown's documentation.](https://partytown.builder.io/forwarding-events)
```js title="astro.config.mjs" {7}
export default defineConfig({
// ...
integrations: [
partytown({
// Example: Add dataLayer.push as a forwarding-event.
config: {
forward: ['dataLayer.push'],
},
}),
],
});
```
## Examples
* [Browse projects with Astro Partytown on GitHub](https://github.com/search?q=%22%40astrojs%2Fpartytown%22+path%3A**%2Fpackage.json\&type=code) for more examples!
[astro-integration]: /en/guides/integrations-guide/
---
File: /src/content/docs/en/guides/integrations-guide/preact.mdx
---
---
type: integration
title: '@astrojs/preact'
description: Learn how to use the @astrojs/preact framework integration to extend component support in your Astro project.
sidebar:
label: Preact
githubIntegrationURL: 'https://github.com/withastro/astro/tree/main/packages/integrations/preact/'
category: renderer
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
import Since from '~/components/Since.astro';
This **[Astro integration][astro-integration]** enables rendering and client-side hydration for your [Preact](https://preactjs.com/) components.
## Why Preact?
Preact is a library that lets you build interactive UI components for the web. If you want to build interactive features on your site using JavaScript, you may prefer using its component format instead of using browser APIs directly.
Preact is also a great choice if you have previously used React. Preact provides the same API as React, but in a much smaller 3kB package. It even supports rendering many React components using the `compat` configuration option (see below).
**Want to learn more about Preact before using this integration?**\
Check out [“Learn Preact”](https://preactjs.com/tutorial), an interactive tutorial on their website.
## 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/preact`, run the following from your project directory and follow the prompts:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npx astro add preact
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm astro add preact
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn astro add preact
```
</Fragment>
</PackageManagerTabs>
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/preact` package:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm install @astrojs/preact
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm add @astrojs/preact
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn add @astrojs/preact
```
</Fragment>
</PackageManagerTabs>
Most package managers will install associated peer dependencies as well. If you see a `Cannot find package 'preact'` (or similar) warning when you start up Astro, you'll need to install Preact:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm install preact
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm add preact
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn add preact
```
</Fragment>
</PackageManagerTabs>
Then, apply the integration to your `astro.config.*` file using the `integrations` property:
```js title="astro.config.mjs" ins={2} ins="preact()"
import { defineConfig } from 'astro/config';
import preact from '@astrojs/preact';
export default defineConfig({
// ...
integrations: [preact()],
});
```
And add the following code to the `tsconfig.json` file.
```json title="tsconfig.json" ins={5-8}
{
"extends": "astro/tsconfigs/strict",
"include": [".astro/types.d.ts", "**/*"],
"exclude": ["dist"],
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "preact"
}
}
```
## Usage
To use your first Preact component in Astro, head to our [UI framework documentation][astro-ui-frameworks]. You'll explore:
* 📦 how framework components are loaded,
* 💧 client-side hydration options, and
* 🤝 opportunities to mix and nest frameworks together
Also check our [Astro Integration Documentation][astro-integration] for more on integrations.
## Configuration
The Astro Preact integration handles how Preact components are rendered and it has its own options. Change these in the `astro.config.mjs` file which is where your project's integration settings live.
For basic usage, you do not need to configure the Preact integration.
### compat
You can enable `preact/compat`, Preact’s compatibility layer for rendering React components without needing to install or ship React’s larger libraries to your users’ web browsers.
To do so, pass an object to the Preact integration and set `compat: true`.
```js title="astro.config.mjs" "compat: true"
import { defineConfig } from 'astro/config';
import preact from '@astrojs/preact';
export default defineConfig({
integrations: [preact({ compat: true })],
});
```
With the `compat` option enabled, the Preact integration will render React components as well as Preact components in your project and also allow you to import React components inside Preact components. Read more in [“Switching to Preact (from React)”](https://preactjs.com/guide/v10/switching-to-preact) on the Preact website.
When importing React component libraries, in order to swap out the `react` and `react-dom` dependencies as `preact/compat`, you can use [`overrides`](https://docs.npmjs.com/cli/v8/configuring-npm/package-json#overrides) to do so.
```json title="package.json"
{
"overrides": {
"react": "npm:@preact/compat@latest",
"react-dom": "npm:@preact/compat@latest"
}
}
```
Check out the [`pnpm` overrides](https://pnpm.io/package_json#pnpmoverrides) and [`yarn` resolutions](https://yarnpkg.com/configuration/manifest#resolutions) docs for their respective overrides features.
:::note
Currently, the `compat` option only works for React libraries that export code as ESM. If an error happens during build-time, try adding the library to `vite.ssr.noExternal: ['the-react-library']` in your `astro.config.mjs` file.
:::
### devtools
<p><Since pkg="@astrojs/preact" v="3.3.0" /></p>
You can enable [Preact devtools](https://preactjs.github.io/preact-devtools/) in development by passing an object with `devtools: true` to your `preact()` integration config:
```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import preact from '@astrojs/preact';
export default defineConfig({
// ...
integrations: [preact({ devtools: true })],
});
```
## Options
### Combining multiple JSX frameworks
When you are using multiple JSX frameworks (React, Preact, Solid) in the same project, Astro needs to determine which JSX framework-specific transformations should be used for each of your components. If you have only added one JSX framework integration to your project, no extra configuration is needed.
Use the `include` (required) and `exclude` (optional) configuration options to specify which files belong to which framework. Provide an array of files and/or folders to `include` for each framework you are using. Wildcards may be used to include multiple file paths.
We recommend placing common framework components in the same folder (e.g. `/components/react/` and `/components/solid/`) to make specifying your includes easier, but this is not required:
```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import preact from '@astrojs/preact';
import react from '@astrojs/react';
import svelte from '@astrojs/svelte';
import vue from '@astrojs/vue';
import solid from '@astrojs/solid-js';
export default defineConfig({
// Enable many frameworks to support all different kinds of components.
// No `include` is needed if you are only using a single JSX framework!
integrations: [
preact({
include: ['**/preact/*'],
}),
react({
include: ['**/react/*'],
}),
solid({
include: ['**/solid/*'],
}),
],
});
```
## Examples
* The [Astro Preact example](https://github.com/withastro/astro/tree/latest/examples/framework-preact) shows how to use an interactive Preact component in an Astro project.
* The [Astro Nanostores example](https://github.com/withastro/astro/tree/latest/examples/with-nanostores) shows how to share state between different components — and even different frameworks! — in an Astro project.
[astro-integration]: /en/guides/integrations-guide/
[astro-ui-frameworks]: /en/guides/framework-components/#using-framework-components
---
File: /src/content/docs/en/guides/integrations-guide/prefetch.mdx
---
---
title: '@astrojs/prefetch'
description: The deprecated prefetch integration.
sidebar:
label: Prefetch
i18nReady: true
---
:::caution[Removed]
`@astrojs/prefetch` has been replaced by the [built-in `prefetch` feature](/en/guides/prefetch/) introduced in Astro 3.5. See the [migration guide](/en/guides/prefetch/#migrating-from-astrojsprefetch) for instructions on updating an older project.
If you are still using this integration in a pre-v3.5 Astro project, you can read an archived copy of [the `@astrojs/prefetch` README](https://github.com/withastro/astro/blob/c47478bbf6b21973419f25234c68efb59466b368/packages%2Fintegrations%2Fprefetch%2FREADME.md) on GitHub.
:::
---
File: /src/content/docs/en/guides/integrations-guide/react.mdx
---
---
type: integration
title: '@astrojs/react'
description: Learn how to use the @astrojs/react framework integration to extend component support in your Astro project.
sidebar:
label: React
githubIntegrationURL: 'https://github.com/withastro/astro/tree/main/packages/integrations/react/'
category: renderer
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
This **[Astro integration][astro-integration]** enables rendering and client-side hydration for your [React](https://react.dev/) components.
## 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/react`, run the following from your project directory and follow the prompts:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npx astro add react
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm astro add react
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn astro add react
```
</Fragment>
</PackageManagerTabs>
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/react` package:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm install @astrojs/react
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm add @astrojs/react
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn add @astrojs/react
```
</Fragment>
</PackageManagerTabs>
Most package managers will install associated peer dependencies as well. If you see a `Cannot find package 'react'` (or similar) warning when you start up Astro, you'll need to install `react` and `react-dom` with its type definitions:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm install react react-dom @types/react @types/react-dom
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm add react react-dom @types/react @types/react-dom
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn add react react-dom @types/react @types/react-dom
```
</Fragment>
</PackageManagerTabs>
Then, apply the integration to your `astro.config.*` file using the `integrations` property:
```js ins="react()" ins={2} title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
export default defineConfig({
// ...
integrations: [react()],
});
```
And add the following code to the `tsconfig.json` file.
```json title="tsconfig.json" ins={5-8}
{
"extends": "astro/tsconfigs/strict",
"include": [".astro/types.d.ts", "**/*"],
"exclude": ["dist"],
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "react"
}
}
```
## Getting started
To use your first React component in Astro, head to our [UI framework documentation][astro-ui-frameworks]. You'll explore:
* 📦 how framework components are loaded,
* 💧 client-side hydration options, and
* 🤝 opportunities to mix and nest frameworks together
## Options
### Combining multiple JSX frameworks
When you are using multiple JSX frameworks (React, Preact, Solid) in the same project, Astro needs to determine which JSX framework-specific transformations should be used for each of your components. If you have only added one JSX framework integration to your project, no extra configuration is needed.
Use the `include` (required) and `exclude` (optional) configuration options to specify which files belong to which framework. Provide an array of files and/or folders to `include` for each framework you are using. Wildcards may be used to include multiple file paths.
We recommend placing common framework components in the same folder (e.g. `/components/react/` and `/components/solid/`) to make specifying your includes easier, but this is not required:
```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import preact from '@astrojs/preact';
import react from '@astrojs/react';
import svelte from '@astrojs/svelte';
import vue from '@astrojs/vue';
import solid from '@astrojs/solid-js';
export default defineConfig({
// Enable many frameworks to support all different kinds of components.
// No `include` is needed if you are only using a single JSX framework!
integrations: [
preact({
include: ['**/preact/*'],
}),
react({
include: ['**/react/*'],
}),
solid({
include: ['**/solid/*'],
}),
],
});
```
### Children parsing
Children passed into a React component from an Astro component are parsed as plain strings, not React nodes.
For example, the `<ReactComponent />` below will only receive a single child element:
```astro
---
import ReactComponent from './ReactComponent';
---
<ReactComponent>
<div>one</div>
<div>two</div>
</ReactComponent>
```
If you are using a library that *expects* more than one child element to be passed, for example so that it can slot certain elements in different places, you might find this to be a blocker.
You can set the experimental flag `experimentalReactChildren` to tell Astro to always pass children to React as React virtual DOM nodes. There is some runtime cost to this, but it can help with compatibility.
You can enable this option in the configuration for the React integration:
```js title="astro.config.mjs" ins={8}
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
export default defineConfig({
// ...
integrations: [
react({
experimentalReactChildren: true,
}),
],
});
```
### Disable streaming (experimental)
Astro streams the output of React components by default. However, you can disable this behavior by enabling the `experimentalDisableStreaming` option. This is particularly helpful for supporting libraries that don’t work well with streaming, like some CSS-in-JS solutions.
To disable streaming for all React components in your project, configure `@astrojs/react` with `experimentalDisableStreaming: true`:
```js title="astro.config.mjs" ins={8}
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
export default defineConfig({
// ...
integrations: [
react({
experimentalDisableStreaming: true,
}),
],
});
```
[astro-integration]: /en/guides/integrations-guide/
[astro-ui-frameworks]: /en/guides/framework-components/#using-framework-components
---
File: /src/content/docs/en/guides/integrations-guide/sitemap.mdx
---
---
type: integration
title: '@astrojs/sitemap'
description: Learn how to use the @astrojs/sitemap integration in your Astro project.
sidebar:
label: Sitemap
githubIntegrationURL: 'https://github.com/withastro/astro/tree/main/packages/integrations/sitemap/'
category: other
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
This **[Astro integration][astro-integration]** generates a sitemap based on your pages when you build your Astro project.
## Why Astro Sitemap
A Sitemap is an XML file that outlines all of the pages, videos, and files on your site. Search engines like Google read this file to crawl your site more efficiently. [See Google's own advice on sitemaps](https://developers.google.com/search/docs/advanced/sitemaps/overview) to learn more.
A sitemap file is recommended for large multi-page sites. If you don't use a sitemap, most search engines will still be able to list your site's pages, but a sitemap is a great way to ensure that your site is as search engine friendly as possible.
With Astro Sitemap, you don't have to worry about creating this XML file yourself: the Astro Sitemap integration will crawl your statically-generated routes and create the sitemap file, including [dynamic routes](/en/guides/routing/#dynamic-routes) like `[...slug]` or `src/pages/[lang]/[version]/info.astro` generated by `getStaticPaths()`.
This integration cannot generate sitemap entries for dynamic routes in [SSR mode](/en/guides/on-demand-rendering/).
## 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.
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npx astro add sitemap
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm astro add sitemap
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn astro add sitemap
```
</Fragment>
</PackageManagerTabs>
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/sitemap` package using your package manager.
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm install @astrojs/sitemap
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm add @astrojs/sitemap
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn add @astrojs/sitemap
```
</Fragment>
</PackageManagerTabs>
Then, apply the integration to your `astro.config.*` file using the `integrations` property:
```js ins={2} ins="sitemap()"
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
// ...
integrations: [sitemap()],
});
```
## Usage
`@astrojs/sitemap` needs to know your site’s deployed URL to generate a sitemap.
Add your site's URL as the [`site`](/en/reference/configuration-reference/#site) option in `astro.config.mjs`. This must begin with `http://` or `https://`.
```js title="astro.config.mjs" {5}
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
site: 'https://stargazers.club',
integrations: [sitemap()],
// ...
});
```
With the sitemap integration configured, `sitemap-index.xml` and `sitemap-0.xml` files will be added to your output directory when building your site.
`sitemap-index.xml` links to all the numbered sitemap files.
`sitemap-0.xml` lists the pages on your site.
For extremely large sites, there may also be additional numbered files like `sitemap-1.xml` and `sitemap-2.xml`.
<details>
<summary>Example of generated files for a two-page website</summary>
```xml title="sitemap-index.xml"
<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>https://stargazers.club/sitemap-0.xml</loc>
</sitemap>
</sitemapindex>
```
```xml title="sitemap-0.xml"
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
<url>
<loc>https://stargazers.club/</loc>
</url>
<url>
<loc>https://stargazers.club/second-page/</loc>
</url>
</urlset>
```
</details>
### Sitemap discovery
You can make it easier for crawlers to find your sitemap with links in your site's `<head>` and `robots.txt` file.
#### Sitemap link in `<head>`
Add a `<link rel="sitemap">` element to your site’s `<head>` pointing to the sitemap index file:
```html title="src/layouts/Layout.astro" ins={2}
<head>
<link rel="sitemap" href="/sitemap-index.xml" />
</head>
```
#### Sitemap link in `robots.txt`
If you have a `robots.txt` for your website, you can add the URL for the sitemap index to help crawlers:
```txt ins={5}
# public/robots.txt
User-agent: *
Allow: /
Sitemap: https://<YOUR SITE>/sitemap-index.xml
```
If you want to reuse the `site` value from `astro.config.mjs`, you can also generate `robots.txt` dynamically.
Instead of using a static file in the `public/` directory, create a `src/pages/robots.txt.ts` file and add the following code:
```ts title="src/pages/robots.txt.ts"
import type { APIRoute } from 'astro';
const getRobotsTxt = (sitemapURL: URL) => `
User-agent: *
Allow: /
Sitemap: ${sitemapURL.href}
`;
export const GET: APIRoute = ({ site }) => {
const sitemapURL = new URL('sitemap-index.xml', site);
return new Response(getRobotsTxt(sitemapURL));
};
```
## Configuration
To configure this integration, pass an object to the `sitemap()` function in `astro.config.mjs`.
```js title="astro.config.mjs" {6-8}
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
integrations: [
sitemap({
// configuration options
}),
],
});
```
### filter
All pages are included in your sitemap by default. By adding a custom `filter` function, you can filter included pages by URL.
```js title="astro.config.mjs" ins={8}
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
site: 'https://stargazers.club',
integrations: [
sitemap({
filter: (page) => page !== 'https://stargazers.club/secret-vip-lounge/',
}),
],
});
```
The function will be called for every page on your site. The `page` function parameter is the full URL of the page currently under consideration, including your `site` domain. Return `true` to include the page in your sitemap, and `false` to leave it out.
To filter multiple pages, add arguments with target URLs.
```js title="astro.config.mjs" {8-12}
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
site: 'https://stargazers.club',
integrations: [
sitemap({
filter: (page) =>
page !== 'https://stargazers.club/secret-vip-lounge-1/' &&
page !== 'https://stargazers.club/secret-vip-lounge-2/' &&
page !== 'https://stargazers.club/secret-vip-lounge-3/' &&
page !== 'https://stargazers.club/secret-vip-lounge-4/',
}),
],
});
```
### customPages
In some cases, a page might be part of your deployed site but not part of your Astro project. If you'd like to include a page in your sitemap that *isn't* created by Astro, you can use this option.
```js title="astro.config.mjs" ins={8}
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
site: 'https://stargazers.club',
integrations: [
sitemap({
customPages: ['https://stargazers.club/external-page', 'https://stargazers.club/external-page2'],
}),
],
});
```
### entryLimit
The maximum number entries per sitemap file. The default value is 45000. A sitemap index and multiple sitemaps are created if you have more entries. See this [explanation of splitting up a large sitemap](https://developers.google.com/search/docs/advanced/sitemaps/large-sitemaps).
```js title="astro.config.mjs" ins={8}
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
site: 'https://stargazers.club',
integrations: [
sitemap({
entryLimit: 10000,
}),
],
});
```
### changefreq, lastmod, and priority
These options correspond to the `<changefreq>`, `<lastmod>`, and `<priority>` tags in the [Sitemap XML specification.](https://www.sitemaps.org/protocol.html)
Note that `changefreq` and `priority` are ignored by Google.
:::note
Due to limitations of Astro's [Integration API](/en/reference/integrations-reference/), this integration can't analyze a given page's source code. This configuration option can set `changefreq`, `lastmod` and `priority` on a *site-wide* basis; see the next option **serialize** for how you can set these values on a per-page basis.
:::
```js title="astro.config.mjs" ins={8-10}
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
site: 'https://stargazers.club',
integrations: [
sitemap({
changefreq: 'weekly',
priority: 0.7,
lastmod: new Date('2022-02-24'),
}),
],
});
```
### serialize
A function called for each sitemap entry just before writing to a disk. This function can be asynchronous.
It receives as its parameter a `SitemapItem` object that can have these properties:
* `url` (absolute page URL). This is the only property that is guaranteed to be on `SitemapItem`.
* `changefreq`
* `lastmod` (ISO formatted date, `String` type)
* `priority`
* `links`.
This `links` property contains a `LinkItem` list of alternate pages including a parent page.
The `LinkItem` type has two fields: `url` (the fully-qualified URL for the version of this page for the specified language) and `lang` (a supported language code targeted by this version of the page).
The `serialize` function should return `SitemapItem`, touched or not.
The example below shows the ability to add sitemap specific properties individually.
```js title="astro.config.mjs" ins={8-18}
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
site: 'https://stargazers.club',
integrations: [
sitemap({
serialize(item) {
if (/exclude-from-sitemap/.test(item.url)) {
return undefined;
}
if (/your-special-page/.test(item.url)) {
item.changefreq = 'daily';
item.lastmod = new Date();
item.priority = 0.9;
}
return item;
},
}),
],
});
```
### i18n
To localize a sitemap, pass an object to this `i18n` option.
This object has two required properties:
* `defaultLocale`: `String`. Its value must exist as one of `locales` keys.
* `locales`: `Record<String, String>`, key/value - pairs. The key is used to look for a locale part in a page path. The value is a language attribute, only English alphabet and hyphen allowed.
[Read more about language attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang).
[Read more about localization](https://developers.google.com/search/docs/advanced/crawling/localized-versions#all-method-guidelines).
```js title="astro.config.mjs" ins={8-15}
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
site: 'https://stargazers.club',
integrations: [
sitemap({
i18n: {
defaultLocale: 'en', // All urls that don't contain `es` or `fr` after `https://stargazers.club/` will be treated as default locale, i.e. `en`
locales: {
en: 'en-US', // The `defaultLocale` value must present in `locales` keys
es: 'es-ES',
fr: 'fr-CA',
},
},
}),
],
});
```
The resulting sitemap looks like this:
```xml title="sitemap-0.xml"
...
<url>
<loc>https://stargazers.club/</loc>
<xhtml:link rel="alternate" hreflang="en-US" href="https://stargazers.club/"/>
<xhtml:link rel="alternate" hreflang="es-ES" href="https://stargazers.club/es/"/>
<xhtml:link rel="alternate" hreflang="fr-CA" href="https://stargazers.club/fr/"/>
</url>
<url>
<loc>https://stargazers.club/es/</loc>
<xhtml:link rel="alternate" hreflang="en-US" href="https://stargazers.club/"/>
<xhtml:link rel="alternate" hreflang="es-ES" href="https://stargazers.club/es/"/>
<xhtml:link rel="alternate" hreflang="fr-CA" href="https://stargazers.club/fr/"/>
</url>
<url>
<loc>https://stargazers.club/fr/</loc>
<xhtml:link rel="alternate" hreflang="en-US" href="https://stargazers.club/"/>
<xhtml:link rel="alternate" hreflang="es-ES" href="https://stargazers.club/es/"/>
<xhtml:link rel="alternate" hreflang="fr-CA" href="https://stargazers.club/fr/"/>
</url>
<url>
<loc>https://stargazers.club/es/second-page/</loc>
<xhtml:link rel="alternate" hreflang="es-ES" href="https://stargazers.club/es/second-page/"/>
<xhtml:link rel="alternate" hreflang="fr-CA" href="https://stargazers.club/fr/second-page/"/>
<xhtml:link rel="alternate" hreflang="en-US" href="https://stargazers.club/second-page/"/>
</url>
...
```
### xslURL
The URL of an XSL stylesheet to style and prettify your sitemap.
The value set can be either a path relative to your configured `site` URL for a local stylesheet, or can be an absolute URL link to an external stylesheet.
```js title="astro.config.mjs" ins={8}
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
site: 'https://example.com',
integrations: [
sitemap({
xslURL: '/sitemap.xsl'
}),
],
});
```
## Examples
* The official Astro website uses Astro Sitemap to generate [its sitemap](https://astro.build/sitemap-index.xml).
* [Browse projects with Astro Sitemap on GitHub](https://github.com/search?q=%22%40astrojs%2Fsitemap%22+path%3Apackage.json\&type=Code) for more examples!
[astro-integration]: /en/guides/integrations-guide/
---
File: /src/content/docs/en/guides/integrations-guide/solid-js.mdx
---
---
type: integration
title: '@astrojs/solid-js'
description: Learn how to use the @astrojs/solid-js framework integration to extend component support in your Astro project.
sidebar:
label: SolidJS
githubIntegrationURL: 'https://github.com/withastro/astro/tree/main/packages/integrations/solid/'
category: renderer
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import Since from '~/components/Since.astro';
This **[Astro integration][astro-integration]** enables rendering and client-side hydration for your [SolidJS](https://www.solidjs.com/) components.
## 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/solid-js`, run the following from your project directory and follow the prompts:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npx astro add solid
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm astro add solid
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn astro add solid
```
</Fragment>
</PackageManagerTabs>
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/solid-js` package:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm install @astrojs/solid-js
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm add @astrojs/solid-js
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn add @astrojs/solid-js
```
</Fragment>
</PackageManagerTabs>
Most package managers will install associated peer dependencies as well. If you see a `Cannot find package 'solid-js'` (or similar) warning when you start up Astro, you'll need to install SolidJS:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm install solid-js
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm add solid-js
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn add solid-js
```
</Fragment>
</PackageManagerTabs>
Then, apply the integration to your `astro.config.*` file using the `integrations` property:
```js title="astro.config.mjs" ins={2} ins="solidJs()"
import { defineConfig } from 'astro/config';
import solidJs from '@astrojs/solid-js';
export default defineConfig({
// ...
integrations: [solidJs()],
});
```
And add the following code to the `tsconfig.json` file.
```json title="tsconfig.json" ins={5-8}
{
"extends": "astro/tsconfigs/strict",
"include": [".astro/types.d.ts", "**/*"],
"exclude": ["dist"],
"compilerOptions": {
"jsx": "preserve",
"jsxImportSource": "solid-js"
}
}
```
## Getting started
To use your first SolidJS component in Astro, head to our [UI framework documentation][astro-ui-frameworks]. You'll explore:
* 📦 how framework components are loaded,
* 💧 client-side hydration options, and
* 🤝 opportunities to mix and nest frameworks together
## Configuration
### devtools
<p><Since pkg="@astrojs/solid-js" v="4.2.0" /></p>
You can enable [Solid DevTools](https://github.com/thetarnav/solid-devtools) in development by passing an object with `devtools: true` to your `solid()` integration config and adding `solid-devtools` to your project dependencies:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm install solid-devtools
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm add solid-devtools
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn add solid-devtools
```
</Fragment>
</PackageManagerTabs>
```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import solid from '@astrojs/solid-js';
export default defineConfig({
// ...
integrations: [solid({ devtools: true })],
});
```
## Options
### Combining multiple JSX frameworks
When you are using multiple JSX frameworks (React, Preact, Solid) in the same project, Astro needs to determine which JSX framework-specific transformations should be used for each of your components. If you have only added one JSX framework integration to your project, no extra configuration is needed.
Use the `include` (required) and `exclude` (optional) configuration options to specify which files belong to which framework. Provide an array of files and/or folders to `include` for each framework you are using. Wildcards may be used to include multiple file paths.
We recommend placing common framework components in the same folder (e.g. `/components/react/` and `/components/solid/`) to make specifying your includes easier, but this is not required:
```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import preact from '@astrojs/preact';
import react from '@astrojs/react';
import svelte from '@astrojs/svelte';
import vue from '@astrojs/vue';
import solid from '@astrojs/solid-js';
export default defineConfig({
// Enable many frameworks to support all different kinds of components.
// No `include` is needed if you are only using a single JSX framework!
integrations: [
preact({
include: ['**/preact/*'],
}),
react({
include: ['**/react/*'],
}),
solid({
include: ['**/solid/*', '**/node_modules/@suid/material/**'],
}),
],
});
```
## Usage
Use a SolidJS component as you would any [UI framework component](/en/guides/framework-components/).
### Suspense Boundaries
In order to support Solid Resources and Lazy Components without excessive configuration, server-only and hydrating components are automatically wrapped in top-level Suspense boundaries and rendered on the server using the [`renderToStringAsync`][solid-render-to-string-async] function. Therefore, you do not need to add a top-level Suspense boundary around async components.
For example, you can use Solid's [`createResource`][solid-create-resource] to fetch async remote data on the server. The remote data will be included in the initial server-rendered HTML from Astro:
```tsx
// CharacterName.tsx
function CharacterName() {
const [name] = createResource(() =>
fetch('https://swapi.dev/api/people/1')
.then((result) => result.json())
.then((data) => data.name)
);
return (
<>
<h2>Name:</h2>
{/* Luke Skywalker */}
<div>{name()}</div>
</>
);
}
```
Similarly, Solid's [Lazy Components][solid-lazy-components] will also be resolved and their HTML will be included in the initial server-rendered page.
Non-hydrating [`client:only` components][astro-client-only] are not automatically wrapped in Suspense boundaries.
Feel free to add additional Suspense boundaries according to your preference.
[astro-integration]: /en/guides/integrations-guide/
[astro-ui-frameworks]: /en/guides/framework-components/#using-framework-components
[astro-client-only]: /en/reference/directives-reference/#clientonly
[solid-render-to-string-async]: https://www.solidjs.com/docs/latest/api#rendertostringasync
[solid-create-resource]: https://www.solidjs.com/docs/latest/api#createresource
[solid-lazy-components]: https://www.solidjs.com/docs/latest/api#lazy
---
File: /src/content/docs/en/guides/integrations-guide/svelte.mdx
---
---
type: integration
title: '@astrojs/svelte'
description: Learn how to use the @astrojs/svelte framework integration to extend component support in your Astro project.
sidebar:
label: Svelte
githubIntegrationURL: 'https://github.com/withastro/astro/tree/main/packages/integrations/svelte/'
category: renderer
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import Since from '~/components/Since.astro';
This **[Astro integration][astro-integration]** enables rendering and client-side hydration for your [Svelte](https://svelte.dev/) 5 components. For Svelte 3 and 4 support, install `@astrojs/svelte@5` instead.
## 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/svelte`, run the following from your project directory and follow the prompts:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npx astro add svelte
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm astro add svelte
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn astro add svelte
```
</Fragment>
</PackageManagerTabs>
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/svelte` package:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm install @astrojs/svelte
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm add @astrojs/svelte
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn add @astrojs/svelte
```
</Fragment>
</PackageManagerTabs>
Most package managers will install associated peer dependencies as well. If you see a `Cannot find package 'svelte'` (or similar) warning when you start up Astro, you'll need to install Svelte and TypeScript:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm install svelte typescript
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm add svelte typescript
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn add svelte typescript
```
</Fragment>
</PackageManagerTabs>
Then, apply the integration to your `astro.config.*` file using the `integrations` property:
```js title="astro.config.mjs" ins={2} ins="svelte()"
import { defineConfig } from 'astro/config';
import svelte from '@astrojs/svelte';
export default defineConfig({
// ...
integrations: [svelte()],
});
```
And create a new file called `svelte.config.js` in your project root directory and add the following code:
```js title="svelte.config.js"
import { vitePreprocess } from '@astrojs/svelte';
export default {
preprocess: vitePreprocess(),
}
```
## Getting started
To use your first Svelte component in Astro, head to our [UI framework documentation][astro-ui-frameworks]. You'll explore:
* 📦 how framework components are loaded,
* 💧 client-side hydration options, and
* 🤝 opportunities to mix and nest frameworks together
## Options
This integration is powered by `@sveltejs/vite-plugin-svelte`. To customize the Svelte compiler, options can be provided to the integration. See the [`@sveltejs/vite-plugin-svelte` docs](https://github.com/sveltejs/vite-plugin-svelte/blob/HEAD/docs/config.md) for more details.
You can set options either by passing them to the `svelte` integration in `astro.config.mjs` or in `svelte.config.js`. The options in `astro.config.mjs` will take precedence over the options in `svelte.config.js` if both are present:
```js title="astro.config.mjs" "extensions: ['.svelte']"
import { defineConfig } from 'astro/config';
import svelte from '@astrojs/svelte';
export default defineConfig({
integrations: [svelte({ extensions: ['.svelte'] })],
});
```
```js title="svelte.config.js"
export default {
extensions: ['.svelte'],
};
```
## Preprocessors
<Since v="2.0.0" pkg="@astrojs/svelte" />
If you're using SCSS or Stylus in your Svelte files, you can create a `svelte.config.js` file so that they are preprocessed by Svelte, and the Svelte IDE extension can correctly parse the Svelte files.
```js title="svelte.config.js"
import { vitePreprocess } from '@astrojs/svelte';
export default {
preprocess: vitePreprocess(),
};
```
This config file will be automatically added for you when you run `astro add svelte`. See the [`@sveltejs/vite-plugin-svelte` docs](https://github.com/sveltejs/vite-plugin-svelte/blob/HEAD/docs/preprocess.md) for more details about `vitePreprocess`.
[astro-integration]: /en/guides/integrations-guide/
[astro-ui-frameworks]: /en/guides/framework-components/#using-framework-components
---
File: /src/content/docs/en/guides/integrations-guide/tailwind.mdx
---
---
title: '@astrojs/tailwind'
description: Learn how to use the @astrojs/tailwind integration in your Astro project.
i18nReady: true
---
:::caution[Deprecated]
Tailwind CSS now offers a Vite plugin which is the preferred way to use Tailwind 4 in Astro.
:::
To use Tailwind in Astro, follow the [styling guide for Tailwind](/en/guides/styling/#tailwind).
---
File: /src/content/docs/en/guides/integrations-guide/vercel.mdx
---
---
type: integration
title: '@astrojs/vercel'
description: Learn how to use the @astrojs/vercel adapter to deploy your Astro project.
sidebar:
label: Vercel
githubIntegrationURL: 'https://github.com/withastro/astro/tree/main/packages/integrations/vercel/'
category: adapter
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import Since from '~/components/Since.astro';
import ReadMore from '~/components/ReadMore.astro'
import { Steps } from '@astrojs/starlight/components';
This adapter allows Astro to deploy your [on-demand rendered routes and features](/en/guides/on-demand-rendering/) to [Vercel](https://www.vercel.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 only need this adapter if you are using additional Vercel services (e.g. [Vercel Web Analytics](https://vercel.com/docs/analytics), [Vercel Image Optimization](https://vercel.com/docs/image-optimization)). Otherwise, you do not need an adapter to deploy your static site.
Learn how to deploy your Astro site in our [Vercel deployment guide](/en/guides/deploy/vercel/).
## Why Astro Vercel?
[Vercel](https://www.vercel.com/) is a deployment platform that allows you to host your site by connecting directly to your GitHub repository. This adapter enhances the Astro build process to prepare your project for deployment through Vercel.
## 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 Vercel adapter to enable on-demand rendering in your Astro project with the following `astro add` command. This will install `@astrojs/vercel` and make the appropriate changes to your `astro.config.mjs` file in one step.
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npx astro add vercel
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm astro add vercel
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn astro add vercel
```
</Fragment>
</PackageManagerTabs>
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
First, add the `@astrojs/vercel` adapter to your project’s dependencies using your preferred package manager:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm install @astrojs/vercel
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm add @astrojs/vercel
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn add @astrojs/vercel
```
</Fragment>
</PackageManagerTabs>
Then, add the adapter to your `astro.config.*` file:
```js title="astro.config.mjs" ins={2, 6}
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel';
export default defineConfig({
// ...
adapter: vercel(),
});
```
## Usage
<ReadMore>Find out more about [deploying your project to Vercel](/en/guides/deploy/vercel/).</ReadMore>
You can deploy by CLI (`vercel deploy`) or by connecting your new repo in the [Vercel Dashboard](https://vercel.com/). Alternatively, you can create a production build locally:
```sh
astro build
vercel deploy --prebuilt
```
## Configuration
To configure this adapter, pass an object to the `vercel()` function call in `astro.config.mjs`:
### `webAnalytics`
**Type:** `VercelWebAnalyticsConfig`<br/>
**Available for:** Serverless, Static<br/>
<Since v="3.8.0" pkg="@astrojs/vercel" />
With `@vercel/[email protected]` or earlier, you can set `webAnalytics: { enabled: true }` in your Astro config to inject Vercel’s tracking scripts into all of your pages.
For `@vercel/[email protected]` and later, use Vercel's Analytics component to enable [Vercel Web Analytics](https://vercel.com/docs/concepts/analytics) instead.
```js title="astro.config.mjs" ins={7-9}
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel';
export default defineConfig({
// ...
adapter: vercel({
webAnalytics: {
enabled: true,
},
}),
});
```
### `imagesConfig`
**Type:** `VercelImageConfig`<br/>
**Available for:** Serverless, Static
<Since v="3.3.0" pkg="@astrojs/vercel" />
Configuration options for [Vercel's Image Optimization API](https://vercel.com/docs/concepts/image-optimization). See [Vercel's image configuration documentation](https://vercel.com/docs/build-output-api/v3/configuration#images) for a complete list of supported parameters.
The `domains` and `remotePatterns` properties will automatically be filled using [the Astro corresponding `image` settings](/en/reference/configuration-reference/#image-options).
```js title="astro.config.mjs" ins={8-10}
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel';
export default defineConfig({
// ...
output: 'static',
adapter: vercel({
imagesConfig: {
sizes: [320, 640, 1280],
},
}),
});
```
### `imageService`
**Type:** `boolean`<br/>
**Available for:** Serverless, Static
<Since v="3.3.0" pkg="@astrojs/vercel" />
When enabled, an [Image Service](/en/reference/image-service-reference/) powered by the Vercel Image Optimization API will be automatically configured and used in production. In development, the image service specified by [`devImageService`](#devimageservice) will be used instead.
```js title="astro.config.mjs" ins={8}
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel';
export default defineConfig({
// ...
output: 'static',
adapter: vercel({
imageService: true,
}),
});
```
```astro title="src/pages/index.astro"
---
import { Image } from 'astro:assets';
import astroLogo from '../assets/logo.png';
---
<!-- This component -->
<Image src={astroLogo} alt="My super logo!" />
<!-- will become the following HTML -->
<img
src="/_vercel/image?url=_astro/logo.hash.png&w=...&q=..."
alt="My super logo!"
loading="lazy"
decoding="async"
width="..."
height="..."
/>
```
### `devImageService`
**Type:** `'sharp' | string`<br/>
**Available for:** Serverless, Static
<Since v="3.8.0" pkg="@astrojs/vercel" />
**Default**: `sharp`
Allows you to configure which image service to use in development when [imageService](#imageservice) is enabled. This can be useful if you cannot install Sharp's dependencies on your development machine, but using another image service like Squoosh would allow you to preview images in your dev environment. Build is unaffected and will always use Vercel's Image Optimization.
It can also be set to any arbitrary value in order to use a custom image service instead of Astro's built-in ones.
```js title="astro.config.mjs" ins={7-8}
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel';
export default defineConfig({
// ...
adapter: vercel({
imageService: true,
devImageService: 'sharp',
}),
});
```
### `isr`
**Type:** <code>boolean | VercelISRConfig</code><br/>
**Available for:** Serverless
<Since v="7.2.0" pkg="@astrojs/vercel" />
**Default**: `false`
Allows your project to be deployed as an [ISR (Incremental Static Regeneration)](https://vercel.com/docs/incremental-static-regeneration) function, which caches your on-demand rendered pages in the same way as prerendered pages after first request.
To enable this feature, set `isr` to true in your Vercel adapter configuration in `astro.config.mjs`:
```js title="astro.config.mjs" ins={7}
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel';
export default defineConfig({
// ...
adapter: vercel({
isr: true,
}),
});
```
Note that ISR function requests do not include search params, similar to [requests](/en/reference/api-reference/#request) in static mode.
#### ISR cache invalidation
By default, an ISR function caches for the duration of your deployment. You can further control caching by setting an expiration time, or by excluding particular routes from caching entirely.
##### Time-based invalidation
You can change the length of time to cache routes this by configuring an `expiration` value in seconds:
```js title="astro.config.mjs" {7-10}
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel';
export default defineConfig({
// ...
adapter: vercel({
isr: {
// caches all pages on first request and saves for 1 day
expiration: 60 * 60 * 24,
},
}),
});
```
##### Excluding paths from caching
To implement Vercel's [Draft mode](https://vercel.com/docs/build-output-api/v3/features#draft-mode), or [On-Demand Incremental Static Regeneration (ISR)](https://vercel.com/docs/build-output-api/v3/features#on-demand-incremental-static-regeneration-isr), you can create a bypass token and provide it to the `isr` config along with any routes to exclude from caching:
```js title="astro.config.mjs" {6-15}
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel';
export default defineConfig({
adapter: vercel({
isr: {
// A secret random string that you create.
bypassToken: "005556d774a8",
// Paths that will always be served fresh.
exclude: [
'/preview',
'/auth/[page]',
/^\/api\/.+/ // Regular expressions supported since @astrojs/[email protected]
]
}
})
})
```
### `includeFiles`
**Type:** `string[]`<br/>
**Available for:** Serverless
Use this property to force files to be bundled with your function. This is helpful when you notice missing files.
```js title="astro.config.mjs" ins={7}
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel';
export default defineConfig({
// ...
adapter: vercel({
includeFiles: ['./my-data.json'],
}),
});
```
### `excludeFiles`
**Type:** `string[]`<br/>
**Available for:** Serverless
Use this property to exclude any files from the bundling process that would otherwise be included.
```js title="astro.config.mjs" ins={7}
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel';
export default defineConfig({
// ...
adapter: vercel({
excludeFiles: ['./src/some_big_file.jpg'],
}),
});
```
### `maxDuration`
**Type:** `number`<br/>
**Available for:** Serverless
Use this property to extend or limit the maximum duration (in seconds) that Serverless Functions can run before timing out. See the [Vercel documentation](https://vercel.com/docs/functions/serverless-functions/runtimes#maxduration) for the default and maximum limit for your account plan.
```js title="astro.config.mjs" ins={7}
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel';
export default defineConfig({
// ...
adapter: vercel({
maxDuration: 60
}),
});
```
### `skewProtection`
**Type:** `boolean`<br/>
**Available for:** Serverless
<Since pkg="@astrojs/vercel" v="7.6.0" />
Use this property to enable [Vercel Skew protection](https://vercel.com/docs/deployments/skew-protection) (available with Vercel Pro and Enterprise accounts).
```js title="astro.config.mjs" ins={7}
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel';
export default defineConfig({
// ...
adapter: vercel({
skewProtection: true
}),
});
```
### Running Astro middleware on Vercel Edge Functions
The `@astrojs/vercel` adapter can create an [edge function](https://vercel.com/docs/functions/edge-functions) from an Astro middleware in your code base. When `edgeMiddleware` is enabled, an edge function will execute your middleware code for all requests including static assets, prerendered pages, and on-demand rendered pages.
For on-demand rendered pages, the `context.locals` object is serialized using JSON and sent in a header for the serverless function, which performs the rendering. As a security measure, the serverless function will refuse to serve requests with a `403 Forbidden` response unless they come from the generated edge function.
This is an opt-in feature. To enable it, set `edgeMiddleware` to `true`:
```js title="astro.config.mjs" "edgeMiddleware: true"
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel';
export default defineConfig({
// ...
adapter: vercel({
edgeMiddleware: true,
}),
});
```
The edge middleware has access to Vercel's [`RequestContext`](https://vercel.com/docs/functions/edge-middleware/middleware-api#requestcontext) as `ctx.locals.vercel.edge`. If you’re using TypeScript, you can get proper typings by updating `src/env.d.ts` to use `EdgeLocals`:
```ts
type EdgeLocals = import('@astrojs/vercel').EdgeLocals
declare namespace App {
interface Locals extends EdgeLocals {
// ...
}
}
```
### Node.js Version Support
The `@astrojs/vercel` adapter supports specific Node.js versions for deploying your Astro project on Vercel. To view the supported Node.js versions on Vercel, click on the settings tab for a project and scroll down to "Node.js Version" section.
Check out the [Vercel documentation](https://vercel.com/docs/functions/serverless-functions/runtimes/node-js#default-and-available-versions) to learn more.
### 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.
When using sessions on Vercel, you need to [configure a driver](/en/reference/configuration-reference/#sessiondriver) for session storage. You can install a storage provider from [the Vercel marketplace](https://vercel.com/marketplace?category=storage).
For example, if you have installed [a Redis integration](https://vercel.com/marketplace?category=storage&search=redis) and linked a database to your site:
<Steps>
1. Install the `ioredis` package:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm install ioredis
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm install ioredis
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn add ioredis
```
</Fragment>
</PackageManagerTabs>
2. Use [the Vercel CLI](https://vercel.com/docs/cli) to load your environment variables:
```sh
vercel env pull .env.local
```
This will create a `.env.local` file in your project root with the environment variables needed to connect to your Redis database when developing locally.
3. Configure the session driver:
```js title="astro.config.mjs" ins={6-11}
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel';
export default defineConfig({
adapter: vercel(),
session: {
driver: 'redis',
options: {
url: process.env.REDIS_URL,
},
},
});
```
</Steps>
[astro-integration]: /en/guides/integrations-guide/
---
File: /src/content/docs/en/guides/integrations-guide/vue.mdx
---
---
type: integration
title: '@astrojs/vue'
description: Learn how to use the @astrojs/vue framework integration to extend component support in your Astro project.
sidebar:
label: Vue
githubIntegrationURL: 'https://github.com/withastro/astro/tree/main/packages/integrations/vue/'
category: renderer
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import Since from '~/components/Since.astro';
This **[Astro integration][astro-integration]** enables rendering and client-side hydration for your [Vue 3](https://vuejs.org/) components.
## 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/vue`, run the following from your project directory and follow the prompts:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npx astro add vue
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm astro add vue
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn astro add vue
```
</Fragment>
</PackageManagerTabs>
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/vue` package:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm install @astrojs/vue
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm add @astrojs/vue
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn add @astrojs/vue
```
</Fragment>
</PackageManagerTabs>
Most package managers will install associated peer dependencies as well. If you see a `Cannot find package 'vue'` (or similar) warning when you start up Astro, you'll need to install Vue:
<PackageManagerTabs>
<Fragment slot="npm">
```sh
npm install vue
```
</Fragment>
<Fragment slot="pnpm">
```sh
pnpm add vue
```
</Fragment>
<Fragment slot="yarn">
```sh
yarn add vue
```
</Fragment>
</PackageManagerTabs>
Then, apply the integration to your `astro.config.*` file using the `integrations` property:
```js ins={2} ins="vue()" title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import vue from '@astrojs/vue';
export default defineConfig({
// ...
integrations: [vue()],
});
```
## Getting started
To use your first Vue component in Astro, head to our [UI framework documentation][astro-ui-frameworks]. You'll explore:
* 📦 how framework components are loaded,
* 💧 client-side hydration options, and
* 🤝 opportunities to mix and nest frameworks together
## Troubleshooting
For help, check out the `#support` channel on [Discord](https://astro.build/chat). Our friendly Support Squad members are here to help!
You can also check our [Astro Integration Documentation][astro-integration] for more on integrations.
## Contributing
This package is maintained by Astro's Core team. You're welcome to submit an issue or PR!
[astro-integration]: /en/guides/integrations-guide/
[astro-ui-frameworks]: /en/guides/framework-components/#using-framework-components
## Options
This integration is powered by `@vitejs/plugin-vue`. To customize the Vue compiler, options can be provided to the integration. See the `@vitejs/plugin-vue` [docs](https://www.npmjs.com/package/@vitejs/plugin-vue) for more details.
```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import vue from '@astrojs/vue';
export default defineConfig({
// ...
integrations: [
vue({
template: {
compilerOptions: {
// treat any tag that starts with ion- as custom elements
isCustomElement: (tag) => tag.startsWith('ion-'),
},
},
// ...
}),
],
});
```
### appEntrypoint
You can extend the Vue `app` instance setting the `appEntrypoint` option to a root-relative import specifier (for example, `appEntrypoint: "/src/pages/_app"`).
The default export of this file should be a function that accepts a Vue `App` instance prior to rendering, allowing the use of [custom Vue plugins](https://vuejs.org/guide/reusability/plugins.html), `app.use`, and other customizations for advanced use cases.
```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import vue from '@astrojs/vue';
export default defineConfig({
// ...
integrations: [vue({ appEntrypoint: '/src/pages/_app' })],
});
```
```ts title="src/pages/_app.ts"
import type { App } from 'vue';
import i18nPlugin from 'my-vue-i18n-plugin';
export default (app: App) => {
app.use(i18nPlugin);
};
```
### jsx
You can use Vue JSX by setting `jsx: true`.
```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import vue from '@astrojs/vue';
export default defineConfig({
// ...
integrations: [vue({ jsx: true })],
});
```
This will enable rendering for both Vue and Vue JSX components. To customize the Vue JSX compiler, pass an options object instead of a boolean. See the `@vitejs/plugin-vue-jsx` [docs](https://www.npmjs.com/package/@vitejs/plugin-vue-jsx) for more details.
```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import vue from '@astrojs/vue';
export default defineConfig({
// ...
integrations: [
vue({
jsx: {
// treat any tag that starts with ion- as custom elements
isCustomElement: (tag) => tag.startsWith('ion-'),
},
}),
],
});
```
### devtools
<p><Since pkg="@astrojs/vue" v="4.2.0" /></p>
You can enable [Vue DevTools](https://devtools-next.vuejs.org/) in development by passing an object with `devtools: true` to your `vue()` integration config:
```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import vue from '@astrojs/vue';
export default defineConfig({
// ...
integrations: [vue({ devtools: true })],
});
```
#### Customizing Vue DevTools
<p><Since pkg="@astrojs/vue" v="4.3.0" /></p>
For more customization, you can instead pass options that the [Vue DevTools Vite Plugin](https://devtools-next.vuejs.org/guide/vite-plugin#options) supports. (Note: `appendTo` is not supported.)
For example, you can set `launchEditor` to your preferred editor if you are not using Visual Studio Code:
```js title="astro.config.mjs"
import { defineConfig } from "astro/config";
import vue from "@astrojs/vue";
export default defineConfig({
// ...
integrations: [
vue({
devtools: { launchEditor: "webstorm" },
}),
],
});
```
---
File: /src/content/docs/en/guides/media/cloudinary.mdx
---
---
title: Cloudinary & Astro
description: Add images and videos to your Astro project using Cloudinary
sidebar:
label: Cloudinary
type: media
stub: true
service: Cloudinary
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
[Cloudinary](https://cloudinary.com) is an image and video platform and headless Digital Asset Manager (DAM) that lets you host assets and deliver them from their content delivery network (CDN).
When delivering from Cloudinary, you additionally get access to their Transformation API, giving you the ability to edit your assets with tools like background removal, dynamic cropping and resizing, and generative AI.
## Using Cloudinary in Astro
Cloudinary supports a wide variety of SDKs that can be used depending on your Astro environment.
The [Cloudinary Astro SDK](https://astro.cloudinary.dev/) provides native Astro components, including image, video, and upload components, as well as a content loader that can be used with Astro content collections.
Alternatively, both the Cloudinary [Node.js SDK](https://cloudinary.com/documentation/node_integration) and [JavaScript SDK](https://cloudinary.com/documentation/javascript_integration) can be used to generate URLs for your images. The Node.js SDK can additionally make requests to the Cloudinary API including uploading assets, requesting resources, and running content analysis.
## Prerequisites
- An existing Astro project
- A Cloudinary account
## Installing Astro Cloudinary
Install the Cloudinary Astro SDK by running the appropriate command for your package manager:
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install astro-cloudinary
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm add astro-cloudinary
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn add astro-cloudinary
```
</Fragment>
</PackageManagerTabs>
## Configuring your account
Create a new `.env` file in the root of your project and add your Cloudinary credentials:
```shell title=".env"
PUBLIC_CLOUDINARY_CLOUD_NAME="<Your Cloud Name>"
// Only needed if using CldUploadWidget or cldAssetsLoader
PUBLIC_CLOUDINARY_API_KEY="<Your API Key>"
CLOUDINARY_API_SECRET="<Your API Secret>"
```
## Using Cloudinary images
Add images in `.astro` components by passing image data (e.g. `src`, `width`, `alt`) to the `<CldImage>` component. This will automatically optimize your image and give you access to the Transformations API.
```jsx title="Component.astro"
---
import { CldImage } from 'astro-cloudinary';
---
<CldImage
src="<Public ID>"
width="<Width>"
height="<Height>"
alt="<Description>"
/>
```
See [Cloudinary's `<CldImage>` documentation](https://astro.cloudinary.dev/cldimage/basic-usage) for more information.
## Using Cloudinary videos
To add video to your `.astro` components, add the `<CldVideoPlayer>` and pass the appropriate properties. This component will automatically optimize and embed your video using the [Cloudinary Video Player](https://cloudinary.com/documentation/cloudinary_video_player).
```jsx title="Component.astro"
---
import { CldVideoPlayer } from 'astro-cloudinary';
---
<CldVideoPlayer
src="<Public ID>"
width="<Width>"
height="<Height>"
/>
```
See [Cloudinary's `<CldVideoPlayer>` documentation](https://astro.cloudinary.dev/cldvideoplayer/basic-usage) for more information.
## Enabling Cloudinary uploads
To enable file uploading in your website or app's UI, add the `<CldUploadWidget>` which will embed the [Cloudinary Upload Widget](https://cloudinary.com/documentation/upload_widget).
The following example creates a widget to allow unsigned uploads by passing an unsigned [Upload Preset](https://cloudinary.com/documentation/upload_presets):
```jsx title="Component.astro"
---
import { CldUploadWidget } from 'astro-cloudinary';
---
<CldUploadWidget uploadPreset="<Upload Preset>">
<button>Upload</button>
</CldUploadWidget>
```
For signed uploads, you can find [a guide and example](https://astro.cloudinary.dev/clduploadwidget/signed-uploads) on the Astro Cloudinary docs.
See [Cloudinary's `<CldUploadWidget>` documentation](https://astro.cloudinary.dev/clduploadwidget/basic-usage) for more information.
## Cloudinary content loader
The Cloudinary Astro SDK provides the `cldAssetsLoader` content loader to load Cloudinary assets for content collections.
To load a collection of images or videos, set `loader: cldAssetsLoader ({})` with a `folder`, if required:
```jsx title="config.ts"
import { defineCollection } from 'astro:content';
import { cldAssetsLoader } from 'astro-cloudinary/loaders';
export const collections = {
assets: defineCollection({
loader: cldAssetsLoader({
folder: '<Folder>' // Optional, without loads root directory
})
}),
}
```
You can then use the [`getCollection()` or `getEntry()` query functions](/en/guides/content-collections/#querying-collections) to select one or many images or videos from your collection.
See [Cloudinary's `cldAssetsLoader` documentation](https://astro.cloudinary.dev/cldassetsloader/basic-usage) for more information.
## Generating Cloudinary image URLs
The Astro Cloudinary SDK provides a `getCldOgImageUrl()` helper for generating and using URLs for your images. Use this when you need a URL instead of a component to display your image.
One common use for a URL is for an Open Graph image in `<meta>` tags for social media cards. This helper, like the components, provides you access to Cloudinary transformations to create dynamic, unique social cards for any of your pages.
The following example shows the necessary `<meta>` tags for a social media card, using `getCldOgImageUrl()` to generate an Open Graph image:
```jsx title="Layout.astro"
---
import { getCldOgImageUrl } from 'astro-cloudinary/helpers';
const ogImageUrl = getCldOgImageUrl({ src: '<Public ID>' });
---
<meta property="og:image" content={ogImageUrl} />
<meta property="og:image:secure_url" content={ogImageUrl} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="twitter:title" content="<Twitter Title>" />
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:image" content={ogImageUrl} />
```
Find [Cloudinary Social Media Card templates](https://astro.cloudinary.dev/templates/social-media-cards) on the Cloudinary docs.
See [Cloudinary's `getCldOgImageUrl()` documentation](https://astro.cloudinary.dev/getcldogimageurl/basic-usage) for more information.
## Using Cloudinary in Node.js
For more complex asset management, uploading, or analysis, you can use the Cloudinary Node.js SDK when working in an Astro Node.js environment.
Install the Cloudinary Node.js SDK by running the appropriate command for your package manager:
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm install cloudinary
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm add cloudinary
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn add cloudinary
```
</Fragment>
</PackageManagerTabs>
Add the following environment variables in your `.env` file:
```shell title=".env"
PUBLIC_CLOUDINARY_CLOUD_NAME="<Your Cloud Name>"
PUBLIC_CLOUDINARY_API_KEY="<Your API Key>"
CLOUDINARY_API_SECRET="<Your API Secret>"
```
Configure your account with a new Cloudinary instance by adding the following code between the fences of your Astro component:
```js title="Component.astro"
---
import { v2 as cloudinary } from "cloudinary";
cloudinary.config({
cloud_name: import.meta.env.PUBLIC_CLOUDINARY_CLOUD_NAME,
api_key: import.meta.env.PUBLIC_CLOUDINARY_API_KEY,
api_secret: import.meta.env.CLOUDINARY_API_SECRET,
});
---
```
This will give you access to all of the Cloudinary APIs to allow you to interact with your images, videos, and other supported files.
```js title="Component.astro"
await cloudinary.uploader.upload('./path/to/file');
```
Learn how to [upload files using the Cloudinary Node.js SDK with Astro Forms](https://www.youtube.com/watch?v=DQUYMyT2MTM).
## Official Resources
- [Cloudinary Astro SDK](https://astro.cloudinary.dev/)
- [Cloudinary Node.js SDK](https://cloudinary.com/documentation/node_integration)
- [Using Cloudinary with Astro (YouTube)](https://www.youtube.com/playlist?list=PL8dVGjLA2oMqnpf2tShn1exf5GkSWuu5-)
- [Code Examples Using Cloudinary Astro SDK (GitHub)](https://github.com/cloudinary-community/cloudinary-examples/tree/main/examples/astro-cloudinary)
---
File: /src/content/docs/en/guides/media/index.mdx
---
---
title: Use a DAM with Astro
description: How to use a Digital Asset Manager (DAM) to add images and videos to Astro
sidebar:
label: Digital Asset Management overview
i18nReady: true
---
import MediaGuidesNav from '~/components/MediaGuidesNav.astro';
import ReadMore from '~/components/ReadMore.astro';
import Badge from "~/components/Badge.astro"
**Ready to connect a headless Digital Asset Manager (DAM) to your Astro project?** Follow one of our guides to integrate a hosted media system.
:::tip
Find [community-maintained integrations](https://astro.build/integrations/) for connecting a DAM or hosted media system to your project in our integrations directory.
:::
## Hosted Media Guides
Note that many of these pages are **stubs**: they're collections of resources waiting for your contribution!
<MediaGuidesNav />
## Why use a DAM or hosted media?
Using a DAM, or Digital Asset Manager, helps individuals, teams, and organizations manage their image and video assets from a central location much like a [CMS](/en/guides/cms/).
The difference is the type of content being managed: a DAM would primarily manage images, videos, other media assets like 3D models, and any metadata associated with those assets.
This can be useful particularly when using a single source of truth for your assets between multiple web or mobile properties. This is important if you're part of an organization that requires multiple teams to use the same assets, or are integrating into other content systems like a PIM (Product Information Manager) to connect your assets to products.
## Which hosted media systems or DAMs work well with Astro?
Much like when using a CMS, as Astro handles the _presentation_ of your content, you'll want to use a headless DAM that allows you to fetch and interact with your assets via an API or SDK.
Some headless DAMs, like Cloudinary, provide an Astro [integration](/en/guides/integrations-guide/) that allows you to easily fetch your assets as well as display them on your website or app.
## Can I use Astro without a hosted media system or DAM?
Yes! Astro provides built-in ways to [store images](/en/guides/images/#where-to-store-images), including support for referencing remote images.
---
File: /src/content/docs/en/guides/migrate-to-astro/from-create-react-app.mdx
---
---
title: Migrating from Create React App (CRA)
description: Tips for migrating an existing Create React App project to Astro
sidebar:
label: Create React App
type: migration
stub: true
framework: Create React App
i18nReady: true
---
import AstroJSXTabs from '~/components/tabs/AstroJSXTabs.astro';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import { FileTree } from '@astrojs/starlight/components';
import ReadMore from '~/components/ReadMore.astro';
import Badge from "~/components/Badge.astro"
import { LinkCard, CardGrid } from '@astrojs/starlight/components';
Astro's [React integration](/en/guides/integrations-guide/react/) provides support for [using React components inside Astro components](/en/guides/framework-components/), including entire React apps like Create React App (CRA)!
```astro title="src/pages/index.astro"
---
// Import your root App component
import App from '../cra-project/App.jsx';
---
<!-- Use a client directive to load your app -->
<App client:load />
```
<ReadMore>See how to [Build a Single Page Application (SPA) with Astro](https://logsnag.com/blog/react-spa-with-astro) <Badge text="External" /> using React Router.</ReadMore>
Many apps will "just work" as full React apps when you add them directly to your Astro project with the React integration installed. This is a great way to get your project up and running immediately and keep your app functional while you migrate to Astro.
Over time, you can convert your structure piece-by-piece to a combination of `.astro` and `.jsx` components. You will probably discover you need fewer React components than you think!
Here are some key concepts and migration strategies to help you get started. Use the rest of our docs and our [Discord community](https://astro.build/chat) to keep going!
## Key Similarities between CRA and Astro
- The [syntax of `.astro` files is similar to JSX](/en/reference/astro-syntax/#differences-between-astro-and-jsx). Writing Astro should feel familiar.
- Astro uses file-based routing, and [allows specially named pages to create dynamic routes](/en/guides/routing/#dynamic-routes).
- Astro is [component-based](/en/basics/astro-components/), and your markup structure will be similar before and after your migration.
- Astro has [official integrations for React, Preact, and Solid](/en/guides/integrations-guide/react/) so you can use your existing JSX components. Note that in Astro, these files **must** have a `.jsx` or `.tsx` extension.
- Astro has support for [installing NPM packages](/en/guides/imports/#npm-packages), including React libraries. Many of your existing dependencies will work in Astro.
## Key Differences between CRA and Astro
When you rebuild your CRA site in Astro, you will notice some important differences:
- CRA is a single-page application that uses `index.js` as your project's root. Astro is a multi-page site, and `index.astro` is your home page.
- [`.astro` components](/en/basics/astro-components/) are not written as exported functions that return page templating. Instead, you'll split your code into a "code fence" for your JavaScript and a body exclusively for the HTML you generate.
- [content-driven](/en/concepts/why-astro/#content-driven): Astro was designed to showcase your content and to allow you to opt-in to interactivity only as needed. An existing CRA app might be built for high client-side interactivity and may require advanced Astro techniques to include items that are more challenging to replicate using `.astro` components, such as dashboards.
## Add your CRA to Astro
Your existing app can be rendered directly inside a new Astro project, often with no changes to your app's code.
### Create a new Astro project
Use the `create astro` command for your package manager to launch Astro's CLI wizard and select a new "empty" Astro project.
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm create astro@latest
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm create astro@latest
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn create astro@latest
```
</Fragment>
</PackageManagerTabs>
### Add integrations and dependencies
Add the React integration using the `astro add` command for your package manager. If your app uses other packages supported by the `astro add` command, like Tailwind and MDX, you can add them all with one command:
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npx astro add react
npx astro add react tailwind mdx
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm astro add react
pnpm astro add react tailwind mdx
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn astro add react
yarn astro add react tailwind mdx
```
</Fragment>
</PackageManagerTabs>
If your CRA requires any dependencies (e.g. NPM packages), then install them individually using the command line or by adding them to your new Astro project's `package.json` manually and then running an install command. Note that many, but not all, React dependencies will work in Astro.
### Add your existing app files
Copy your existing Create React App (CRA) project source files and folders (e.g. `components`, `hooks`, `styles`, etc.) into a new folder inside `src/`, keeping its file structure so your app will continue to work. Note that all `.js` file extensions must be renamed to `.jsx` or `.tsx`.
Do not include any configuration files. You will use Astro's own `astro.config.mjs`, `package.json`, and `tsconfig.json`.
Move the contents of your app's `public/` folder (e.g. static assets) into Astro's `public/` folder.
<FileTree>
- public/
- logo.png
- favicon.ico
- ...
- src/
- cra-project/
- App.jsx
- ...
- pages/
- index.astro
- astro.config.mjs
- package.json
- tsconfig.json
</FileTree>
### Render your app
Import your app's root component in the frontmatter section of `index.astro`, then render the `<App />` component in your page template:
```astro title="src/pages/index.astro"
---
import App from '../cra-project/App.jsx';
---
<App client:load />
```
:::note[Client directives]
Your app needs a [client directive](/en/reference/directives-reference/#client-directives) for interactivity. Astro will render your React app as static HTML until you opt-in to client-side JavaScript.
Use `client:load` to ensure your app loads immediately from the server, or `client:only="react"` to skip rendering on the server and run your app entirely client-side.
:::
## Convert your CRA to Astro
After [adding your existing app to Astro](#add-your-cra-to-astro), you will probably want to convert your app itself to Astro!
You will replicate a similar component-based design [using Astro HTML templating components for your basic structure](/en/basics/astro-components/) while importing and including individual React components (which may themselves be entire apps!) for islands of interactivity.
Every migration will look different and can be done incrementally without disrupting your working app. Convert individual pieces at your own pace so that more and more of your app is powered by Astro components over time.
As you convert your React app, you will decide which React components you will [rewrite as Astro components](#converting-jsx-files-to-astro-files). Your only restriction is that Astro components can import React components, but React components must only import other React components:
```astro title="src/pages/static-components.astro" ins={2,7}
---
import MyReactComponent from '../components/MyReactComponent.jsx';
---
<html>
<body>
<h1>Use React components directly in Astro!</h1>
<MyReactComponent />
</body>
</html>
```
Instead of importing Astro components into React components, you can nest React components inside a single Astro component:
```astro title="src/pages/nested-components.astro" {2,3,5,8,10}
---
import MyReactSidebar from '../components/MyReactSidebar.jsx';
import MyReactButton from '../components/MyReactButton.jsx';
---
<MyReactSidebar>
<p>Here is a sidebar with some text and a button.</p>
<div slot="actions">
<MyReactButton client:idle />
</div>
</MyReactSidebar>
```
You may find it helpful to learn about [Astro islands](/en/concepts/islands/) and [Astro components](/en/basics/astro-components/) before restructuring your CRA as an Astro project.
### Compare: JSX vs Astro
Compare the following CRA component and a corresponding Astro component:
<AstroJSXTabs>
<Fragment slot="jsx">
```jsx title="StarCount.jsx"
import React, { useState, useEffect } from 'react';
import Header from './Header';
import Footer from './Footer';
const Component = () => {
const [stars, setStars] = useState(0);
const [message, setMessage] = useState('');
useEffect(() => {
const fetchData = async () => {
const res = await fetch('https://api.github.com/repos/withastro/astro');
const json = await res.json();
setStars(json.stargazers_count || 0);
setMessage(json.message);
};
fetchData();
}, []);
return (
<>
<Header />
<p style={{
backgroundColor: `#f4f4f4`,
padding: `1em 1.5em`,
textAlign: `center`,
marginBottom: `1em`
}}>Astro has {stars} 🧑‍🚀</p>
<Footer />
</>
)
};
export default Component;
```
</Fragment>
<Fragment slot="astro">
```astro title="StarCount.astro"
---
import Header from './Header.astro';
import Footer from './Footer.astro';
import './layout.css';
const res = await fetch('https://api.github.com/repos/withastro/astro')
const json = await res.json();
const message = json.message;
const stars = json.stargazers_count || 0;
---
<Header />
<p class="banner">Astro has {stars} 🧑‍🚀</p>
<Footer />
<style>
.banner {
background-color: #f4f4f4;
padding: 1em 1.5em;
text-align: center;
margin-bottom: 1em;
}
</style>
```
</Fragment>
</AstroJSXTabs>
### Converting JSX files to `.astro` files
Here are some tips for converting a CRA `.js` component into a `.astro` component:
1. Use the returned JSX of the existing CRA component function as the basis for your HTML template.
2. Change any [CRA or JSX syntax to Astro](#reference-convert-cra-syntax-to-astro) or to HTML web standards. This includes `{children}` and `className`, for example.
3. Move any necessary JavaScript, including import statements, into a ["code fence" (`---`)](/en/basics/astro-components/#the-component-script). Note: JavaScript to [conditionally render content](/en/reference/astro-syntax/#dynamic-html) is often written inside the HTML template directly in Astro.
4. Use [`Astro.props`](/en/reference/api-reference/#props) to access any additional props that were previously passed to your CRA function.
5. Decide whether any imported components also need to be converted to Astro. You can keep them as React components for now, or forever. But, you may eventually want to convert them to `.astro` components, especially if they do not need to be interactive!
6. Replace `useEffect()` with import statements or [`import.meta.glob()`](/en/guides/imports/#importmetaglob) to query your local files. Use `fetch()` to fetch external data.
### Migrating Tests
As Astro outputs raw HTML, it is possible to write end-to-end tests using the output of the build step. Any end-to-end tests written previously might work out-of-the-box if you have been able to match the markup of your CRA site. Testing libraries such as Jest and React Testing Library can be imported and used in Astro to test your React components.
See Astro's [testing guide](/en/guides/testing/) for more.
## Reference: Convert CRA Syntax to Astro
### CRA Imports to Astro
Update any [file imports](/en/guides/imports/) to reference relative file paths exactly. This can be done using [import aliases](/en/guides/typescript/#import-aliases), or by writing out a relative path in full.
Note that `.astro` and several other file types must be imported with their full file extension.
```astro title="src/pages/authors/Fred.astro"
---
import Card from '../../components/Card.astro';
---
<Card />
```
### CRA Children Props to Astro
Convert any instances of `{children}` to an Astro `<slot />`. Astro does not need to receive `{children}` as a function prop and will automatically render child content in a `<slot />`.
```astro title="src/components/MyComponent.astro" del={3-9} ins={11-13}
---
---
export default function MyComponent(props) {
return (
<div>
{props.children}
</div>
);
}
<div>
<slot />
</div>
```
React components that pass multiple sets of children can be migrated to an Astro component using [named slots](/en/basics/astro-components/#named-slots).
See more about [specific `<slot />` usage in Astro](/en/basics/astro-components/#slots).
### CRA Data Fetching to Astro
Fetching data in a Create React App component is similar to Astro, with some slight differences.
You will need to remove any instances of a side effect hook (`useEffect`) for either `import.meta.glob()` or `getCollection()`/`getEntryBySlug()` to access data from other files in your project source.
To [fetch remote data](/en/guides/data-fetching/), use `fetch()`.
These data requests are made in the frontmatter of the Astro component and use top-level await.
```astro title="src/pages/index.astro"
---
import { getCollection } from 'astro:content';
// Get all `src/content/blog/` entries
const allBlogPosts = await getCollection('blog');
// Get all `src/pages/posts/` entries
const allPosts = Object.values(import.meta.glob('../pages/post/*.md', { eager: true }));
// Fetch remote data
const response = await fetch('https://randomuser.me/api/');
const data = await response.json();
const randomUser = data.results[0];
---
```
See more about local files imports with [`import.meta.glob()`](/en/guides/imports/#importmetaglob), [querying using the Collections API](/en/guides/content-collections/#querying-collections) or [fetching remote data](/en/guides/data-fetching/).
### CRA Styling to Astro
You may need to replace any [CSS-in-JS libraries](https://github.com/withastro/astro/issues/4432) (e.g. styled-components) with other available CSS options in Astro.
If necessary, convert any inline style objects (`style={{ fontWeight: "bold" }}`) to inline HTML style attributes (`style="font-weight:bold;"`). Or, use an [Astro `<style>` tag](/en/guides/styling/#styling-in-astro) for scoped CSS styles.
```astro title="src/components/Card.astro" del={1} ins={2}
<div style={{backgroundColor: `#f4f4f4`, padding: `1em`}}>{message}</div>
<div style="background-color: #f4f4f4; padding: 1em;">{message}</div>
```
Tailwind is supported after installing the [Tailwind Vite plugin](/en/guides/styling/#tailwind). No changes to your existing Tailwind code are required!
See more about [Styling in Astro](/en/guides/styling/).
## Troubleshooting
Your CRA might "just work" in Astro! But, you may likely need to make minor adjustments to duplicate your existing app's functionality and/or styles.
If you cannot find your answers within these docs, please visit the [Astro Discord](https://astro.build/chat) and ask questions in our support forum!
## Community Resources
<CardGrid>
<LinkCard title="Code Fix: The SIBA Website's Move from Create-React-App to Astro" href="https://brittanisavery.com/post/move-siba-to-astro"/>
</CardGrid>
:::note[Have a resource to share?]
If you found (or made!) a helpful video or blog post about converting a Create React App to Astro, [add it to this list](https://github.com/withastro/docs/edit/main/src/content/docs/en/guides/migrate-to-astro/from-create-react-app.mdx)!
:::
---
File: /src/content/docs/en/guides/migrate-to-astro/from-docusaurus.mdx
---
---
title: Migrating from Docusaurus
description: Tips for migrating an existing Docusaurus project to Astro
sidebar:
label: Docusaurus
type: migration
stub: true
framework: Docusaurus
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import { CardGrid, LinkCard } from '@astrojs/starlight/components';
[Docusaurus](https://Docusaurus.io) is a popular documentation website builder built on React.
## Key Similarities between Docusaurus and Astro
Docusaurus and Astro share some similarities that will help you migrate your project:
- Both Astro and Docusaurus are modern, JavaScript-based (Jamstack) site builders intended for [content-driven websites](/en/concepts/why-astro/#content-driven), like documentation sites.
- Both Astro and Docusaurus support [MDX pages](/en/guides/markdown-content/). You should be able to use your existing `.mdx` files to Astro.
- Both Astro and Docusaurus use [file-based routing](/en/guides/routing/) to generate page routes automatically for any MDX file located in `src/pages`. Using Astro's file structure for your existing content and when adding new pages should feel familiar.
- Astro has an [official integration for using React components](/en/guides/integrations-guide/react/). Note that in Astro, React files **must** have a `.jsx` or `.tsx` extension.
- Astro supports [installing NPM packages](/en/guides/imports/#npm-packages), including several for React. You may be able to keep some or all of your existing React components and dependencies.
- [Astro's JSX-like syntax](/en/basics/astro-components/#the-component-template) should feel familiar if you are used to writing React.
## Key Differences between Docusaurus and Astro
When you rebuild your Docusaurus site in Astro, you will notice some important differences:
- Docusaurus is a React-based single-page application (SPA). Astro sites are multi-page apps built using [`.astro` components](/en/basics/astro-components/), but can also support [React, Preact, Vue.js, Svelte, SolidJS, AlpineJS](/en/guides/framework-components/) and raw HTML templating.
- Docusaurus was designed to build documentation websites and has some built-in, documentation-specific website features that you would have to build yourself in Astro. Instead, Astro offers some of these features through [Starlight: an official docs theme](https://starlight.astro.build). This website was the inspiration for Starlight, and now runs on it! You can also find more [community docs themes](https://astro.build/themes?search=&categories%5B%5D=docs) with built-in features in our Themes Showcase.
- Docusaurus sites use MDX pages for content. Astro's docs theme uses Markdown (`.md`) files by default and does not require you to use MDX. You can optionally [install Astro's MDX integration](/en/guides/integrations-guide/mdx/) (included in our Starlight theme by default) and use `.mdx` files in addition to standard Markdown files.
## Switch from Docusaurus to Astro
To convert a Docusaurus documentation site to Astro, start with our official [Starlight docs theme starter template](https://starlight.astro.build), or explore more community docs themes in our [theme showcase](https://astro.build/themes?search=&categories%5B%5D=docs).
You can pass a `--template` argument to the `create astro` command to start a new Astro project with one of our official starters. Or, you can [start a new project from any existing Astro repository on GitHub](/en/install-and-setup/#install-from-the-cli-wizard).
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm create astro@latest -- --template starlight
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm create astro@latest --template starlight
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn create astro --template starlight
```
</Fragment>
</PackageManagerTabs>
Astro's MDX integration is included by default, so you can [bring your existing content files to Starlight](https://starlight.astro.build/getting-started/#add-content) right away.
You can find Astro's docs starter, and other official templates, on [astro.new](https://astro.new). You'll find a link to each project's GitHub repository, as well as one-click links to open a working project in IDX, StackBlitz, CodeSandbox and Gitpod online development environments.
## Community Resources
<CardGrid>
<LinkCard title="Speeding up documentation by 10 times (Russian)" href="https://habr.com/ru/articles/880220/"/>
</CardGrid>
:::note[Have a resource to share?]
If you found (or made!) a helpful video or blog post about converting a Docusaurus site to Astro, [add it to this list](https://github.com/withastro/docs/edit/main/src/content/docs/en/guides/migrate-to-astro/from-docusaurus.mdx)!
:::
---
File: /src/content/docs/en/guides/migrate-to-astro/from-eleventy.mdx
---
---
title: Migrating from Eleventy
description: Tips for migrating an existing Eleventy project to Astro
sidebar:
label: Eleventy
type: migration
stub: true
framework: Eleventy
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import { Steps, LinkCard, CardGrid } from '@astrojs/starlight/components';
[Eleventy](https://11ty.dev) is an open-source static site generator that works with multiple template languages.
## Key Similarities between Eleventy (11ty) and Astro
Eleventy (11ty) and Astro share some similarities that will help you migrate your project:
- Both Astro and Eleventy are modern, JavaScript-based (Jamstack) site builders.
- Astro and Eleventy both allow you to use a [headless CMS, APIs or Markdown files for data](/en/guides/data-fetching/). You can continue to use your preferred content authoring system, and will be able to keep your existing content.
## Key Differences between Eleventy (11ty) and Astro
When you rebuild your Eleventy (11ty) site in Astro, you will notice some important differences:
- Eleventy supports a variety of templating languages. Astro supports [including components from several popular JS Frameworks (e.g. React, Svelte, Vue, Solid)](/en/guides/framework-components/), but uses [Astro layouts, pages and components](/en/basics/astro-components/) for most page templating.
- Astro uses a [`src/` directory](/en/basics/project-structure/#src) for all files, including site metadata, that are available for querying and processing during site build. Within this is a [special `src/pages/` folder for file-based routing](/en/basics/astro-pages/).
- Astro uses a [`public/` folder for static assets](/en/basics/project-structure/#public) that do not need to be processed nor transformed during the build.
- In Eleventy, bundling CSS, JavaScript, and other assets needs to be configured manually. [Astro handles this for you out-of-the-box](/en/concepts/why-astro/#easy-to-use).
## Switch from Eleventy to Astro
To convert an Eleventy blog to Astro, start with our blog theme starter template, or explore more community blog themes in our [theme showcase](https://astro.build/themes/).
You can pass a `--template` argument to the `create astro` command to start a new Astro project with one of our official starters. Or, you can [start a new project from any existing Astro repository on GitHub](/en/install-and-setup/#install-from-the-cli-wizard).
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm create astro@latest -- --template blog
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm create astro@latest --template blog
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn create astro --template blog
```
</Fragment>
</PackageManagerTabs>
Bring your existing Markdown (or MDX, with our optional integration) files as content to [create Markdown or MDX pages](/en/guides/markdown-content/).
Your Eleventy project allowed you to use a variety of templating languages to build your site. In an Astro project, your page templating will mostly be achieved with Astro components, which can be used as UI elements, layouts and even full pages. You may want to explore [Astro's component syntax](/en/basics/astro-components/) to see how to template in Astro using components.
To convert other types of sites, such as a portfolio or documentation site, see more official starter templates on [astro.new](https://astro.new). You'll find a link to each project's GitHub repository, as well as one-click links to open a working project in IDX, StackBlitz, CodeSandbox and Gitpod online development environments.
## Community Resources
<CardGrid>
<LinkCard title="This Site Is Now Built with Astro" href="https://aqandrew.com/blog/now-built-with-astro/" description="Why I switched from Eleventy."/>
<LinkCard title="Website Rewrite: 2025" href="https://www.welchcanavan.com/posts/site-rewrite-2025/" />
</CardGrid>
:::note[Have a resource to share?]
If you found (or made!) a helpful video or blog post about converting an Eleventy site to Astro, [add it to this list](https://github.com/withastro/docs/edit/main/src/content/docs/en/guides/migrate-to-astro/from-eleventy.mdx)!
:::
---
File: /src/content/docs/en/guides/migrate-to-astro/from-gatsby.mdx
---
---
title: Migrating from Gatsby
description: Tips for migrating an existing Gatsby project to Astro
sidebar:
label: Gatsby
type: migration
stub: false
framework: Gatsby
i18nReady: true
---
import { Steps, LinkCard, CardGrid } from '@astrojs/starlight/components';
import AstroJSXTabs from '~/components/tabs/AstroJSXTabs.astro';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
Here are some key concepts and migration strategies to help you get started. Use the rest of our docs and our [Discord community](https://astro.build/chat) to keep going!
## Key Similarities between Gatsby and Astro
Gatsby and Astro share some similarities that will help you migrate your project:
- The [syntax of `.astro` files is similar to JSX](/en/reference/astro-syntax/#jsx-like-expressions). Writing Astro should feel familiar.
- Astro has built-in support for [Markdown](/en/guides/markdown-content/) and an integration for using MDX files. Also, you can configure and continue to use many of your existing Markdown plugins.
- Astro also has an [official integration for using React components](/en/guides/integrations-guide/react/). Note that in Astro, React files **must** have a `.jsx` or `.tsx` extension.
- Astro has support for [installing NPM packages](/en/guides/imports/#npm-packages), including React libraries. Many of your existing dependencies will work in Astro.
- Like Gatsby, Astro projects can be SSG or [SSR with page-level prerendering](/en/guides/on-demand-rendering/).
## Key Differences between Gatsby and Astro
When you rebuild your Gatsby site in Astro, you will notice some important differences:
- Gatsby projects are React single-page apps and use `index.js` as your project's root. Astro projects are multi-page sites, and `index.astro` is your home page.
- [Astro components](/en/basics/astro-components/) are not written as exported functions that return page templating. Instead, you'll split your code into a "code fence" for your JavaScript and a body exclusively for the HTML you generate.
- [Local file data](/en/guides/imports/): Gatsby uses GraphQL to retrieve data from your project files. Astro uses ESM imports and top-level await functions (e.g. [`import.meta.glob()`](/en/guides/imports/#importmetaglob), [`getCollection()`](/en/guides/content-collections/#querying-collections)) to import data from your project files. You can manually add GraphQL to your Astro project but it is not included by default.
## Convert your Gatsby Project
Each project migration will look different, but there are some common actions you will perform when converting from Gatsby to Astro.
### Create a new Astro project
Use the `create astro` command for your package manager to launch Astro's CLI wizard or choose a community theme from the [Astro Theme Showcase](https://astro.build/themes).
You can pass a `--template` argument to the `create astro` command to start a new Astro project with one of our official starters (e.g. `docs`, `blog`, `portfolio`). Or, you can [start a new project from any existing Astro repository on GitHub](/en/install-and-setup/#install-from-the-cli-wizard).
<PackageManagerTabs>
<Fragment slot="npm">
```shell
# launch the Astro CLI Wizard
npm create astro@latest
# create a new project with an official example
npm create astro@latest -- --template <example-name>
```
</Fragment>
<Fragment slot="pnpm">
```shell
# launch the Astro CLI Wizard
pnpm create astro@latest
# create a new project with an official example
pnpm create astro@latest --template <example-name>
```
</Fragment>
<Fragment slot="yarn">
```shell
# launch the Astro CLI Wizard
yarn create astro@latest
# create a new project with an official example
yarn create astro@latest --template <example-name>
```
</Fragment>
</PackageManagerTabs>
Then, copy your existing Gatsby project files over to your new Astro project into a separate folder outside of `src`.
:::tip
Visit https://astro.new for the full list of official starter templates, and links for opening a new project in IDX, StackBlitz, CodeSandbox, or Gitpod.
:::
### Install integrations (optional)
You may find it useful to install some of [Astro's optional integrations](/en/guides/integrations-guide/) to use while converting your Gatsby project to Astro:
- **@astrojs/react**: to reuse some existing React UI components in your new Astro site or keep writing with React components.
- **@astrojs/mdx**: to bring existing MDX files from your Gatsby project, or to use MDX in your new Astro site.
### Put your code in `src`
Following [Astro's project structure](/en/basics/project-structure/):
<Steps>
1. **Delete** Gatsby's `public/` folder.
Gatsby uses the `public/` directory for its build output, so you can safely discard this folder. You will no longer need a built version of your Gatsby site. (Astro uses `dist/` by default for the build output.)
2. **Rename** Gatsby's `static/` folder to `public/`, and use it as Astro's `public/` folder.
Astro uses a folder called `public/` for static assets. You can alternatively copy the contents of `static/` into your existing Astro `public/` folder.
3. **Copy or Move** Gatsby's other files and folders (e.g. `components`, `pages`, etc.) as needed into your Astro `src/` folder as you rebuild your site, following [Astro's project structure](/en/basics/project-structure/).
Astro's `src/pages/` folder is a special folder used for file-based routing to create your site's pages and posts from `.astro`, `.md` and `.mdx` files. You will not have to configure any routing behavior for your Astro, Markdown, and MDX files.
All other folders are optional, and you can organize the contents of your `src/` folder any way you like. Other common folders in Astro projects include `src/layouts/`, `src/components`, `src/styles`, and `src/scripts`.
</Steps>
### Tips: Convert JSX files to `.astro` files
Here are some tips for converting a Gatsby `.js` component into a `.astro` component:
1. Use only the `return()` of the existing Gatsby component function as your HTML template.
2. Change any [Gatsby or JSX syntax to Astro syntax](#reference-convert-to-astro-syntax) or to HTML web standards. This includes `<Link to="">`, `{children}`, and `className`, for example.
3. Move any necessary JavaScript, including import statements, into a ["code fence" (`---`)](/en/basics/astro-components/#the-component-script). Note: JavaScript to [conditionally render content](/en/reference/astro-syntax/#dynamic-html) is often written inside the HTML template directly in Astro.
4. Use [`Astro.props`](/en/reference/api-reference/#props) to access any additional props that were previously passed to your Gatsby function.
5. Decide whether any imported components also need to be converted to Astro. With the official React integration installed, you can [use existing React components in your Astro files](/en/guides/framework-components/). But, you may want to convert them to `.astro` components, especially if they do not need to be interactive!
6. Remove any GraphQL queries. Instead, use import and [`import.meta.glob()`](/en/guides/imports/#importmetaglob) statements to query your local files.
See [an example from Gatsby's Blog starter template converted step-by-step](#guided-example-gatsby-layout-to-astro)
#### Compare: `.jsx` vs `.astro`
Compare the following Gatsby component and a corresponding Astro component:
<AstroJSXTabs>
<Fragment slot="jsx">
```jsx title="component.jsx"
import * as React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Header from "./header"
import Footer from "./footer"
import "./layout.css"
const Component = ({ message, children }) => {
const data = useStaticQuery(graphql`
query SiteTitleQuery {
site {
siteMetadata {
title
}
}
}
`)
return (
<>
<Header siteTitle={data.site.siteMetadata.title} />
<div style={{ margin: `0`, maxWidth: `960`}}>{message}</div>
<main>{children}</main>
<Footer siteTitle={data.site.siteMetadata} />
</>
)
}
export default Component
```
</Fragment>
<Fragment slot="astro">
```astro title="component.astro"
---
import Header from "./Header.astro"
import Footer from "./Footer.astro"
import "../styles/stylesheet.css"
import { site } from "../data/siteMetaData.js"
const { message } = Astro.props
---
<Header siteTitle={site.title} />
<div style="margin: 0; max-width: 960;">{message}</div>
<main>
<slot />
</main>
<Footer siteTitle={site.title} />
```
</Fragment>
</AstroJSXTabs>
### Migrating Layout Files
You may find it helpful to start by converting your Gatsby layouts and templates into [Astro layout components](/en/basics/layouts/).
Each Astro page explicitly requires `<html>`, `<head>`, and `<body>` tags to be present, so it is common to reuse a layout file across pages. Astro uses a [`<slot />`](/en/basics/astro-components/#slots) instead of React's `{children}` prop for page content, with no import statement required. Your Gatsby `layout.js` and templates will not include these.
Note the standard HTML templating, and direct access to `<head>`:
```astro title="src/layouts/Layout.astro" "slot"
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<title>Astro</title>
</head>
<body>
<!-- Wrap the slot element with your existing layout templating -->
<slot />
</body>
</html>
```
You may also wish to reuse code from Gatsby's `src/components/seo.js` to include additional site metadata. Notice that Astro uses neither `<Helmet>` nor `<Header>` but instead creates `<head>` directly. You may import and use components, even within `<head>`, to separate and organize your page content.
### Migrating Pages and Posts
In Gatsby, your [pages and posts](/en/basics/astro-pages/) may exist in `src/pages/` or outside of `src` in another folder, like `content`. In Astro, all your page content must live within `src/` unless you are using [content collections](/en/guides/content-collections/).
#### React Pages
Your existing Gatsby JSX (`.js`) pages will need to be [converted from JSX files to `.astro` pages](#tips-convert-jsx-files-to-astro-files). You cannot use an existing JSX page file in Astro.
These [`.astro` pages](/en/basics/astro-pages/) must be located within `src/pages/` and will have page routes generated automatically based on their file path.
#### Markdown and MDX pages
Astro has built-in support for Markdown and an optional integration for MDX files. Your existing [Markdown and MDX files](/en/guides/markdown-content/) can be reused but may require some adjustments to their frontmatter, such as adding [Astro's special `layout` frontmatter property](/en/basics/layouts/#markdown-layouts). They can also be placed within `src/pages/` to take advantage of automatic file-based routing.
Alternatively, you can use [content collections](/en/guides/content-collections/) in Astro to store and manage your content. You will retrieve the content yourself and [generate those pages dynamically](/en/guides/content-collections/#generating-routes-from-content).
### Migrating Tests
As Astro outputs raw HTML, it is possible to write end-to-end tests using the output of the build step. Any end-to-end tests written previously might work out-of-the-box if you have been able to match the markup of the older Gatsby site. Testing libraries such as Jest and React Testing Library can be imported and used in Astro to test your React components.
See Astro's [testing guide](/en/guides/testing/) for more.
### Repurpose config files
Gatsby has several top-level configuration files that also include site and page metadata and are used for routing. You will not use any of these `gatsby-*.js` files in your Astro project, but there may be some content that you can reuse as you build your Astro project:
- `gatsby-config.js`: Move your `siteMetadata: {}` into `src/data/siteMetadata.js` (or `siteMetadata.json`) to import data about your site (title, description, social accounts, etc.) into page layouts.
- `gatsby-browser.js`: Consider adding anything used here directly into your [main layout](#migrating-layout-files)'s `<head>` tag.
- `gatsby-node.js`: You will not need to create your own nodes in Astro, but viewing the schema in this file may help you with defining types in your Astro project.
- `gatsby-ssr.js`: If you choose to use SSR in Astro, you will [add and configure the SSR adapter](/en/guides/on-demand-rendering/) of your choice directly in `astro.config.mjs`.
## Reference: Convert to Astro Syntax
The following are some examples of Gatsby-specific syntax that you will need to convert to Astro. See more [differences between Astro and JSX](/en/reference/astro-syntax/#differences-between-astro-and-jsx) in the guide to writing Astro components.
### Gatsby Links to Astro
Convert any Gatsby `<Link to="">`, `<NavLink>` etc. components to HTML `<a href="">` tags.
```astro del={1} ins={2}
<Link to="/blog">Blog</Link>
<a href="/blog">Blog</a>
```
Astro does not use any special component for links, although you are welcome to build your own `<Link>` component. You can then import and use this `<Link>` just as you would any other component.
```astro title="src/components/Link.astro"
---
const { to } = Astro.props
---
<a href={to}><slot /></a>
```
### Gatsby Imports to Astro
If necessary, update any [file imports](/en/guides/imports/) to reference relative file paths exactly. This can be done using [import aliases](/en/guides/typescript/#import-aliases), or by writing out a relative path in full.
Note that `.astro` and several other file types must be imported with their full file extension.
```astro title="src/pages/authors/Fred.astro" ".astro"
---
import Card from `../../components/Card.astro`;
---
<Card />
```
### Gatsby Children Props to Astro
Convert any instances of `{children}` to an Astro `<slot />`. Astro does not need to receive `{children}` as a function prop and will automatically render child content in a `<slot />`.
```astro title="src/components/MyComponent" del={3-9} ins={11-13}
---
---
export default function MyComponent(props) {
return (
<div>
{props.children}
</div>
);
}
<div>
<slot />
</div>
```
React components that pass multiple sets of children can be migrated to an Astro component using [named slots](/en/basics/astro-components/#named-slots).
See more about [specific `<slot />` usage in Astro](/en/basics/astro-components/#slots).
### Gatsby Styling to Astro
You may need to replace any [CSS-in-JS libraries](https://github.com/withastro/astro/issues/4432) (e.g. styled-components) with other available CSS options in Astro.
If necessary, convert any inline style objects (`style={{ fontWeight: "bold" }}`) to inline HTML style attributes (`style="font-weight:bold;"`). Or, use an [Astro `<style>` tag](/en/guides/styling/#styling-in-astro) for scoped CSS styles.
```astro title="src/components/Card.astro" del={1} ins={2}
<div style={{backgroundColor: `#f4f4f4`, padding: `1em`}}>{message}</div>
<div style="background-color: #f4f4f4; padding: 1em;">{message}</div>
```
Tailwind is supported after installing the [Tailwind Vite plugin](/en/guides/styling/#tailwind). No changes to your existing Tailwind code are required!
Global styling is achieved in Gatsby using CSS imports in `gatsby-browser.js`. In Astro, you will import `.css` files directly into a main layout component to achieve global styles.
See more about [Styling in Astro](/en/guides/styling/).
### Gatsby Image Plugin to Astro
Convert Gatsby's `<StaticImage />` and `<GatsbyImage />` components to [Astro's own image integration components](/en/guides/images/), or to a [standard HTML `<img>` / JSX `<img />`](/en/guides/images/#images-in-ui-framework-components) tag as appropriate in your React components.
```astro title="src/pages/index.astro"
---
import { Image } from 'astro:assets';
import rocket from '../assets/rocket.png';
---
<Image src={rocket} alt="A rocketship in space." />
<img src={rocket.src} alt="A rocketship in space.">
```
Astro's `<Image />` component works in `.astro` and `.mdx` files only. See a [full list of its component attributes](/en/reference/modules/astro-assets/#image-properties) and note that several will differ from Gatsby's attributes.
To continue using [images in Markdown (`.md`) files](/en/guides/images/#images-in-markdown-files) using standard Markdown syntax (`![]()`), you may need to update the link. Using the HTML `<img>` tag directly is not supported in `.md` files for local images, and must be converted to Markdown syntax.
```md
<!-- src/pages/post-1.md -->
# My Markdown Page
<!-- Local image stored at src/assets/stars.png -->
![A starry night sky.](../assets/stars.png)
```
In React (`.jsx`) components, use standard JSX image syntax (`<img />`). Astro will not optimize these images, but you can install and use NPM packages for more flexibility.
You can learn more about [using images in Astro](/en/guides/images/) in the Images Guide.
### Gatsby GraphQL to Astro
Remove all references to GraphQL queries, and instead use [`import.meta.glob()`](/en/guides/imports/#importmetaglob) to access data from your local files.
Or, if using content collections, query your Markdown and MDX files using [`getEntry()` and `getCollection()`](/en/guides/content-collections/#generating-routes-from-content).
These data requests are made in the frontmatter of the Astro component using the data.
```astro title="src/pages/index.astro" del={2,12-28}
---
import { graphql } from "gatsby"
import { getCollection } from 'astro:content';
// Get all `src/content/blog/` entries
const allBlogPosts = await getCollection('blog');
// Get all `src/pages/posts/` entries
const allPosts = Object.values(import.meta.glob('../pages/post/*.md', { eager: true }));
---
export const pageQuery = graphql`
{
allMarkdownRemark(sort: { frontmatter: { date: DESC } }) {
nodes {
excerpt
fields {
slug
}
frontmatter {
date(formatString: "MMMM DD, YYYY")
title
description
}
}
}
}
`
```
## Guided example: Gatsby layout to Astro
This example converts the main project layout (`layout.js`) from Gatsby's blog starter to `src/layouts/Layout.astro`.
This page layout shows one header when visiting the home page, and a different header with a link back to Home for all other pages.
<Steps>
1. Identify the `return()` JSX.
```jsx {21-29} title="layout.js"
import * as React from "react"
import { Link } from "gatsby"
const Layout = ({ location, title, children }) => {
const rootPath = `${__PATH_PREFIX__}/`
const isRootPath = location.pathname === rootPath
let header
if (isRootPath) {
header = (
<h1 className="main-heading">
<Link to="/">{title}</Link>
</h1>
)
} else {
header = (
<Link className="header-link-home" to="/">
Home
</Link>
)
}
return (
<div className="global-wrapper" data-is-root-path={isRootPath}>
<header className="global-header">{header}</header>
<main>{children}</main>
<footer>
© {new Date().getFullYear()}, Built with
{` `}
<a href="https://www.gatsbyjs.com">Gatsby</a>
</footer>
</div>
)
}
export default Layout
```
2. Create `Layout.astro` and add this `return` value, [converted to Astro syntax](#reference-convert-to-astro-syntax).
Note that:
- `{new Date().getFullYear()}` just works 🎉
- `{children}` becomes `<slot />` 🦥
- `className` becomes `class` 📛
- `Gatsby` becomes `Astro` 🚀
```astro title="src/layouts/Layout.astro" "<slot />" "class" "Astro" "astro.build"
---
---
<div class="global-wrapper" data-is-root-path={isRootPath}>
<header class="global-header">{header}</header>
<main><slot /></main>
<footer>
© {new Date().getFullYear()}, Built with
{` `}
<a href="https://www.astro.build">Astro</a>
</footer>
</div>
```
3. Add a page shell so that your layout provides each page with the necessary parts of an HTML document:
```astro title="src/layouts/Layout.astro" ins={3-10,22-23}
---
---
<html>
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<title>Astro</title>
</head>
<body>
<div class="global-wrapper" data-is-root-path={isRootPath}>
<header class="global-header">{header}</header>
<main>
<slot />
</main>
<footer>
&#169; {new Date().getFullYear()}, Built with
{` `}
<a href="https://www.astro.build">Astro</a>
</footer>
</div>
</body>
</html>
```
4. Add any needed imports, props, and JavaScript
To conditionally render a header based on the page route and title in Astro:
- Provide the props via `Astro.props`. (Remember: your Astro templating accesses props from its frontmatter, not passed into a function.)
- Use a ternary operator to show one heading if this is the home page, and a different heading otherwise.
- Remove variables for `{header}` and `{isRootPath}` as they are no longer needed.
- Replace Gatsby's `<Link/>` tags with `<a>` anchor tags.
- Use `class` instead of `className`.
- Import a local stylesheet from your project for the class names to take effect.
```astro title="src/layouts/Layout.astro" ins={2-3, 15, 16, 20, 24} "class" "<a" "</a>"
---
import '../styles/style.css';
const { title, pathname } = Astro.props
---
<html>
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<title>Astro</title>
</head>
<body>
<div class="global-wrapper">
<header class="global-header">
{ pathname === "/"
?
<h1 class="main-heading">
<a href="/">{title}</a>
</h1>
:
<h1 class="main-heading">
<a class="header-link-home" href="/">Home</a>
</h1>
}
</header>
<main>
<slot />
</main>
<footer>
&#169; {new Date().getFullYear()}, Built with
{` `}
<a href="https://www.astro.build">Astro</a>
</footer>
</div>
</body>
</html>
```
5. Update `index.astro` to use this new layout and pass it the necessary `title` and `pathname` props:
```astro title="src/pages/index.astro"
---
import Layout from '../layouts/Layout.astro';
const pagePathname = Astro.url.pathname
---
<Layout title="Home Page" pathname={pagePathname}>
<p>Astro</p>
</Layout>
```
:::tip
You can [get the current page's path using `Astro.url`](/en/reference/api-reference/#url).
:::
6. To test the conditional header, create a second page, `about.astro` using the same pattern:
```astro title="src/pages/about.astro"
---
import Layout from '../layouts/Layout.astro';
const pagePathname = Astro.url.pathname
---
<Layout title="About" pathname={pagePathname}>
<p>About</p>
</Layout>
```
You should see a link to "Home" only when visiting the About page.
</Steps>
## Community Resources
<CardGrid>
<LinkCard title="Migrating from Gatsby to Astro" href="https://loige.co/migrating-from-gatsby-to-astro/"
description="How and why I migrated this blog from Gatsby to Astro and what I learned in the process." />
<LinkCard title="Migrating to Astro was EZ" href="https://joelhooks.com/migrating-to-astro-was-ez"
description="This is about the process of migrating from Gatsby to Astro, and why I chose Astro." />
<LinkCard title="My Switch from Gatsby to Astro" href="https://www.joshfinnie.com/blog/my-switch-from-gatsby-to-astro/"
description="The switch to Astro is definitely worth a blog post! It’s revolutionizing the static web development scene for the better."/>
<LinkCard title="Why I moved to Astro from Gatsby" href="https://dev.to/askrodney/why-i-moved-to-astro-from-gatsby-3fck"
description="Taking a quick look at what made me want to switch and why Astro was a good fit." />
<LinkCard title="Another Migration: From Gatsby to Astro" href="https://logarithmicspirals.com/blog/migrating-from-gatsby-to-astro/"
description="Learn about how I transitioned my personal website from Gatsby to Astro as I share insights and experiences from the migration process."/>
<LinkCard title="From Gatsby gridlock to Astro bliss: my personal site redesign" href="https://jwn.gr/posts/migrating-from-gatsby-to-astro/"
description="Gatsby has shown its age and I found myself seeking a modern alternative. Enter Astro — a framework that has breathed some new life into this site."/>
<LinkCard title="Why and how I moved my blog away from Gatsby and React to Astro Js and Preact" href="https://www.helmerdavila.com/blog/en/why-and-how-i-moved-my-blog-away-from-gatsby-and-react-to-astro-js-and-preact"
description="All is about simplicity and power at the same time." />
<LinkCard title="How I rewrote my HUGE Gatsby site in Astro and learned to love it in the process" href="https://dunedinsound.com/blog/how_i_rewrote_my_huge_gatsby_site_in_astro_and_learned_to_love_it_in_the_process/"
description="Everything is faster. Happier. More productive."/>
<LinkCard title="How I switched from Gatsby to Astro (While Keeping Drupal in the Mix)" href="https://albert.skibinski.nl/en/blog/how-i-switched-gatsby-astro-while-keeping-drupal-mix/"
description="I came across the relatively new Astro, which ticked all the boxes."/>
<LinkCard title="Migrating my website from Gatsby to Astro" href="https://dev.to/flashblaze/migrating-my-website-from-gatsby-to-astro-2ej5"
description="Astro has entered the chat." />
<LinkCard title="Gatsby to Astro" href="https://alvin.codes/writing/gatsby-to-astro"
description="Why and how I migrated this website from Gatsby to Astro."/>
</CardGrid>
:::note[Have a resource to share?]
If you found (or made!) a helpful video or blog post about converting a Gatsby site to Astro, [add it to this list](https://github.com/withastro/docs/edit/main/src/content/docs/en/guides/migrate-to-astro/from-gatsby.mdx)!
:::
---
File: /src/content/docs/en/guides/migrate-to-astro/from-gitbook.mdx
---
---
title: Migrating from GitBook
description: Tips for migrating an existing GitBook project to Astro
sidebar:
label: GitBook
type: migration
stub: true
framework: GitBook
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
[GitBook](https://gitbook.com) is a web-based platform for creating and publishing documentation and books in a collaborative manner, with version control integration and customizable features.
## Key Similarities between GitBook and Astro
GitBook and Astro share some similarities that will help you migrate your project:
- Both Astro and GitBook support [Markdown](/en/guides/markdown-content/). You can migrate all your existing documentation utilizing GitBook's Git Sync feature.
- Both Astro and GitBook use some form of [file-based routing](/en/guides/routing/). Using Astro's file structure for your existing content and when adding new pages should feel familiar.
## Key Differences between GitBook and Astro
When you migrate your GitBook docs to Astro, you will notice some important differences:
- A GitBook site is edited using an online dashboard. In Astro, you will use a [code editor](/en/editor-setup/) and development environment to maintain your site. You can develop locally on your machine, or choose a cloud editor/development environment like IDX, StackBlitz, CodeSandbox, or Gitpod.
- GitBook stores your content in a database. In Astro, you will have individual files (typically Markdown or MDX) in your [project directory](/en/basics/project-structure/) for each page's content. Or, you can choose to use a [CMS for your content](/en/guides/cms/) and use Astro to fetch and present the data.
- GitBook uses a custom syntax on top of Markdown for content. Astro supports Markdoc via the optional [Markdoc integration](/en/guides/integrations-guide/markdoc/), which features a similar syntax to the one you would use in GitBook.
## Switch from GitBook to Astro
To convert a GitBook documentation site to Astro, start with our official [Starlight docs theme starter template](https://starlight.astro.build), or explore more community docs themes in our [theme showcase](https://astro.build/themes?search=&categories%5B%5D=docs).
You can pass a `--template` argument to the `create astro` command to start a new Astro project with one of our official starters. Or, you can [start a new project from any existing Astro repository on GitHub](/en/install-and-setup/#install-from-the-cli-wizard).
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm create astro@latest -- --template starlight
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm create astro@latest --template starlight
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn create astro --template starlight
```
</Fragment>
</PackageManagerTabs>
Once you have a new Astro project, you can sync your existing GitBook content to your new Astro project. GitBook has a [Git Sync feature](https://docs.gitbook.com/product-tour/git-sync) that will automatically sync your GitBook content to a GitHub/GitLab repository.
To sync directly to the docs template's content collection, specify `src/content/docs/en` or `src/content/docs` as the project directory.
:::caution
When enabling Git Sync be sure to specify "**GitBook to GitHub**" as the priority. This will ensure that your GitBook content is synced to your GitHub repository. Otherwise, you will overwrite your existing GitBook content.
:::
After syncing the content, you will now have a copy of your GitBook content in your Astro repository. Disable git sync to prevent future syncing with GitBook.
Note that although you now have your content migrated to your Astro project, it will not be immediately usable. To use this content in your Astro site, you will need to spend some time manually changing GitBook's syntax into a format compatible with Astro. In particular:
- Astro's [Markdoc integration](/en/guides/integrations-guide/markdoc/) requires that the file extension be `.mdoc`. This is to avoid conflicts with other Markdown extensions like `.mdx` and `.md`.
- GitBook syntax differs from Markdoc where the `/` prefix denoting a closing tag is replaced with `end` for GitBook files. You will need to update this notation throughout your files.
- Some features of GitBook rely on custom components. These components will not exist in Astro and must be created and added to your project through [Markdoc's config `tags` attribute](/en/guides/integrations-guide/markdoc/#use-astro-components-as-markdoc-tags) or removed from your files.
## Community Resources
:::note[Have a resource to share?]
If you found (or made!) a helpful video or blog post about converting a GitBook site to Astro, [add it to this list](https://github.com/withastro/docs/edit/main/src/content/docs/en/guides/migrate-to-astro/from-gitbook.mdx)!
:::
---
File: /src/content/docs/en/guides/migrate-to-astro/from-gridsome.mdx
---
---
title: Migrating from Gridsome
description: Tips for migrating an existing Gridsome project to Astro
sidebar:
label: Gridsome
type: migration
stub: true
framework: Gridsome
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import { CardGrid, LinkCard } from '@astrojs/starlight/components';
[Gridsome](https://gridsome.org) is an open-source static site generator built on Vue and GraphQL.
## Key Similarities between Gridsome and Astro
Gridsome and Astro share some similarities that will help you migrate your project:
- Both Gridsome and Astro are modern JavaScript static-site generators with similar [project file structures](/en/basics/project-structure/#directories-and-files).
- Both Gridsome and Astro use a `src/` folder for your project files and a [special `src/pages/` folder for file-based routing](/en/basics/astro-pages/). Creating and managing pages for your site should feel familiar.
- Astro has [an official integration for using Vue components](/en/guides/integrations-guide/vue/) and supports [installing NPM packages](/en/guides/imports/#npm-packages), including several for Vue. You will be able to write Vue UI components, and may be able to keep some or all of your existing Gridsome Vue components and dependencies.
- Astro and Gridsome both allow you to use a [headless CMS, APIs or Markdown files for data](/en/guides/data-fetching/). You can continue to use your preferred content authoring system, and will be able to keep your existing content.
## Key Differences between Gridsome and Astro
When you rebuild your Gridsome site in Astro, you will notice some important differences:
- Gridsome is a Vue-based single-page application (SPA). Astro sites are multi-page apps built using [`.astro` components](/en/basics/astro-components/), but can also support [React, Preact, Vue.js, Svelte, SolidJS, AlpineJS](/en/guides/framework-components/) and raw HTML templating.
- As an SPA, Gridsome uses `vue-router` for SPA routing, and `vue-meta` for managing `<head>`. In Astro, you will create separate HTML pages and control your page `<head>` directly, or in a [layout component](/en/basics/layouts/).
- [Local file data](/en/guides/imports/): Gridsome uses GraphQL to retrieve data from your project files. Astro uses ESM imports and [`import.meta.glob()`](/en/guides/imports/#importmetaglob) to import data from local project files. Remote resources can be loaded using the standard `fetch()` API. GraphQL may be optionally added to your project, but is not included by default.
## Switch from Gridsome to Astro
To convert a Gridsome blog to Astro, start with our blog theme starter template, or explore more community blog themes in our [theme showcase](https://astro.build/themes/).
You can pass a `--template` argument to the `create astro` command to start a new Astro project with one of our official starters. Or, you can [start a new project from any existing Astro repository on GitHub](/en/install-and-setup/#install-from-the-cli-wizard).
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm create astro@latest -- --template blog
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm create astro@latest --template blog
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn create astro --template blog
```
</Fragment>
</PackageManagerTabs>
Bring your existing Markdown (or MDX, with our optional integration) files as content to [create Markdown or MDX pages](/en/guides/markdown-content/).
Since Gridsome's project structure is similar to Astro's, you may be able to copy several existing files from your project into the same location in your new Astro project. However, the two project structures are not identical. You may want to examine [Astro's project structure](/en/basics/project-structure/) to see what the differences are.
Since Astro queries and imports your local files differently than Gridsome, you may want to read about how to load files using [`import.meta.glob()`](/en/guides/imports/#importmetaglob) to understand how to work with your local files.
To convert other types of sites, such as a portfolio or documentation site, see more official starter templates on [astro.new](https://astro.new). You'll find a link to each project's GitHub repository, as well as one-click links to open a working project in IDX, StackBlitz, CodeSandbox and Gitpod online development environments.
## Community Resources
<CardGrid>
<LinkCard title="Migration from Gridsome to Astro" href="https://fyodor.io/migration-from-gridsome-to-astro/"/>
<LinkCard title="Hello Astro!" href="https://thamas.hu/astro-hello"/>
</CardGrid>
:::note[Have a resource to share?]
If you found (or made!) a helpful video or blog post about converting a Gridsome site to Astro, [add it to this list](https://github.com/withastro/docs/edit/main/src/content/docs/en/guides/migrate-to-astro/from-gridsome.mdx)!
:::
---
File: /src/content/docs/en/guides/migrate-to-astro/from-hugo.mdx
---
---
title: Migrating from Hugo
description: Tips for migrating an existing Hugo project to Astro
sidebar:
label: Hugo
type: migration
stub: true
framework: Hugo
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import { CardGrid, LinkCard } from '@astrojs/starlight/components';
[Hugo](https://gohugo.io) is an open-source static site generator built on Go.
## Key Similarities between Hugo and Astro
Hugo and Astro share some similarities that will help you migrate your project:
- Hugo and Astro are both modern static-site generators, ideally suited to [content-driven websites](/en/concepts/why-astro/#content-driven) like blogs.
- Hugo and Astro both allow you to [author your content in Markdown](/en/guides/markdown-content/). However, Hugo includes several special frontmatter properties and allows you to write frontmatter in YAML, TOML or JSON. Even though many of your existing Hugo frontmatter properties will not be "special" in Astro, you can continue to use your existing Markdown files and YAML (or TOML) frontmatter values.
- Hugo and Astro both allow you to enhance your site with a variety of [integrations and external packages](https://astro.build/integrations/).
## Key Differences between Hugo and Astro
When you rebuild your Hugo site in Astro, you will notice some important differences:
- Hugo uses Go Templating for page templating. [Astro syntax](/en/basics/astro-components/) is a JSX-like superset of HTML.
- Astro does not use shortcodes for dynamic content in standard Markdown files, but [Astro's MDX integration](/en/guides/integrations-guide/mdx/) does allow you to use JSX and import components in MDX files.
- While Hugo can use "partials" for reusable layout elements, [Astro is entirely component-based](/en/basics/astro-components/). Any `.astro` file can be a component, a layout or an entire page, and can import and render any other Astro components. Astro components can also include [other UI framework components (e.g. React, Svelte, Vue, Solid)](/en/guides/framework-components/) as well as content or metadata from [other files in your project](/en/guides/imports/), such as Markdown or MDX.
## Switch from Hugo to Astro
To convert a Hugo blog to Astro, start with our blog theme starter template, or explore more community blog themes in our [theme showcase](https://astro.build/themes/).
You can pass a `--template` argument to the `create astro` command to start a new Astro project with one of our official starters. Or, you can [start a new project from any existing Astro repository on GitHub](/en/install-and-setup/#install-from-the-cli-wizard).
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm create astro@latest -- --template blog
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm create astro@latest --template blog
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn create astro --template blog
```
</Fragment>
</PackageManagerTabs>
Bring your existing Markdown (or MDX, with our optional integration) files as content to [create Markdown or MDX pages](/en/guides/markdown-content/). Astro allows YAML or TOML frontmatter in these files, so if you are using JSON frontmatter, you will need to convert it.
To continue to use dynamic content such as variables, expressions or UI components within your Markdown content, add Astro's optional MDX integration and convert your existing Markdown files to [MDX pages](/en/guides/markdown-content/). MDX supports YAML and TOML frontmatter, so you can keep your existing frontmatter properties. But, you must replace any shortcode syntax with [MDX's own syntax](https://mdxjs.com/docs/what-is-mdx/#mdx-syntax), which allows JSX expressions and/or component imports.
To convert other types of sites, such as a portfolio or documentation site, see more official starter templates on [astro.new](https://astro.new). You'll find a link to each project's GitHub repository, as well as one-click links to open a working project in IDX, StackBlitz, CodeSandbox and Gitpod online development environments.
## Community Resources
<CardGrid>
<LinkCard title="Elio Struyf's migration story from Hugo to Astro" href="https://www.eliostruyf.com/migration-story-hugo-astro/"/>
<LinkCard title="Hugo Vs Astro - Which Static Site Generator To Choose In 2023" href="https://onebite.dev/hugo-vs-astro-which-static-site-generator-to-choose-in-2023/"/>
<LinkCard title="Lessons from an AI-assisted migration to Astro" href="https://bennet.org/blog/lessons-from-ai-assisted-migration-to-astro/" />
</CardGrid>
:::note[Have a resource to share?]
If you found (or made!) a helpful video or blog post about converting a Hugo site to Astro, [add it to this list](https://github.com/withastro/docs/edit/main/src/content/docs/en/guides/migrate-to-astro/from-hugo.mdx)!
:::
---
File: /src/content/docs/en/guides/migrate-to-astro/from-jekyll.mdx
---
---
title: Migrating from Jekyll
description: Tips for migrating an existing Jekyll project to Astro
sidebar:
label: Jekyll
type: migration
stub: true
framework: Jekyll
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import { LinkCard, CardGrid } from '@astrojs/starlight/components';
[Jekyll](https://jekyllrb.com) is a static site generator built on Ruby.
## Key Similarities between Jekyll and Astro
Jekyll and Astro share some similarities that will help you migrate your project:
- Both Jekyll and Astro are static-site generators, commonly used to create blogs.
- Both Jekyll and Astro allow you to write your content in Markdown and HTML. Jekyll and Astro both provide some special frontmatter YAML properties for page layout and unpublished draft posts. You can continue to use your existing Markdown files in Astro.
- Both Jekyll and Astro use [file-based routing](/en/guides/routing/) for creating pages from your blog posts. Astro provides a [special `src/pages/` directory for all pages and posts](/en/basics/project-structure/#srcpages). Jekyll uses a similar special folder called `_posts/` for your Markdown blog posts, however your site pages can exist elsewhere. Creating new blog posts should feel familiar.
## Key Differences between Jekyll and Astro
When you rebuild your Jekyll site in Astro, you will notice some important differences:
- As Jekyll is primarily a blogging platform, several blog features are built-in that you may have to build yourself in Astro. Or, choose a [blog starter template theme](https://astro.build/themes?search=&categories%5B%5D=blog) that includes these features. For example, Jekyll has built-in support for tags and categories which you will find in several Astro blog themes, but is not included in a minimal Astro project.
- Jekyll uses Liquid templates for reusable layout elements and templating. Astro uses JSX-like [`.astro` files for templating and components](/en/basics/astro-components/). Any `.astro` file can be a component, a layout or an entire page, and can import and render any other Astro components. You can also build using [other UI framework components (e.g. React, Svelte, Vue, Solid)](/en/guides/framework-components/) as well as content or metadata from [other files in your project](/en/guides/imports/), such as Markdown or MDX.
## Switch from Jekyll to Astro
To convert a Jekyll blog to Astro, start with our blog theme starter template, or explore more community blog themes in our [theme showcase](https://astro.build/themes/).
You can pass a `--template` argument to the `create astro` command to start a new Astro project with one of our official starters. Or, you can [start a new project from any existing Astro repository on GitHub](/en/install-and-setup/#install-from-the-cli-wizard).
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm create astro@latest -- --template blog
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm create astro@latest --template blog
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn create astro --template blog
```
</Fragment>
</PackageManagerTabs>
Bring your existing Markdown files as content to [create Markdown pages](/en/guides/markdown-content/), using an [Astro Markdown layout](/en/basics/layouts/#markdown-layouts) instead of a Liquid template.
Much of your existing HTML page content can be converted into [Astro pages](/en/basics/astro-pages/), and you will additionally be able to [use variables, JSX-like expressions and component imports directly in your HTML templating](/en/reference/astro-syntax/#jsx-like-expressions).
Astro does not have a `permalink` property that accepts placeholders. You may need to read more about [Astro's page routing](/en/guides/routing/) if you want to keep your existing URL structure. Or, consider [setting redirects at a host like Netlify](https://docs.netlify.com/routing/redirects/).
To convert other types of sites, such as a portfolio or documentation site, see more official starter templates on [astro.new](https://astro.new). You'll find a link to each project's GitHub repository, as well as one-click links to open a working project in IDX, StackBlitz, CodeSandbox and Gitpod online development environments.
## Community Resources
<CardGrid>
<LinkCard title="From Jekyll to Astro" href="https://jackcarey.co.uk/posts/astro-rewrite/"/>
<LinkCard title="Goodbye Jekyll, Hello Astro" href="https://kiranrao.in/blog/bye-jekyll-hello-astro/" />
<LinkCard title="Back to the Future: Our Tech Blog's Transition from Jekyll to Astro" href="https://alasco.tech/2023/09/06/migrating-to-astro"/>
</CardGrid>
:::note[Have a resource to share?]
If you found (or made!) a helpful video or blog post about converting a Jekyll site to Astro, [add it to this list](https://github.com/withastro/docs/edit/main/src/content/docs/en/guides/migrate-to-astro/from-jekyll.mdx)!
:::
---
File: /src/content/docs/en/guides/migrate-to-astro/from-nextjs.mdx
---
---
title: Migrating from Next.js
description: Tips for migrating an existing Next.js project to Astro
sidebar:
label: Next.js
type: migration
stub: false
framework: Next.js
i18nReady: true
---
import { Steps, LinkCard, CardGrid } from '@astrojs/starlight/components';
import AstroJSXTabs from '~/components/tabs/AstroJSXTabs.astro';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
Here are some key concepts and migration strategies to help you get started. Use the rest of our docs and our [Discord community](https://astro.build/chat) to keep going!
## Key Similarities between Next.js and Astro
Next.js and Astro share some similarities that will help you migrate your project:
- The [syntax of `.astro` files is similar to JSX](/en/reference/astro-syntax/#differences-between-astro-and-jsx). Writing Astro should feel familiar.
- Astro projects can also be SSG or [SSR with page-level prerendering](/en/guides/on-demand-rendering/).
- Astro uses file-based routing, and [allows specially named pages to create dynamic routes](/en/guides/routing/#dynamic-routes).
- Astro is [component-based](/en/basics/astro-components/), and your markup structure will be similar before and after your migration.
- Astro has [official integrations for React, Preact, and Solid](/en/guides/integrations-guide/react/) so you can use your existing JSX components. Note that in Astro, these files **must** have a `.jsx` or `.tsx` extension.
- Astro has support for [installing NPM packages](/en/guides/imports/#npm-packages), including React libraries. Many of your existing dependencies will work in Astro.
## Key Differences between Next.js and Astro
When you rebuild your Next.js site in Astro, you will notice some important differences:
- Next.js is a React single-page app, and uses `index.js` as your project's root. Astro is a multi-page site, and `index.astro` is your home page.
- [`.astro` components](/en/basics/astro-components/) are not written as exported functions that return page templating. Instead, you'll split your code into a "code fence" for your JavaScript and a body exclusively for the HTML you generate.
- [content-driven](/en/concepts/why-astro/#content-driven): Astro was designed to showcase your content and to allow you to opt-in to interactivity only as needed. An existing Next.js app might be built for high client-side interactivity and may require advanced Astro techniques to include items that are more challenging to replicate using `.astro` components, such as dashboards.
## Convert your Next.js Project
Each project migration will look different, but there are some common actions you will perform when converting from Next.js to Astro.
### Create a new Astro project
Use the `create astro` command for your package manager to launch Astro's CLI wizard or choose a community theme from the [Astro Theme Showcase](https://astro.build/themes).
You can pass a `--template` argument to the `create astro` command to start a new Astro project with one of our official starters (e.g. `docs`, `blog`, `portfolio`). Or, you can [start a new project from any existing Astro repository on GitHub](/en/install-and-setup/#install-from-the-cli-wizard).
<PackageManagerTabs>
<Fragment slot="npm">
```shell
# launch the Astro CLI Wizard
npm create astro@latest
# create a new project with an official example
npm create astro@latest -- --template <example-name>
```
</Fragment>
<Fragment slot="pnpm">
```shell
# launch the Astro CLI Wizard
pnpm create astro@latest
# create a new project with an official example
pnpm create astro@latest --template <example-name>
```
</Fragment>
<Fragment slot="yarn">
```shell
# launch the Astro CLI Wizard
yarn create astro@latest
# create a new project with an official example
yarn create astro@latest --template <example-name>
```
</Fragment>
</PackageManagerTabs>
Then, copy your existing Next project files over to your new Astro project in a separate folder outside of `src`.
:::tip
Visit https://astro.new for the full list of official starter templates, and links for opening a new project in IDX, StackBlitz, CodeSandbox, or Gitpod.
:::
### Install integrations (optional)
You may find it useful to install some of [Astro's optional integrations](/en/guides/integrations-guide/) to use while converting your Next project to Astro:
- **@astrojs/react**: to reuse some existing React UI components in your new Astro site, or keep writing with React components.
- **@astrojs/mdx**: to bring existing MDX files from your Next project, or to use MDX in your new Astro site.
### Put your source code in `src`
Following [Astro's project structure](/en/basics/project-structure/):
<Steps>
1. **Keep** Next's `public/` folder untouched.
Astro uses the `public/` directory for static assets, just like Next. There is no change needed to this folder, nor its contents.
2. **Copy or Move** Next's other files and folders (e.g. `pages`, `styles` etc.) into Astro's `src/` folder as you rebuild your site, following [Astro's project structure](/en/basics/project-structure/).
Like Next, Astro's `src/pages/` folder is a special folder used for file-based routing. All other folders are optional, and you can organize the contents of your `src/` folder any way you like. Other common folders in Astro projects include `src/layouts/`, `src/components`, `src/styles`, `src/scripts`.
</Steps>
### The Astro config file
Astro has a configuration file at the root of your project called [`astro.config.mjs`](/en/guides/configuring-astro/). This is used only for configuring your Astro project and any installed integrations, including [SSR adapters](/en/guides/deploy/).
### Tips: Convert JSX files to `.astro` files
Here are some tips for converting a Next `.js` component into a `.astro` component:
1. Use the returned JSX of the existing Next.js component function as the basis for your HTML template.
2. Change any [Next or JSX syntax to Astro](#reference-convert-nextjs-syntax-to-astro) or to HTML web standards. This includes `<Link>`, `<Script>`, `{children}`, and `className`, for example.
3. Move any necessary JavaScript, including import statements, into a ["code fence" (`---`)](/en/basics/astro-components/#the-component-script). Note: JavaScript to [conditionally render content](/en/reference/astro-syntax/#dynamic-html) is often written inside the HTML template directly in Astro.
4. Use [`Astro.props`](/en/reference/api-reference/#props) to access any additional props that were previously passed to your Next function.
5. Decide whether any imported components also need to be converted to Astro. With the official integration installed, you can [use existing React components in your Astro file](/en/guides/framework-components/). But, you may want to convert them to `.astro` components, especially if they do not need to be interactive!
6. Replace `getStaticProps()` with import statements or [`import.meta.glob()`](/en/guides/imports/#importmetaglob) to query your local files. Use `fetch()` to fetch external data.
See [an example of a Next `.js` file converted step-by-step](#guided-example-next-data-fetching-to-astro).
#### Compare: JSX vs Astro
Compare the following Next component and a corresponding Astro component:
<AstroJSXTabs>
<Fragment slot="jsx">
```jsx title="StarCount.jsx"
import Header from "./header";
import Footer from "./footer";
import "./layout.css";
export async function getStaticProps() {
const res = await fetch("https://api.github.com/repos/withastro/astro");
const json = await res.json();
return {
props: { message: json.message, stars: json.stargazers_count || 0 },
}
}
const Component = ({ stars, message }) => {
return (
<>
<Header />
<p style={{
backgroundColor: `#f4f4f4`,
padding: `1em 1.5em`,
textAlign: `center`,
marginBottom: `1em`
}}>Astro has {stars} 🧑‍🚀</p>
<Footer />
</>
)
}
export default Component;
```
</Fragment>
<Fragment slot="astro">
```astro title="StarCount.astro"
---
import Header from "./header";
import Footer from "./footer";
import "./layout.css";
const res = await fetch("https://api.github.com/repos/withastro/astro");
const json = await res.json();
const message = json.message;
const stars = json.stargazers_count || 0;
---
<Header />
<p class="banner">Astro has {stars} 🧑‍🚀</p>
<Footer />
<style>
.banner {
background-color: #f4f4f4;
padding: 1em 1.5em;
text-align: center;
margin-bottom: 1em;
}
<style>
```
</Fragment>
</AstroJSXTabs>
### Migrating Layout Files
You may find it helpful to start by converting your Next.js layouts and templates into [Astro layout components](/en/basics/layouts/).
Next has two different methods for creating layout files, each of which handles layouts differently than Astro:
- The `pages` directory
- [The `/app` directory](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#layouts)
Each Astro page explicitly requires `<html>`, `<head>`, and `<body>` tags to be present, so it is common to reuse a layout file across pages. Astro uses a [`<slot />`](/en/basics/astro-components/#slots) for page content, with no import statement required. Note the standard HTML templating, and direct access to `<head>`:
```astro title="src/layouts/Layout.astro"
---
---
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Astro</title>
</head>
<body>
<!-- Wrap the slot element with your existing layout templating -->
<slot />
</body>
</html>
```
#### Migrating from Next.js' `pages` directory
Your Next project may have a `pages/_document.jsx` file that imports React components to customize your app's `<head>`:
```jsx title="pages/_document.jsx"
import Document, { Html, Head, Main, NextScript } from "next/document";
export default class MyDocument extends Document {
render() {
return (
<Html lang="en">
<Head>
<link rel="icon" href="/favicon.ico" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
```
<Steps>
1. Make a new Astro layout file using only the returned JSX.
2. Replace any React components with `<html>`, `<head>`, `<slot>`, and other HTML standard tags.
```astro title="src/layouts/Document.astro"
<html lang="en">
<head>
<link rel="icon" href="/favicon.ico" />
</head>
<body>
<slot/>
</body>
</html>
```
</Steps>
#### Migrating from Next.js' `/app` directory
Next.js' `app/` directory layout files are created with two files: a `layout.jsx` file to customize the `<html>` and `<body>` contents, and a `head.jsx` file to customize the `<head>` element contents.
```jsx title="app/layout.jsx"
export default function Layout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
```
```jsx title="app/head.jsx"
export default function Head() {
return (
<>
<title>My Page</title>
</>
);
}
```
<Steps>
1. Make a new Astro layout file using only the returned JSX.
2. Replace both these files with a single Astro layout file that contains a page shell (`<html>`, `<head>`, and `<body>` tags) and a `<slot/>` instead of React's `{children}` prop:
```astro title="src/layouts/Layout.astro"
<html lang="en">
<head>
<title>My Page</title>
</head>
<body>
<slot/>
</body>
</html>
```
</Steps>
### Migrating Pages and Posts
In Next.js, your posts either live in `/pages` or `/app/routeName/page.jsx`.
In Astro, all your page content must live within `src/` unless you are using [content collections](/en/guides/content-collections/).
#### React pages
Your existing Next JSX (`.js`) pages will need to be [converted from JSX files to `.astro` pages](#tips-convert-jsx-files-to-astro-files). You cannot use an existing JSX page file in Astro.
These [`.astro` pages](/en/basics/astro-pages/) must be located within `src/pages/` and will have page routes generated automatically based on their file path.
#### Markdown and MDX pages
Astro has built-in support for Markdown and an optional integration for MDX files. You can reuse any existing [Markdown and MDX files](/en/guides/markdown-content/), but they may require some adjustments to their frontmatter, such as adding [Astro's special `layout` frontmatter property](/en/basics/layouts/#markdown-layouts). You will no longer need to manually create pages for each Markdown-generated route. These files can be placed within `src/pages/` to take advantage of automatic file-based routing.
Alternatively, you can use [content collections](/en/guides/content-collections/) in Astro to store and manage your content. You will retrieve the content yourself and [generate those pages dynamically](/en/guides/content-collections/#generating-routes-from-content).
### Migrating Tests
As Astro outputs raw HTML, it is possible to write end-to-end tests using the output of the build step. Any end-to-end tests written previously might work out-of-the-box if you have been able to match the markup of your Next site. Testing libraries such as Jest and React Testing Library can be imported and used in Astro to test your React components.
See Astro's [testing guide](/en/guides/testing/) for more.
## Reference: Convert Next.js Syntax to Astro
### Next Links to Astro
Convert any Next `<Link to="">`, `<NavLink>` etc. components to HTML `<a href="">` tags.
```astro del={1} ins={2}
<Link to="/blog">Blog</Link>
<a href="/blog">Blog</a>
```
Astro does not use any special component for links, although you are welcome to build your own `<Link>` component. You can then import and use this `<Link>` just as you would any other component.
```astro title="src/components/Link.astro"
---
const { to } = Astro.props;
---
<a href={to}><slot /></a>
```
### Next Imports to Astro
Update any [file imports](/en/guides/imports/) to reference relative file paths exactly. This can be done using [import aliases](/en/guides/typescript/#import-aliases), or by writing out a relative path in full.
Note that `.astro` and several other file types must be imported with their full file extension.
```astro title="src/pages/authors/Fred.astro"
---
import Card from "../../components/Card.astro";
---
<Card />
```
### Next Children Props to Astro
Convert any instances of `{children}` to an Astro `<slot />`. Astro does not need to receive `{children}` as a function prop and will automatically render child content in a `<slot />`.
```astro title="src/components/MyComponent.astro" del={3-9} ins={11-13}
---
---
export default function MyComponent(props) {
return (
<div>
{props.children}
</div>
);
}
<div>
<slot />
</div>
```
React components that pass multiple sets of children can be migrated to an Astro component using [named slots](/en/basics/astro-components/#named-slots).
See more about [specific `<slot />` usage in Astro](/en/basics/astro-components/#slots).
### Next Data Fetching to Astro
Convert any instances of `getStaticProps()` to either `import.meta.glob()` or `getCollection()`/`getEntryBySlug()` in order to access data from other files in your project source. To [fetch remote data](/en/guides/data-fetching/), use `fetch()`.
These data requests are made in the frontmatter of the Astro component and use top-level await.
```astro title="src/pages/index.astro"
---
import { getCollection } from 'astro:content';
// Get all `src/content/blog/` entries
const allBlogPosts = await getCollection('blog');
// Get all `src/pages/posts/` entries
const allPosts = Object.values(import.meta.glob('../pages/posts/*.md', { eager: true }));
const response = await fetch('https://randomuser.me/api/');
const data = await response.json();
const randomUser = data.results[0];
---
```
See more about local files imports with [`import.meta.glob()`](/en/guides/imports/#importmetaglob), [querying using the Collections API](/en/guides/content-collections/#querying-collections) or [fetching remote data](/en/guides/data-fetching/).
### Next Styling to Astro
You may need to replace any [CSS-in-JS libraries](https://github.com/withastro/astro/issues/4432) (e.g. styled-components) with other available CSS options in Astro.
If necessary, convert any inline style objects (`style={{ fontWeight: "bold" }}`) to inline HTML style attributes (`style="font-weight:bold;"`). Or, use an [Astro `<style>` tag](/en/guides/styling/#styling-in-astro) for scoped CSS styles.
```astro title="src/components/Card.astro" del={1} ins={2}
<div style={{backgroundColor: `#f4f4f4`, padding: `1em`}}>{message}</div>
<div style="background-color: #f4f4f4; padding: 1em;">{message}</div>
```
Tailwind is supported after installing the [Tailwind Vite plugin](/en/guides/styling/#tailwind). No changes to your existing Tailwind code are required!
See more about [Styling in Astro](/en/guides/styling/).
### Next Image Plugin to Astro
Convert any Next `<Image />` components to [Astro's own image component](/en/guides/images/) in `.astro` or `.mdx` files, or to a [standard HTML `<img>` / JSX `<img />`](/en/guides/images/#images-in-ui-framework-components) tag as appropriate in your React components.
Astro's `<Image />` component works in `.astro` and `.mdx` files only. See a [full list of its component attributes](/en/reference/modules/astro-assets/#image-properties) and note that several will differ from Next's attributes.
```astro title="src/pages/index.astro"
---
import { Image } from 'astro:assets';
import rocket from '../assets/rocket.png';
---
<Image src={rocket} alt="A rocketship in space." />
<img src={rocket.src} alt="A rocketship in space.">
```
In React (`.jsx`) components, use standard JSX image syntax (`<img />`). Astro will not optimize these images, but you can install and use NPM packages for more flexibility.
You can learn more about [using images in Astro](/en/guides/images/) in the Images Guide.
## Guided example: Next data fetching to Astro
Here is an example of Next.js Pokédex data fetch converted to Astro.
`pages/index.js` fetches and displays a list of the first 151 Pokémon using [the REST PokéAPI](https://pokeapi.co/).
Here's how to recreate that in `src/pages/index.astro`, replacing `getStaticProps()` with `fetch()`.
<Steps>
1. Identify the return() JSX.
```jsx title="pages/index.js" {6-18}
import Link from 'next/link'
import styles from '../styles/poke-list.module.css';
export default function Home({ pokemons }) {
return (
<>
<ul className={`plain-list ${styles.pokeList}`}>
{pokemons.map((pokemon) => (
<li className={styles.pokemonListItem} key={pokemon.name}>
<Link className={styles.pokemonContainer} as={`/pokemon/${pokemon.name}`} href="/pokemon/[name]">
<p className={styles.pokemonId}>No. {pokemon.id}</p>
<img className={styles.pokemonImage} src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemon.id}.png`} alt={`${pokemon.name} picture`}></img>
<h2 className={styles.pokemonName}>{pokemon.name}</h2>
</Link>
</li>
))}
</ul>
</>
)
}
export const getStaticProps = async () => {
const res = await fetch("https://pokeapi.co/api/v2/pokemon?limit=151")
const resJson = await res.json();
const pokemons = resJson.results.map(pokemon => {
const name = pokemon.name;
// https://pokeapi.co/api/v2/pokemon/1/
const url = pokemon.url;
const id = url.split("/")[url.split("/").length - 2];
return {
name,
url,
id
}
});
return {
props: {
pokemons,
},
}
}
```
2. Create `src/pages/index.astro`
Use the return value of the Next function. Convert any Next or React syntax to Astro, including changing the case of any [HTML global attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes).
Note that:
- `.map` just works!
- `className` becomes `class`.
- `<Link>` becomes `<a>`.
- The `<> </>` fragment is not required in Astro templating.
- `key` is a React attribute, and is not an attribute of `li` in Astro.
```astro title="src/pages/index.astro" "class" "</a>" "<a"
---
---
<ul class="plain-list pokeList">
{pokemons.map((pokemon) => (
<li class="pokemonListItem">
<a class="pokemonContainer" href={`/pokemon/${pokemon.name}`}>
<p class="pokemonId">No. {pokemon.id}</p>
<img class="pokemonImage" src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemon.id}.png`} alt={`${pokemon.name} picture`}/>
<h2 class="pokemonName">{pokemon.name}</h2>
</a>
</li>
))}
</ul>
```
3. Add any needed imports, props, and JavaScript
Note that:
- the `getStaticProps` function is no longer needed. Data from the API is fetched directly in the code fence.
- A `<Layout>` component is imported and wraps the page templating.
```astro ins={2,4-16,19,31} title="src/pages/index.astro"
---
import Layout from '../layouts/layout.astro';
const res = await fetch("https://pokeapi.co/api/v2/pokemon?limit=151");
const resJson = await res.json();
const pokemons = resJson.results.map(pokemon => {
const name = pokemon.name;
// https://pokeapi.co/api/v2/pokemon/1/
const url = pokemon.url;
const id = url.split("/")[url.split("/").length - 2];
return {
name,
url,
id
}
});
---
<Layout>
<ul class="plain-list pokeList">
{pokemons.map((pokemon) => (
<li class="pokemonListItem" key={pokemon.name}>
<a class="pokemonContainer" href={`/pokemon/${pokemon.name}`}>
<p class="pokemonId">No. {pokemon.id}</p>
<img class="pokemonImage" src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemon.id}.png`} alt={`${pokemon.name} picture`}/>
<h2 class="pokemonName">{pokemon.name}</h2>
</a>
</li>
))}
</ul>
</Layout>
```
</Steps>
## Community Resources
<CardGrid>
<LinkCard title="Why we switched to Astro (and why it might interest you)" href="https://www.datocms.com/blog/why-we-switched-to-astro" />
<LinkCard title="Migrating from Next.js to Astro" href="https://johnzanussi.com/posts/nextjs-to-astro-migration" />
<LinkCard title="From NextJS to Astro" href="https://vanntile.com/blog/next-to-astro" />
<LinkCard title="Converting Next.js to Astro" href="https://ericclemmons.com/blog/converting-nextjs-to-astro" />
<LinkCard title="Migrating to Astro (from Next.js)" href="https://www.raygesualdo.com/posts/migrating-to-astro-the-beginning/" />
<LinkCard title="Astro.js as an alternative to Next.js" href="https://www.railyard.works/blog/astro-as-alternative-to-next" />
<LinkCard title="Why I Switched My Website from Next.js to Astro" href="https://praveenjuge.com/blog/why-i-switched-my-website-from-nextjs-to-astro/" />
<LinkCard title="NextJS to Astro: more control = faster sites" href="https://www.youtube.com/watch?v=PSzCtdM20Fc" />
<LinkCard title="How Astro made my site 100x faster" href="https://www.youtube.com/watch?v=cOxA3kMYtkM" />
</CardGrid>
:::note[Have a resource to share?]
If you found (or made!) a helpful video or blog post about converting a Next.js site to Astro, [add it to this list](https://github.com/withastro/docs/edit/main/src/content/docs/en/guides/migrate-to-astro/from-nextjs.mdx)!
:::
---
File: /src/content/docs/en/guides/migrate-to-astro/from-nuxtjs.mdx
---
---
title: Migrating from NuxtJS
description: Tips for migrating an existing NuxtJS project to Astro
sidebar:
label: NuxtJS
type: migration
stub: false
framework: NuxtJS
i18nReady: true
---
import { Steps, LinkCard, CardGrid } from '@astrojs/starlight/components';
import AstroVueTabs from '~/components/tabs/AstroVueTabs.astro';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import { FileTree } from '@astrojs/starlight/components';
Here are some key concepts and migration strategies to help you get started. Use the rest of our docs and our [Discord community](https://astro.build/chat) to keep going!
> This guide is referring to [Nuxt 2](https://nuxtjs.org/), not the newer Nuxt 3. While some of the concepts are similar, Nuxt 3 is a newer version of the framework and may require different strategies for parts of your migration.
## Key Similarities between Nuxt and Astro
Nuxt and Astro share some similarities that will help you migrate your project:
- Astro projects can also be SSG or [SSR with page level prerendering](/en/guides/on-demand-rendering/).
- Astro uses file-based routing, and [allows specially named pages to create dynamic routes](/en/guides/routing/#dynamic-routes).
- Astro is [component-based](/en/basics/astro-components/), and your markup structure will be similar before and after your migration.
- Astro has [an official integration for using Vue components](/en/guides/integrations-guide/vue/).
- Astro has support for [installing NPM packages](/en/guides/imports/#npm-packages), including Vue libraries. You may be able to keep some or all of your existing Vue components and dependencies.
## Key Differences between Nuxt and Astro
When you rebuild your Nuxt site in Astro, you will notice some important differences:
- Nuxt is a Vue-based SPA (single-page application). Astro sites are multi-page apps built using `.astro` components, but can also support React, Preact, Vue.js, Svelte, SolidJS, AlpineJS, and raw HTML templating.
- [Page Routing](/en/basics/astro-pages/#file-based-routing): Nuxt uses `vue-router` for SPA routing, and `vue-meta` for managing `<head>`. In Astro, you will create separate HTML page routes and control your page `<head>` directly, or in a layout component.
- [content-driven](/en/concepts/why-astro/#content-driven): Astro was designed to showcase your content and to allow you to opt-in to interactivity only as needed. An existing Nuxt app may be built for high client-side interactivity. Astro has built-in capabilities for working with your content, such as page generation, but may require advanced Astro techniques to include items that are more challenging to replicate using `.astro` components, such as dashboards.
## Convert your NuxtJS Project
Each project migration will look different, but there are some common actions you will perform when converting from Nuxt to Astro.
### Create a new Astro project
Use the `create astro` command for your package manager to launch Astro's CLI wizard or choose a community theme from the [Astro Theme Showcase](https://astro.build/themes).
You can pass a `--template` argument to the `create astro` command to start a new Astro project with one of our official starters (e.g. `docs`, `blog`, `portfolio`). Or, you can [start a new project from any existing Astro repository on GitHub](/en/install-and-setup/#install-from-the-cli-wizard).
<PackageManagerTabs>
<Fragment slot="npm">
```shell
# launch the Astro CLI Wizard
npm create astro@latest
# create a new project with an official example
npm create astro@latest -- --template <example-name>
```
</Fragment>
<Fragment slot="pnpm">
```shell
# launch the Astro CLI Wizard
pnpm create astro@latest
# create a new project with an official example
pnpm create astro@latest --template <example-name>
```
</Fragment>
<Fragment slot="yarn">
```shell
# launch the Astro CLI Wizard
yarn create astro@latest
# create a new project with an official example
yarn create astro@latest --template <example-name>
```
</Fragment>
</PackageManagerTabs>
Then, copy your existing Nuxt project files over to your new Astro project in a separate folder outside of `src`.
:::tip
Visit https://astro.new for the full list of official starter templates, and links for opening a new project in IDX, StackBlitz, CodeSandbox, or Gitpod.
:::
### Install integrations (optional)
You may find it useful to install some of [Astro's optional integrations](/en/guides/integrations-guide/) to use while converting your Nuxt project to Astro:
- **@astrojs/vue**: to reuse some existing Vue UI components in your new Astro site, or keep writing with Vue components.
- **@astrojs/mdx**: to bring existing MDX files from your Nuxt project, or to use MDX in your new Astro site.
### Put your source code in `src`
<Steps>
1. **Move** the contents of Nuxt's `static/` folder into `public/`.
Astro uses the `public/` directory for static assets, similar to Nuxt's `static/` folder.
2. **Copy or Move** Nuxt's other files and folders (e.g. `pages`, `layouts` etc.) into Astro's `src/` folder.
Like Nuxt, Astro's `src/pages/` folder is a special folder used for file-based routing. All other folders are optional, and you can organize the contents of your `src/` folder any way you like. Other common folders in Astro projects include `src/layouts/`, `src/components`, `src/styles`, `src/scripts`.
</Steps>
### Convert Vue SFC pages to `.astro` files
Here are some tips for converting a Nuxt `.vue` component into a `.astro` component:
1. Use the `<template>` of the existing NuxtJS component function as the basis for your HTML template.
2. Change any [Nuxt or Vue syntax to Astro](#reference-convert-nuxtjs-syntax-to-astro) or to HTML web standards. This includes `<NuxtLink>`, `:class`, `{{variable}}`, and `v-if`, for example.
3. Move `<script>` JavaScript, into a "code fence" (`---`). Convert your component's data-fetching properties to server-side JavaScript - see [Nuxt data fetching to Astro](#nuxt-data-fetching-to-astro).
4. Use `Astro.props` to access any additional props that were previously passed to your Vue component.
5. Decide whether any imported components also need to be converted to Astro. With the official integration installed, you can [use existing Vue components in your Astro file](/en/guides/integrations-guide/vue/). But, you may want to convert them to Astro, especially if they do not need to be interactive!
See [an example from a Nuxt app converted step-by-step](#guided-example-see-the-steps).
#### Compare: Vue vs Astro
Compare the following Nuxt component and a corresponding Astro component:
<AstroVueTabs>
<Fragment slot="vue">
```vue title="Page.vue"
<template>
<div>
<p v-if="message === 'Not found'">
The repository you're looking up doesn't exist
</p>
<div v-else>
<Header/>
<p class="banner">Astro has {{stars}} 🧑‍🚀</p>
<Footer />
</div>
</div>
</template>
<script>
import Vue from 'vue'
export default Vue.extend({
name: 'IndexPage',
async asyncData() {
const res = await fetch('https://api.github.com/repos/withastro/astro')
const json = await res.json();
return {
message: json.message,
stars: json.stargazers_count || 0,
};
}
});
</script>
<style scoped>
.banner {
background-color: #f4f4f4;
padding: 1em 1.5em;
text-align: center;
margin-bottom: 1em;
}
<style>
```
</Fragment>
<Fragment slot="astro">
```astro title="Page.astro"
---
import Header from "./header";
import Footer from './footer';
import "./layout.css";
const res = await fetch('https://api.github.com/repos/withastro/astro')
const json = await res.json()
const message = json.message;
const stars = json.stargazers_count || 0;
---
{message === "Not Found" ?
<p>The repository you're looking up doesn't exist</p> :
<>
<Header />
<p class="banner">Astro has {stars} 🧑‍🚀</p>
<Footer />
</>
}
<style>
.banner {
background-color: #f4f4f4;
padding: 1em 1.5em;
text-align: center;
margin-bottom: 1em;
}
<style>
```
</Fragment>
</AstroVueTabs>
### Migrating Layout Files
You may find it helpful to start by converting your Nuxt layouts and templates into [Astro layout components](/en/basics/layouts/).
Each Astro page explicitly requires `<html>`, `<head>`, and `<body>` tags to be present. Your Nuxt `layout.vue` and templates will not include these.
Note the standard HTML templating, and direct access to `<head>`:
```astro title="src/layouts/Layout.astro"
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<title>Astro</title>
</head>
<body>
<!-- Wrap the slot element with your existing layout templating -->
<slot />
</body>
</html>
```
You may also wish to reuse code from [your Nuxt's page's `head` property](https://nuxtjs.org/docs/configuration-glossary/configuration-head/#the-head-property) to include additional site metadata. Notice that Astro uses neither `vue-meta` nor a component's `head` property but instead creates `<head>` directly. You may import and use components, even within `<head>`, to separate and organize your page content.
### Migrating Pages and Posts
In NuxtJS, your [pages](/en/basics/astro-pages/) live in `/pages`. In Astro, all your page content must live within `src/` unless you are using [content collections](/en/guides/content-collections/).
#### Vue Pages
Your existing Nuxt Vue (`.vue`) pages will need to be [converted from Vue files to `.astro` pages](#convert-vue-sfc-pages-to-astro-files). You cannot use an existing Vue page file in Astro.
These [`.astro` pages](/en/basics/astro-pages/) must be located within `src/pages/` and will have page routes generated automatically based on their file path.
##### Dynamic File Path Naming
In Nuxt, your dynamic pages use an underscore to represent a dynamic page property that's then passed to the page generation:
<FileTree>
- pages/
- pokemon/
- _name.vue
- index.vue
- nuxt.config.js
</FileTree>
To convert to Astro, change this underscored dynamic path property (e.g. `_name.vue`) to be wrapped in a pair of square brackets (e.g. `[name].astro`):
<FileTree>
- src/
- pages/
- pokemon/
- [name].astro
- index.astro
- astro.config.mjs
</FileTree>
#### Markdown and MDX pages
Astro has built-in support for Markdown and an optional integration for MDX files. You can reuse any existing Markdown and MDX pages, but they may require some adjustments to their frontmatter, such as adding [Astro's special `layout` frontmatter property](/en/basics/layouts/#markdown-layouts).
You will no longer need to manually create pages for each Markdown-generated route or use an external package like `@nuxt/content`. These files can be placed within `src/pages/` to take advantage of automatic file-based routing.
When part of a [content collection](/en/guides/content-collections/), you will [generate pages dynamically](/en/guides/content-collections/#generating-routes-from-content) from your content entries.
### Migrating Tests
As Astro outputs raw HTML, it is possible to write end-to-end tests using the output of the build step. Any end-to-end tests written previously might work out-of-the-box, if you have been able to match the markup of your Nuxt site. Testing libraries such as Jest and Vue Testing Library can be imported and used in Astro to test your Vue components.
See Astro's [testing guide](/en/guides/testing/) for more.
## Reference: Convert NuxtJS Syntax to Astro
### Nuxt Local Variables to Astro
To use local variables in an Astro component's HTML, change the set of two curly braces to one set of curly braces:
```astro title="src/components/Component.astro" del={4} add={5}
---
const message = "Hello!"
---
<p>{{message}}</p>
<p>{message}</p>
```
### Nuxt Property Passing to Astro
To bind an attribute or component property in an Astro component, change this syntax to the following:
```astro title="src/components/Component.astro" del={3-7} ins={9-11}
---
---
<p v-bind:aria-label="message">...</p>
<!-- Or -->
<p :aria-label="message">...</p>
<!-- Also support component props -->
<Header title="Page"/>
<p aria-label={message}>...</p>
<!-- Also support component props -->
<Header title={"Page"}/>
```
### Nuxt Links to Astro
Convert any Nuxt `<NuxtLink to="">` components to HTML `<a href="">` tags.
```astro del={1} ins={2}
<NuxtLink to="/blog">Blog</Link>
<a href="/blog">Blog</a>
```
Astro does not use any special component for links, although you are welcome to build custom link components. You can then import and use this `<Link>` just as you would any other component.
```astro title="src/components/Link.astro"
---
const { to } = Astro.props
---
<a href={to}><slot /></a>
```
### Nuxt Imports to Astro
If necessary, update any [file imports](/en/guides/imports/) to reference relative file paths exactly. This can be done using [import aliases](/en/guides/typescript/#import-aliases), or by writing out a relative path in full.
Note that `.astro` and several other file types must be imported with their full file extension.
```astro title="src/pages/authors/Fred.astro" ".astro"
---
import Card from `../../components/Card.astro`;
---
<Card />
```
### Nuxt Dynamic Page Generation to Astro
In Nuxt, to generate a dynamic page you either must:
- Use SSR.
- [Use the `generate` function in `nuxt.config.js`](https://nuxtjs.org/docs/configuration-glossary/configuration-generate/) to define all possible static routes.
In Astro, you similarly have two choices:
- [Use SSR](/en/guides/on-demand-rendering/).
- Export a `getStaticPaths()` function in the frontmatter of an Astro page to tell the framework which [static routes to generate dynamically](/en/guides/routing/#dynamic-routes).
#### Convert a `generate` function in Nuxt to a `getStaticPaths` function in Astro.
To generate multiple pages, replace the function to create routes in your `nuxt.config.js` with `getStaticPaths()` directly inside a dynamic routing page itself:
```javascript title="nuxt.config.js"
{
// ...
generate: {
async routes() {
// Axios is required here unless you're using Node 18
const res = await axios.get("https://pokeapi.co/api/v2/pokemon?limit=151")
const pokemons = res.data.results;
return pokemons.map(pokemon => {
return '/pokemon/' + pokemon.name
})
}
}
}
```
```astro title="src/pages/pokemon/[name].astro"
---
export const getStaticPaths = async () => {
const res = await fetch("https://pokeapi.co/api/v2/pokemon?limit=151")
const resJson = await res.json();
const pokemons = resJson.results;
return pokemons.map(({ name }) => ({
params: { name },
}))
}
// ...
---
<!-- Your template here -->
```
### Nuxt Data Fetching to Astro
Nuxt has two methods of fetching server-side data:
- [`asyncData` options API](https://nuxtjs.org/docs/features/data-fetching/#async-data)
- [`fetch` hook](https://nuxtjs.org/docs/features/data-fetching/#the-fetch-hook)
In Astro, fetch data inside of your page's code fence.
Migrate the following:
```vue title="pages/index.vue"
{
// ...
async asyncData() {
const res = await fetch("https://pokeapi.co/api/v2/pokemon?limit=151")
const resJson = await res.json();
const pokemons = resJson.results;
return {
pokemons,
}
},
}
```
To a code fence without a wrapper function:
```astro title="src/pages/index.astro"
---
const res = await fetch("https://pokeapi.co/api/v2/pokemon?limit=151")
const resJson = await res.json();
const pokemons = resJson.results;
---
<!-- Your template here -->
```
### Nuxt Styling to Astro
Nuxt utilizes Vue's component styling to generate a page's style.
```vue title="pages/index.vue"
<template>
<!-- Your template here -->
</template>
<script>
// Your server logic here
</script>
<style scoped>
.class {
color: red;
}
</style>
```
Similarly, in Astro you can drop in a `<style>` element in your page's template to provide scoped styles to the component.
```astro title="src/pages/index.vue"
---
// Your server logic here
---
<style>
.class {
color: red;
}
</style>
```
#### Global Styling
`<style>` tags are `scoped` by default in Astro. To make a `<style>` tag global, mark it with the `is:global` attribute:
```astro title="src/pages/index.vue"
<style is:global>
p {
color: red;
}
</style>
```
#### Pre-processor support
[Astro supports the most popular CSS preprocessors](/en/guides/styling/#css-preprocessors) by installing them as a dev dependency. For example, to use SCSS:
```shell
npm install -D sass
```
After doing so, you're then able to use `.scss` or `.sass` styled without modification from your Vue components.
```astro title="src/layouts/Layout.astro"
<p>Hello, world</p>
<style lang="scss">
p {
color: black;
&:hover {
color: red;
}
}
</style>
```
See more about [Styling in Astro](/en/guides/styling/).
### Nuxt Image Plugin to Astro
Convert any [Nuxt `<nuxt-img/>` or `<nuxt-picture/>` components](https://image.nuxtjs.org/components/nuxt-img) to [Astro's own image component](/en/guides/images/) in `.astro` or `.mdx` files, or to a [standard HTML `<img>`](/en/guides/images/#images-in-ui-framework-components) or `<picture>` tag as appropriate in your Vue components.
Astro's `<Image />` component works in `.astro` and `.mdx` files only. See a [full list of its component attributes](/en/reference/modules/astro-assets/#image-properties) and note that several will differ from Nuxt's attributes.
```astro title="src/pages/index.astro"
---
import { Image } from 'astro:assets';
import rocket from '../assets/rocket.png';
---
<Image src={rocket} alt="A rocketship in space." />
<img src={rocket.src} alt="A rocketship in space.">
```
In Vue (`.vue`) components within your Astro app, use standard JSX image syntax (`<img />`). Astro will not optimize these images, but you can install and use NPM packages for more flexibility.
You can learn more about [using images in Astro](/en/guides/images/) in the Images Guide.
## Guided example: See the steps!
Here is an example of Nuxt Pokédex data fetch converted to Astro.
`pages/index.vue` fetches and displays a list of the first 151 Pokémon using [the REST PokéAPI](https://pokeapi.co/).
Here's how to recreate that in `src/pages/index.astro`, replacing `asyncData()` with `fetch()`.
<Steps>
1. Identify the `<template>` and `<style>` in the Vue SFC.
```jsx title="pages/index.vue" {2-13,47-55}
<template>
<ul class="plain-list pokeList">
<li v-for="pokemon of pokemons" class="pokemonListItem" :key="pokemon.name">
<NuxtLink class="pokemonContainer" :to="`/pokemon/${pokemon.name}`">
<p class="pokemonId">No. {{pokemon.id}}</p>
<img
class="pokemonImage"
:src="`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemon.id}.png`"
:alt="`${pokemon.name} picture`"/>
<h2 class="pokemonName">{{pokemon.name}}</h2>
</NuxtLink>
</li>
</ul>
</template>
<script>
import Vue from 'vue'
export default Vue.extend({
name: 'IndexPage',
layout: 'default',
async asyncData() {
const res = await fetch("https://pokeapi.co/api/v2/pokemon?limit=151")
const resJson = await res.json();
const pokemons = resJson.results.map(pokemon => {
const name = pokemon.name;
// https://pokeapi.co/api/v2/pokemon/1/
const url = pokemon.url;
const id = url.split("/")[url.split("/").length - 2];
return {
name,
url,
id
}
});
return {
pokemons,
}
},
head() {
return {
title: "Pokedex: Generation 1"
}
}
});
</script>
<style scoped>
.pokeList {
display: grid;
grid-template-columns: repeat( auto-fit, minmax(250px, 1fr) );
gap: 1rem;
}
/* ... */
</style>
```
2. Create `src/pages/index.astro`
Use the `<template>` and `<style>` tags of the Nuxt SFC. Convert any Nuxt or Vue syntax to Astro.
Note that:
- `<template>` is removed
- `<style>` has its `scoped` attribute removed
- `v-for` becomes `.map`.
- `:attr="val"` becomes `attr={val}`
- `<NuxtLink>` becomes `<a>`.
- The `<> </>` fragment is not required in Astro templating.
```astro title="src/pages/index.astro" ".map" "</a>" "<a" "key={" "}>" "href={" " {pokemon.id}" "} alt={" "src={" "}/>" ">{pokemon.name}<"
---
---
<ul class="plain-list pokeList">
{pokemons.map((pokemon) => (
<li class="pokemonListItem" key={pokemon.name}>
<a class="pokemonContainer" href={`/pokemon/${pokemon.name}`}>
<p class="pokemonId">No. {pokemon.id}</p>
<img class="pokemonImage" src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemon.id}.png`} alt={`${pokemon.name} picture`}/>
<h2 class="pokemonName">{pokemon.name}</h2>
</a>
</li>
))}
</ul>
<style>
.pokeList {
display: grid;
grid-template-columns: repeat( auto-fit, minmax(250px, 1fr) );
gap: 1rem;
}
/* ... */
</style>
```
3. Add any needed imports, props and JavaScript
Note that:
- The `asyncData` function is no longer needed. Data from the API is fetched directly in the code fence.
- A `<Layout>` component is imported, and wraps the page templating.
- Our `head()` Nuxt method is passed to the `<Layout>` component, which is passed to the `<title>` element as a property.
```astro ins={2,4-16,19,31} title="src/pages/index.astro"
---
import Layout from '../layouts/layout.astro';
const res = await fetch("https://pokeapi.co/api/v2/pokemon?limit=151");
const resJson = await res.json();
const pokemons = resJson.results.map(pokemon => {
const name = pokemon.name;
// https://pokeapi.co/api/v2/pokemon/1/
const url = pokemon.url;
const id = url.split("/")[url.split("/").length - 2];
return {
name,
url,
id
}
});
---
<Layout title="Pokedex: Generation 1">
<ul class="plain-list pokeList">
{pokemons.map((pokemon) => (
<li class="pokemonListItem" key={pokemon.name}>
<a class="pokemonContainer" href={`/pokemon/${pokemon.name}`}>
<p class="pokemonId">No. {pokemon.id}</p>
<img class="pokemonImage" src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemon.id}.png`} alt={`${pokemon.name} picture`}/>
<h2 class="pokemonName">{pokemon.name}</h2>
</a>
</li>
))}
</ul>
</Layout>
<style>
.pokeList {
display: grid;
grid-template-columns: repeat( auto-fit, minmax(250px, 1fr) );
gap: 1rem;
}
/* ... */
</style>
```
</Steps>
## Community Resources
<CardGrid>
<LinkCard title="From Nuxt to Astro - rebuilding with Astro" href="https://dev.to/lindsaykwardell/from-nuxt-to-astro-rebuilding-with-astro-5ann"/>
<LinkCard title="Nuxt 2 to Astro 3 Replatforming – from Setup to Production" href="https://stevenwoodson.com/blog/replatforming-from-nuxtjs-2-to-astro/"/>
</CardGrid>
:::note[Have a resource to share?]
If you found (or made!) a helpful video or blog post about converting a Nuxt site to Astro, [add it to this list](https://github.com/withastro/docs/edit/main/src/content/docs/en/guides/migrate-to-astro/from-nuxtjs.mdx)!
:::
---
File: /src/content/docs/en/guides/migrate-to-astro/from-pelican.mdx
---
---
title: Migrating from Pelican
description: Tips for migrating an existing Pelican project to Astro
sidebar:
label: Pelican
type: migration
stub: true
framework: Pelican
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
[Pelican](https://getpelican.com) is an open-source static site generator built on Python.
## Key Similarities between Pelican and Astro
Pelican and Astro share some similarities that will help you migrate your project:
- Pelican and Astro are both static-site generators, ideally suited to [content-driven websites](/en/concepts/why-astro/#content-driven) like blogs.
- Pelican and Astro both have built-in support for [writing in Markdown](/en/guides/markdown-content/), including frontmatter YAML properties for page metadata. However, Astro has very few reserved frontmatter properties compared to Pelican. Even though many of your existing Pelican frontmatter properties will not be "special" in Astro, you can continue to use your existing Markdown files and frontmatter values.
## Key Differences between Pelican and Astro
When you rebuild your Pelican site in Astro, you will notice some important differences:
- Pelican supports writing content in Markdown and reStructured Text (`.rst`). Astro supports [creating pages from Markdown and MDX](/en/guides/markdown-content/) files, but does not support reStructured Text.
- Pelican uses HTML files and Jinja syntax for templating. [Astro syntax](/en/basics/astro-components/) is a JSX-like superset of HTML. All valid HTML is valid `.astro` syntax.
- Pelican was designed to build content-rich websites like blogs and has some built-in, blog features that you would have to build yourself in Astro. Instead, Astro offers some of these features included in an [official blog theme](https://github.com/withastro/astro/tree/latest/examples/blog).
## Switch from Pelican to Astro
To convert a Pelican documentation site to Astro, start with our official [Starlight docs theme starter template](https://starlight.astro.build), or explore more community themes in our [theme showcase](https://astro.build/themes/).
You can pass a `--template` argument to the `create astro` command to start a new Astro project with one of our official starters. Or, you can [start a new project from any existing Astro repository on GitHub](/en/install-and-setup/#install-from-the-cli-wizard).
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm create astro@latest -- --template starlight
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm create astro@latest --template starlight
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn create astro --template starlight
```
</Fragment>
</PackageManagerTabs>
Bring your existing Markdown content files to [create Markdown pages](/en/guides/markdown-content/). You can still take advantage of [file-based routing](/en/guides/routing/) by copying these documents from Pelican's `content/` folder into `src/pages/` in Astro. You may wish to read about [Astro's project structure](/en/basics/project-structure/) to learn where files should be located.
Pelican may have handled much of your site layout and metadata for you. You may wish to read about [building Astro Layouts as Markdown page wrappers](/en/basics/layouts/#markdown-layouts) to see how to manage templating yourself in Astro, including your page `<head>`.
Like Pelican, Astro has many plugins that extend its functionality. Explore the [official list of integrations](/en/guides/integrations-guide/) for adding features such as MDX support, and find hundreds more of community-maintained integrations in the [Astro Integrations Directory](https://astro.build/integrations/). You can even use the [Astro Integration API](/en/reference/integrations-reference/) to build your own custom integration to extend your project's features.
To convert other types of sites, such as a portfolio or a blog, see more official starter templates on [astro.new](https://astro.new). You'll find a link to each project's GitHub repository, as well as one-click links to open a working project in IDX, StackBlitz, CodeSandbox and Gitpod online development environments.
## Community Resources
:::note[Have a resource to share?]
If you found (or made!) a helpful video or blog post about converting a Pelican site to Astro, [add it to this list](https://github.com/withastro/docs/edit/main/src/content/docs/en/guides/migrate-to-astro/from-pelican.mdx)!
:::
---
File: /src/content/docs/en/guides/migrate-to-astro/from-sveltekit.mdx
---
---
title: Migrating from SvelteKit
description: Tips for migrating an existing SvelteKit project to Astro
sidebar:
label: SvelteKit
type: migration
stub: true
framework: SvelteKit
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import { CardGrid, LinkCard } from '@astrojs/starlight/components';
[SvelteKit](https://kit.svelte.dev) is a framework for building web applications on top of Svelte.
## Key Similarities between SvelteKit and Astro
SvelteKit and Astro share some similarities that will help you migrate your project:
- Both SvelteKit and Astro are modern JavaScript static-site generators and server-side rendering frameworks.
- Both SvelteKit and Astro use a [`src/` folder for your project files](/en/basics/project-structure/#src) and a [special folder for file-based routing](/en/basics/astro-pages/). Creating and managing pages for your site should feel familiar.
- Astro has [an official integration for using Svelte components](/en/guides/integrations-guide/svelte/) and supports [installing NPM packages](/en/guides/imports/#npm-packages), including several for Svelte. You will be able to write Svelte UI components, and may be able to keep some or all of your existing components and dependencies.
- Astro and SvelteKit both allow you to use a [headless CMS, APIs or Markdown files for data](/en/guides/data-fetching/). You can continue to use your preferred content authoring system, and will be able to keep your existing content.
## Key Differences between SvelteKit and Astro
When you rebuild your SvelteKit site in Astro, you will notice some important differences:
- Astro sites are multi-page apps, whereas SvelteKit defaults to SPAs (single-page applications) with server-side rendering, but can also create MPAs, traditional SPAs, or you can mix and match these techniques within an app.
- [Components](/en/basics/astro-components/): SvelteKit uses [Svelte](https://svelte.dev). Astro pages are built using [`.astro` components](/en/basics/astro-components/), but can also support [React, Preact, Vue.js, Svelte, SolidJS, AlpineJS](/en/guides/framework-components/) and raw HTML templating.
- [content-driven](/en/concepts/why-astro/#content-driven): Astro was designed to showcase your content and to allow you to opt-in to interactivity only as needed. An existing SvelteKit app might be built for high client-side interactivity. Astro has built-in capabilities for working with your content, such as page generation, but may require advanced Astro techniques to include items that are more challenging to replicate using `.astro` components, such as dashboards.
- [Markdown-ready](/en/guides/markdown-content/): Astro includes built-in Markdown support, and includes a [special frontmatter YAML `layout` property](/en/basics/layouts/#markdown-layouts) used per-file for page templating. If you are converting a SvelteKit Markdown-based blog, you will not have to install a separate Markdown integration and you will not set a layout via a configuration file. You can bring your existing Markdown files, but you may need to reorganize as Astro's file-based routing does not require a folder for each page route.
## Switch from SvelteKit to Astro
To convert a SvelteKit blog to Astro, start with our blog theme starter template, or explore more community blog themes in our [theme showcase](https://astro.build/themes/).
You can pass a `--template` argument to the `create astro` command to start a new Astro project with one of our official starters. Or, you can [start a new project from any existing Astro repository on GitHub](/en/install-and-setup/#install-from-the-cli-wizard).
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm create astro@latest -- --template blog
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm create astro@latest --template blog
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn create astro --template blog
```
</Fragment>
</PackageManagerTabs>
Bring your existing Markdown (or MDX, with our optional integration) files as content to [create Markdown or MDX pages](/en/guides/markdown-content/).
While file-based routing and layout components are similar in Astro, you may wish to read about [Astro's project structure](/en/basics/project-structure/) to learn where files should be located.
To convert other types of sites, such as a portfolio or documentation site, see more official starter templates on [astro.new](https://astro.new). You'll find a link to each project's GitHub repository, as well as one-click links to open a working project in IDX, StackBlitz, CodeSandbox and Gitpod online development environments.
## Community Resources
<CardGrid>
<LinkCard title="Rewriting my blog from SvelteKit to Astro" href="https://kharann.com/blog/rewriting-my-blog/"/>
</CardGrid>
:::note[Have a resource to share?]
If you found (or made!) a helpful video or blog post about converting a SvelteKit site to Astro, [add it to this list](https://github.com/withastro/docs/edit/main/src/content/docs/en/guides/migrate-to-astro/from-sveltekit.mdx)!
:::
---
File: /src/content/docs/en/guides/migrate-to-astro/from-vuepress.mdx
---
---
title: Migrating from VuePress
description: Tips for migrating an existing VuePress project to Astro
sidebar:
label: VuePress
type: migration
stub: true
framework: VuePress
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
[VuePress](https://vuePress.vuejs.org) is an open-source static site generator built on Vue.
## Key Similarities between VuePress and Astro
VuePress and Astro share some similarities that will help you migrate your project:
- Both VuePress and Astro are modern JavaScript static-site generators with similar project file structures. Both use a [special `src/pages/` folder for file-based routing](/en/basics/astro-pages/). Creating and managing pages for your site should feel familiar.
- Astro and VuePress are both designed for [content-driven websites](/en/concepts/why-astro/#content-driven), with excellent built-in support for Markdown files. Writing in Markdown will feel familiar, and you will be able to keep your existing content.
- Astro has [an official integration for using Vue components](/en/guides/integrations-guide/vue/) and supports [installing NPM packages](/en/guides/imports/#npm-packages), including several for Vue. You will be able to write Vue UI components, and may be able to keep some or all of your existing Vue components and dependencies.
## Key Differences between VuePress and Astro
When you rebuild your VuePress site in Astro, you will notice some important differences.
- VuePress is a Vue-based single-page application (SPA). Astro sites are multi-page apps built using [`.astro` components](/en/basics/astro-components/), but can also support [React, Preact, Vue.js, Svelte, SolidJS, AlpineJS](/en/guides/framework-components/) and raw HTML templating.
- [Layout templates](/en/basics/layouts/): VuePress sites are created using Markdown (`.md`) files for page content and HTML (`.html`) templates for layout. Astro is component-based, and uses Astro components, which include HTML templating for pages, layouts and individual UI elements. Astro can also create [pages from `.md` and `.mdx` files](/en/guides/markdown-content/), using an Astro layout component for wrapping Markdown content in a page template.
- VuePress was designed to build content-heavy, Markdown-centric sites and has some built-in, documentation-specific website features that you would have to build yourself in Astro. Instead, Astro offers some documentation-specific features through an [official docs theme](https://starlight.astro.build). This website was the inspiration for that template! You can also find more [community docs themes](https://astro.build/themes?search=&categories%5B%5D=docs) with built-in features in our Themes Showcase.
## Switch from VuePress to Astro
To convert a VuePress documentation site to Astro, start with our official [Starlight docs theme starter template](https://starlight.astro.build), or explore more community docs themes in our [theme showcase](https://astro.build/themes?search=&categories%5B%5D=docs).
You can pass a `--template` argument to the `create astro` command to start a new Astro project with one of our official starters. Or, you can [start a new project from any existing Astro repository on GitHub](/en/install-and-setup/#install-from-the-cli-wizard).
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm create astro@latest -- --template starlight
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm create astro@latest --template starlight
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn create astro --template starlight
```
</Fragment>
</PackageManagerTabs>
Bring your existing Markdown content files to [create Markdown pages](/en/guides/markdown-content/). You can still take advantage of [file-based routing](/en/guides/routing/) by moving these documents from `docs` in VuePress to `src/pages/` in Astro. Create folders with names that correspond to your existing VuePress project, and you should be able to keep your existing URLs.
VuePress, or any theme you installed, probably handled much of your site layout and metadata for you. You may wish to read about [building Astro Layouts as Markdown page wrappers](/en/basics/layouts/#markdown-layouts) to see how to manage templating yourself in Astro, including your page `<head>`.
You can find Astro's docs starter, and other templates, on [astro.new](https://astro.new). You'll find a link to each project's GitHub repository, as well as one-click links to open a working project in IDX, StackBlitz, CodeSandbox and Gitpod online development environments.
## Community Resources
:::note[Have a resource to share?]
If you found (or made!) a helpful video or blog post about converting a VuePress site to Astro, [add it to this list](https://github.com/withastro/docs/edit/main/src/content/docs/en/guides/migrate-to-astro/from-vuepress.mdx)!
:::
---
File: /src/content/docs/en/guides/migrate-to-astro/from-wordpress.mdx
---
---
title: Migrating from WordPress
description: Tips for migrating an existing WordPress project to Astro
sidebar:
label: WordPress
type: migration
stub: true
framework: WordPress
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import { LinkCard, CardGrid } from '@astrojs/starlight/components';
[WordPress](https://wordpress.org) is an open-source, personal publishing system built on PHP and MySQL.
:::tip
You can [use WordPress as a headless CMS for your Astro project](/en/guides/cms/wordpress/). Follow our guide to use your existing WordPress content in a new Astro project.
:::
## Key Similarities between WordPress and Astro
WordPress and Astro share some similarities that will help you migrate your project:
- Both WordPress and Astro are ideal for [content-driven websites](/en/concepts/why-astro/#content-driven) like blogs and support writing your content in Markdown (requires a plugin in WordPress). Although the process for adding new content is different, [writing in Markdown files](/en/guides/markdown-content/) for your Astro blog should feel familiar if you have used Markdown syntax in your WordPress editor.
- Both WordPress and Astro encourage you to [think about the design of your site in "blocks"](/en/concepts/islands/) (components). In Astro you will probably [write more of your own code to create these blocks](/en/basics/astro-components/) rather than rely on pre-built plugins. But thinking about the individual pieces of your site and how they are presented on the page should feel familiar.
## Key Differences between WordPress and Astro
When you rebuild your WordPress site in Astro, you will notice some important differences:
- A WordPress site is edited using an online dashboard. In Astro, you will use a [code editor](/en/editor-setup/) and development environment to maintain your site. You can develop locally on your machine, or choose a cloud editor/development environment like IDX, StackBlitz, CodeSandbox or Gitpod.
- WordPress has an extensive plugin and theme market. In Astro, you will find some themes and [integrations](https://astro.build/integrations/) available, but you may now have to build many of your existing features yourself instead of looking for third-party solutions. Or, you can choose to start with an [Astro theme](https://astro.build/themes) with built-in features!
- WordPress stores your content in a database. In Astro, you will have individual files (typically Markdown or MDX) in your [project directory](/en/basics/project-structure/) for each page's content. Or, you can choose to use a [CMS for your content](/en/guides/cms/), even your existing WordPress site, and use Astro to fetch and present the data.
## Switch from WordPress to Astro
To convert a WordPress blog to Astro, start with our blog theme starter template, or explore more community blog themes in our [theme showcase](https://astro.build/themes).
You can pass a `--template` argument to the `create astro` command to start a new Astro project with one of our official starters. Or, you can [start a new project from any existing Astro repository on GitHub](/en/install-and-setup/#install-from-the-cli-wizard).
<PackageManagerTabs>
<Fragment slot="npm">
```shell
npm create astro@latest -- --template blog
```
</Fragment>
<Fragment slot="pnpm">
```shell
pnpm create astro@latest --template blog
```
</Fragment>
<Fragment slot="yarn">
```shell
yarn create astro --template blog
```
</Fragment>
</PackageManagerTabs>
You can continue to [use your existing WordPress blog as your CMS for Astro](/en/guides/cms/wordpress/), which means you will keep using your WordPress dashboard for writing your posts. Your content will be managed at WordPress, but all other aspects of your Astro site will be built in your code editing environment, and you will [deploy your Astro site](/en/guides/deploy/) separately from your WordPress site. (Be sure to update your domain at your host to keep the same website URL!)
You may wish to take [Astro's Build a Blog Tutorial](/en/tutorial/0-introduction/) if you are new to working in a code editor and using GitHub to store and deploy your site. It will walk you through all the accounts and setup you need! You will also learn how to [build Astro components yourself](/en/tutorial/3-components/), and it will show you how to [add blog posts directly in Astro](/en/tutorial/2-pages/2/) if you choose not to use WordPress to write your content.
If you want to move all of your existing post content to Astro, you may find this [tool for exporting Markdown from WordPress helpful](https://github.com/lonekorean/wordpress-export-to-markdown). You may need to make some adjustments to the result if you have to [convert a large or complicated WordPress site to Markdown](https://swizec.com/blog/how-to-export-a-large-wordpress-site-to-markdown/).
To convert other types of sites, such as a portfolio or documentation site, see more official starter templates on [astro.new](https://astro.new). You'll find a link to each project's GitHub repository, as well as one-click links to open a working project in IDX, StackBlitz, CodeSandbox and Gitpod online development environments.
## Community Resources
<CardGrid>
<LinkCard title="Goodbye Wordpress, hello Astro!" href="https://trib.tv/posts/2025/wordpress-to-astro/" />
<LinkCard title="How I Migrated from Wordpress to Astro" href="https://itsthatlady.dev/blog/migrate-from-wordpress-to-astro/" />
<LinkCard title="How and Why I Moved My Blog from WordPress to Astro and Markdown" href="https://levelup.gitconnected.com/how-and-why-i-moved-my-blog-from-wordpress-to-astro-and-markdown-3549672d5a86" />
<LinkCard title="How I Migrated From Wordpress to Astro: Boosted Pagespeed Scores to 100% and Cut 100% Hosting cost" href="https://devaradise.com/wordpress-to-static-website-astro/" />
<LinkCard title="WordPress to Astro site conversion" href="https://share.transistor.fm/s/d86496cd" />
<LinkCard title="How to Convert a Wordpress blog to an Astro Static Site" href="https://blog.okturtles.org/2024/10/convert-wordpress-to-static-site/" />
<LinkCard title="Why I switched from WordPress to Astro" href="https://dev.to/fratzinger/why-i-switched-from-wordpress-to-astro-5ge" />
<LinkCard title="Why I ditched WordPress for Astro" href="https://vbartalis.xyz/en/blog/why-i-ditched-wordpress-for-astro-js/" />
<LinkCard title="DeWP: utility to use your WordPress data in Astro projects" href="https://delucis.github.io/dewp/" />
<LinkCard title="Astro vs. WordPress: Rendering Patterns of the Modern Web" href="https://andrewkepson.com/blog/headless-wordpress/astro-vs-wordpress-rendering-patterns/" />
</CardGrid>
:::note[Have a resource to share?]
If you found (or made!) a helpful video or blog post about converting a WordPress site to Astro, [add it to this list](https://github.com/withastro/docs/edit/main/src/content/docs/en/guides/migrate-to-astro/from-wordpress.mdx)!
:::
---
File: /src/content/docs/en/guides/migrate-to-astro/index.mdx
---
---
title: Migrate an existing project to Astro
description: Some tips and tricks for converting your site to Astro.
sidebar:
label: Site migration overview
i18nReady: true
---
import MigrationGuidesNav from '~/components/MigrationGuidesNav.astro';
**Ready to convert your site to Astro?** See one of our guides for migration tips.
## Migration Guides
<MigrationGuidesNav />
Note that many of these pages are **stubs**: they're collections of resources waiting for your contribution!
## Why migrate your site to Astro?
Astro provides many benefits: performance, simplicity, and many of the features you want built right into the framework. When you do need to extend your site, Astro provides several [official and 3rd-party community integrations](https://astro.build/integrations).
Migrating may be less work than you think!
Depending on your existing project, you may be able to use your existing:
- [UI framework components](/en/guides/framework-components/) directly in Astro.
- [CSS stylesheets or libraries](/en/guides/styling/) including Tailwind.
- [Markdown/MDX files](/en/guides/markdown-content/), configured using your existing [remark and rehype plugins](/en/guides/markdown-content/#markdown-plugins).
- [Content from a CMS](/en/guides/cms/) through an integration or API.
## Which projects can I convert to Astro?
[Many existing sites can be built with Astro](/en/concepts/why-astro/). Astro is ideally suited for your existing content-based sites like blogs, landing pages, marketing sites and portfolios. Astro integrates with several popular headless CMSes, and allows you to connect eCommerce shop carts.
Astro allows you have a fully statically-generated website, a dynamic app with routes rendered on demand, or a combination of both with [complete control over your project rendering](/en/guides/on-demand-rendering/), making it a great replacement for SSGs or for sites that need to fetch some page data on the fly.
## How will my project design change?
Depending on your existing project, you may need to think differently about:
- Designing in [Astro Islands](/en/concepts/islands/#what-is-an-island) to avoid sending unnecessary JavaScript to the browser.
- Providing client-side interactivity with [client-side `<script>` tags](/en/guides/client-side-scripts/) or [UI framework components](/en/guides/framework-components/).
- Managing [shared state](/en/recipes/sharing-state-islands/) with Nano Stores or local storage instead of app-wide hooks or wrappers.
---
File: /src/content/docs/en/guides/upgrade-to/v1.mdx
---
---
title: Legacy v0.x Upgrade Guide
description: Archived guide documenting changes between pre-v1 versions of Astro
sidebar:
label: v1.0
i18nReady: false
---
import { Steps } from '@astrojs/starlight/components';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
This guide will help you upgrade through breaking changes in pre-v1 versions of Astro.
You can update your project's version of Astro to the latest version using your package manager. If you're using Astro integrations, you'll also want to update those to the latest version.
<PackageManagerTabs>
<Fragment slot="npm">
```shell
# updates the astro dependency:
npm upgrade astro
# or, to update all dependencies:
npm upgrade
```
</Fragment>
<Fragment slot="pnpm">
```shell
# updates the astro dependency:
pnpm upgrade astro
# or, to update all dependencies:
pnpm upgrade
```
</Fragment>
<Fragment slot="yarn">
```shell
# updates the astro dependency:
yarn upgrade astro
# or, to update all dependencies:
yarn upgrade
```
</Fragment>
</PackageManagerTabs>
Read the guide below for major highlights and instructions on how to handle breaking changes.
## Astro 1.0
Astro v1.0 introduces some changes that you should be aware of when migrating from v0.x and v1.0-beta releases. See below for more details.
### Updated: Vite 3
Astro v1.0 has upgraded from Vite 2 to [Vite 3](https://vite.dev/). We've handled most of the upgrade for you inside of Astro; however, some subtle Vite behaviors may still change between versions. Refer to the official [Vite Migration Guide](https://vite.dev/guide/migration.html#general-changes) if you run into trouble.
### Deprecated: `Astro.canonicalURL`
You can now use the new [`Astro.url`](/en/reference/api-reference/#url) helper to construct your own canonical URL from the current page/request URL.
```js del="Astro.canonicalURL" ins="new URL(Astro.url.pathname, Astro.site)"
// Before:
const canonicalURL = Astro.canonicalURL;
// After:
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
```
### Changed: Scoped CSS specificity
[Specificity](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity) will now be preserved in scoped CSS styles. This change will cause most scoped styles to _happen_ to take precedence over global styles. But, this behavior is no longer explicitly guaranteed.
Technically, this is accomplished using [the `:where()` pseudo-class](https://developer.mozilla.org/en-US/docs/Web/CSS/:where) instead of using classes directly in Astro’s CSS output.
Let’s use the following style block in an Astro component as an example:
```astro
<style>
div { color: red; } /* 0-0-1 specificity */
</style>
```
Previously, Astro would transform this into the following CSS, which has a specificity of `0-1-1` — a higher specificity than the source CSS:
```css del=".astro-XXXXXX"
div.astro-XXXXXX { color: red; } /* 0-1-1 specificity */
```
Now, Astro wraps the class selector with `:where()`, maintaining the authored specificity:
```css ins=":where(.astro-XXXXXX)"
div:where(.astro-XXXXXX) { color: red; } /* 0-0-1 specificity */
```
The previous specificity increase made it hard to combine scoped styles in Astro with other CSS files or styling libraries (e.g. Tailwind, CSS Modules, Styled Components, Stitches). This change will allow Astro's scoped styles to work consistently alongside them while still preserving the exclusive boundaries that prevent styles from applying outside the component.
:::caution
When upgrading, please visually inspect your site output to make sure everything is styled as expected. If not, find your scoped style and increase the selector specificity manually to match the old behavior.
:::
### Deprecated: Components and JSX in Markdown
Astro no longer supports components or JSX expressions in Markdown pages by default. For long-term support you should migrate to the [`@astrojs/mdx`](/en/guides/integrations-guide/mdx/) integration.
To make migrating easier, a new `legacy.astroFlavoredMarkdown` flag (removed in v2.0) can be used to re-enable previous Markdown features.
### Converting existing `.md` files to `.mdx`
If you're not familiar with MDX, here are some steps you can follow to quickly convert an existing "Astro Flavored Markdown" file to MDX. As you learn more about MDX, feel free to explore other ways of writing your pages!
<Steps>
1. Install the [`@astrojs/mdx`](/en/guides/integrations-guide/mdx/) integration.
2. Change your existing `.md` file extensions to `.mdx`
3. Remove any `setup:` properties from your frontmatter, and write any import statements below the frontmatter instead.
```mdx del={4-5} ins={10}
// src/pages/posts/my-post.mdx
---
layout: '../../layouts/BaseLayout.astro'
setup: |
import ReactCounter from '../../components/ReactCounter.jsx'
title: 'Migrating to MDX'
date: 2022-07-26
tags: ["markdown", "mdx", "astro"]
---
import ReactCounter from '../../components/ReactCounter.jsx'
# {frontmatter.title}
Here is my counter component, working in MDX:
<ReactCounter client:load />
```
4. Update any `Astro.glob()` statements that currently return `.md` files so that they will now return your `.mdx` files.
:::caution
The object returned when importing `.mdx` files (including using Astro.glob) differs from the object returned when importing `.md` files. However, `frontmatter`, `file`, and `url` work identically.
:::
5. Update any use of the `<Content />` component to use the default export when importing MDX:
```astro title="src/pages/index.astro" ins=".default"
---
// Multiple imports with Astro.glob
const mdxPosts = await Astro.glob('./posts/*.mdx');
---
{mdxPosts.map(Post => <Post.default />)}
```
```astro title="src/pages/index.astro" ins="default as"
---
// Import a single page
import { default as About } from './about.mdx';
---
<About />
```
</Steps>
:::tip
While you are transitioning to MDX, you may wish to enable the `legacy.astroFlavoredMarkdown` flag (removed in v2.0) and include both **`.md` and `.mdx`** files, so that your site continues to function normally even before all your files have been converted. Here is one way you can do that:
```astro
---
const mdPosts = await Astro.glob('../pages/posts/*.md');
const mdxPosts = await Astro.glob('../pages/posts/*.mdx');
const allPosts = [...mdxPosts, ...mdPosts];
---
```
:::
### `<Markdown />` Component Removed
Astro's built-in `<Markdown />` component has been moved to a separate package. To continue using this component, you will now need to install `@astrojs/markdown-component` and update your imports accordingly. For more details, see [the `@astrojs/markdown` README](https://github.com/withastro/astro/tree/main/packages/markdown/component).
:::tip
Astro now has support for [MDX](https://mdxjs.com/) through our [MDX integration](https://github.com/withastro/astro/tree/main/packages/integrations/mdx). MDX gives you the ability to include both Markdown and imported components in the same file. MDX can be good alternative for the `<Markdown />` component due to its large community and stable APIs.
:::
## Migrate to v1.0.0-beta
On April 4, 2022 we released the Astro 1.0 Beta! 🎉
If you are coming from v0.25 or earlier, make sure you have read and followed the [v0.26 Migration Guide](#migrate-to-v026) below, which contained several major breaking changes.
The `v1.0.0-beta.0` release of Astro contained no breaking changes. Below are small changes that were introduced during the beta period.
### Changed: RSS Feeds
RSS feeds should now be generated using the `@astrojs/rss` package, as described in our [RSS guide](/en/recipes/rss/).
## Migrate to v0.26
### New Configuration API
Our Configuration API has been redesigned to solve a few glaring points of confusion that had built up over the last year. Most of the configuration options have just been moved or renamed, which will hopefully be a quick update for most users. A few options have been refactored more heavily, and may require a few additional changes:
- `.buildOptions.site` has been replaced with `.site` (your deployed domain) and a new `.base` (your deployed subpath) option.
- `.markdownOptions` has been replaced with `.markdown`, a mostly similar config object with some small changes to simplify Markdown configuration.
- `.sitemap` has been moved into the [@astrojs/sitemap](https://www.npmjs.com/package/@astrojs/sitemap) integration.
If you run Astro with legacy configuration, you will see a warning with instructions on how to update. See our updated [Configuration Reference](/en/reference/configuration-reference/) for more information on upgrading.
Read [RFC0019](https://github.com/withastro/rfcs/blob/main/proposals/0019-config-finalization.md) for more background on these changes.
### New Markdown API
Astro v0.26 releases a brand new Markdown API for your content. This included three major user-facing changes:
- You can now `import`/`import()` markdown content directly using an ESM import.
- A new `Astro.glob()` API, for easier glob imports (especially for Markdown).
- **BREAKING CHANGE:** `Astro.fetchContent()` has been removed and replaced by `Astro.glob()`
- **BREAKING CHANGE:** Markdown objects have an updated interface.
```js del={2} ins={4}
// v0.25
let allPosts = Astro.fetchContent('./posts/*.md');
// v0.26+
let allPosts = await Astro.glob('./posts/*.md');
```
When migrating, be careful about the new Markdown object interface. Frontmatter, for example, has been moved to the `.frontmatter` property, so references like `post.title` should change to `post.frontmatter.title`.
This should solve many issues for Markdown users, including some nice performance boosts for larger sites.
Read [RFC0017](https://github.com/withastro/rfcs/blob/main/proposals/0017-markdown-content-redesign.md) for more background on these changes.
### New Default Script Behavior
`<script>` tags in Astro components are now built, bundled and optimized by default. This completes a long-term move to make our Astro component syntax more consistent, matching the default-optimized behavior our `<style>` tags have today.
This includes a few changes to be aware of:
- **BREAKING:** `<script hoist>` is the new default `<script>` behavior. The `hoist` attribute has been removed. To use the new default behaviour, make sure there are no other attributes on the `<script>` tag. For example, remove `type="module"` if you were using it before.
- New `<script is:inline>` directive, to revert a `<script>` tag to previous default behavior (unbuilt, unbundled, untouched by Astro).
- New `<style is:inline>` directive, to leave a style tag inline in the page template (similar to previous `<script>` behavior).
- New `<style is:global>` directive to replace `<style global>` in a future release.
```js del={2} ins={4}
// v0.25
<script hoist type="module">
// v0.26+
<script>
```
See how to use [client-side scripts](/en/guides/client-side-scripts/) in Astro for full details.
Read [RFC0016](https://github.com/withastro/rfcs/blob/main/proposals/0016-style-script-defaults.md) for more background on these changes.
### Updated `Astro.request` API
`Astro.request` has been changed from our custom object to a standard `Request` object. This is part of a project to use more web standard APIs, especially where SSR is concerned.
This includes a few changes to be aware of:
- Change `Astro.request` to become a [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object.
- Move `Astro.request.params` to `Astro.params`.
- Move `Astro.request.canonicalURL` to `Astro.canonicalURL`.
Read [RFC0018](https://github.com/withastro/rfcs/blob/main/proposals/0018-astro-request.md) for more background on these changes.
### Other Changes
- Improve `Astro.slots` API to support passing arguments to function-based slots. This allows for more ergonomic utility components that accept a callback function as a child.
- Update CLI output formatting, especially around error reporting.
- Update `@astrojs/compiler`, fixing some bugs related to RegExp usage in frontmatter
## Migrate to v0.25
### Astro Integrations
The `renderers` config has been replaced by a new, official integration system! This unlocks some really exciting new features for Astro. You can read our [Using Integrations](/en/guides/integrations-guide/) guide for more details on how to use this new system.
Integrations replace our original `renderers` concept, and come with a few breaking changes and new defaults for existing users. These changes are covered below.
#### Removed: Built-in Framework Support
Previously, React, Preact, Svelte, and Vue were all included with Astro by default. Starting in v0.25.0, Astro no longer comes with any built-in renderers. If you did not have a `renderers` configuration entry already defined for your project, you will now need to install those frameworks yourself.
Read our [step-by-step walkthrough](/en/guides/integrations-guide/) to learn how to add a new Astro integration for the framework(s) that you currently use.
#### Deprecated: Renderers
:::note
Read this section if you have custom "renderers" already defined in your configuration file.
:::
The new integration system replaces the previous `renderers` system, including the published `@astrojs/renderer-*` packages on npm. Going forward, `@astrojs/renderer-react` becomes `@astrojs/react`, `@astrojs/renderer-vue` becomes `@astrojs/vue`, and so on.
**To migrate:** update Astro to `v0.25.0` and then run `astro dev` or `astro build` with your old configuration file containing the outdated `"renderers"` config. You will immediately see a notice telling you the exact changes you need to make to your `astro.config.mjs` file, based on your current config. You can also update your packages yourself, using the table below.
For a deeper walkthrough, read our [step-by-step guide](/en/guides/integrations-guide/) to learn how to replace existing renderers with a new Astro framework integration.
```shell add={3-4}
# Install your new integrations and frameworks:
# (Read the full walkthrough: https://docs.astro.build/en/guides/integrations-guide)
npm install @astrojs/lit lit
npm install @astrojs/react react react-dom
```
```js ins={3-4,8} del={7}
// Then, update your `astro.config.mjs` file:
// (Read the full walkthrough: https://docs.astro.build/en/guides/integrations-guide)
import lit from '@astrojs/lit';
import react from '@astrojs/react';
export default {
renderers: ['@astrojs/renderer-lit', '@astrojs/renderer-react'],
integrations: [lit(), react()],
}
```
| Deprecated Renderers on npm | v0.25+ Integrations on npm |
| --------------------------- | -------------------------- |
| @astrojs/renderer-react | @astrojs/react |
| @astrojs/renderer-preact | @astrojs/preact |
| @astrojs/renderer-solid | @astrojs/solid-js |
| @astrojs/renderer-vue | @astrojs/vue |
| @astrojs/renderer-svelte | @astrojs/svelte |
#### Handling Peer Dependencies
:::note
Read this section if: You are on Node v14 **or** if you use any package manager other than npm.
:::
Unlike the old renderers, integrations no longer mark the frameworks themselves ("react", "svelte", "vue", etc.) as direct dependencies of the integration. Instead, you should now install your framework packages *in addition to* your integrations.
```shell ins="react react-dom"
# Example: Install integrations and frameworks together
npm install @astrojs/react react react-dom
```
If you see a `"Cannot find package 'react'"` (or similar) warning when you start up Astro, that means that you need to install that package into your project. See our [note on peer dependencies](/en/guides/troubleshooting/#cannot-find-package-x) in the troubleshooting guide for more information.
If you are using `npm` & Node v16+, then this may be automatically handled for you by `npm`, since the latest version of `npm` (v7+) installs peer dependencies like this for you automatically. In that case, installing a framework like "react" into your project is an optional but still recommended step.
### Updated: Syntax Highlighting
We love to find sensible defaults that "just work" out-of-the-box. As part of this, we decided to make [Shiki](https://github.com/shikijs/shiki) our new default syntax highlighter. This comes pre-configured with the `github-dark` theme, providing zero-config highlighting in your code blocks without extraneous CSS classes, stylesheets, or client-side JS.
Check our new syntax highlighting docs for full details. **If you prefer to keep Prism as your syntax highlighter,** set the `syntaxHighlight` option to `'prism'` in your project's markdown configuration.
#### The `<Prism />` component has a new home
As part of our mission to keep Astro core as lean as possible, we've moved the built-in `Prism` component out of `astro/components` and into the `@astrojs/prism` package. You can now import this component from `@astrojs/prism` like so:
```astro
---
import { Prism } from '@astrojs/prism';
---
```
Since the `@astrojs/prism` package is still bundled with `astro` core, you won't need to install anything new, nor add Prism as an integration! However, note that we _do_ plan to extract `@astrojs/prism` (and Prism syntax highlighting in general) to a separate, installable package in the future. See the `<Prism />` component API reference for more details.
### CSS Parser Upgrade
Our internal CSS parser has been updated, and comes with better support for advanced CSS syntax, like container queries. This should be a mostly invisible change for most users, but hopefully advanced users will enjoy the new CSS feature support.
## Migrate to v0.24
:::note
The new build strategy is on by default on 0.24. If you run into a problem you can continue using the old build strategy by passing the `--legacy-build` flag. Please [open an issue](https://github.com/withastro/astro/issues/new/choose) so that we can resolve problems with the new build strategy.
:::
0.24 introduced a new *static build* strategy that changes the behavior of a few features. In previous versions of Astro this was available behavior with an opt-in flag: `--experimental-static-build`.
To migrate for the transition, be aware of the following changes that will be required to move to this new build engine. You can make these changes to your codebase at any time so that you are ready ahead of schedule.
### Deprecated: `Astro.resolve()`
`Astro.resolve()` allows you to get resolved URLs to assets that you might want to reference in the browser. This was most commonly used inside of `<link>` and `<img>` tags to load CSS files and images as needed. Unfortunately, this will no longer work due to Astro now building assets at *build time* rather than at *render time*. You'll want to upgrade your asset references to one of the following future-proof options available going forward:
#### How to Resolve CSS Files
**1. ESM Import (Recommended)**
**Example:** `import './style.css';`
**When to use this:** If your CSS file lives inside of the `src/` directory, and you want automatic CSS build and optimization features.
Use an ESM import to add some CSS onto the page. Astro detects these CSS imports and then builds, optimizes, and adds the CSS to the page automatically. This is the easiest way to migrate from `Astro.resolve()` while keeping the automatic building/bundling that Astro provides.
```astro
---
// Example: Astro will include and optimize this CSS for you automatically
import './style.css';
---
<html><!-- Your page here --></html>
```
Importing CSS files should work anywhere that ESM imports are supported, including:
- JavaScript files
- TypeScript files
- Astro component frontmatter
- non-Astro components like React, Svelte, and others
When a CSS file is imported using this method, any `@import` statements are also resolved and inlined into the imported CSS file. All `url()` references are also resolved relative to the source file, and any `url()` referenced assets will be included in the final build.
**2. Absolute URL Path**
**Example:** `<link href="/style.css">`
**When to use this:** If your CSS file lives inside of `public/`, and you prefer to create your HTML `link` element yourself.
You can reference any file inside of the `public/` directory by absolute URL path in your component template. This is a good option if you want to control the `<link>` tag on the page yourself. However, this approach also skips the CSS processing, bundling and optimizations that are provided by Astro when you use the `import` method described above.
We recommend using the `import` approach over the absolute URL approach since it provides the best possible CSS performance and features by default.
#### How to Resolve JavaScript Files
**1. Absolute URL Path**
**Example:** `<script src="/some-external-script.js" />`
**When to use this:** If your JavaScript file lives inside of `public/`.
You can reference any file inside of the `public/` directory by absolute URL path in your Astro component templates. This is a good default option for external scripts because it lets you control the `<script>` tag on the page yourself.
Note that this approach skips the JavaScript processing, bundling and optimizations that are provided by Astro when you use the `import` method described below. However, this may be preferred for any external scripts that have already been published and minified separately from Astro. If your script was downloaded from an external source, then this method is probably preferred.
**2. ESM Import via `<script hoist>`**
**Example:** `<script hoist>import './some-external-script.js';</script>`
**When to use this:** If your external script lives inside of `src/` _and_ it supports the ESM module type.
Use an ESM import inside of a `<script hoist>` element in your Astro template, and Astro will include the JavaScript file in your final build. Astro detects these JavaScript client-side imports and then builds, optimizes, and adds the JavaScript to the page automatically. This is the easiest way to migrate from `Astro.resolve()` while keeping the automatic building/bundling that Astro provides.
```astro
<script hoist>
import './some-external-script.js';
</script>
```
Note that Astro will bundle this external script with the rest of your client-side JavaScript, and load it in the `type="module"` script context. Some older JavaScript files may not be written for the `module` context, in which case they may need to be updated to use this method.
#### How to Resolve Images & Other Assets
**1. Absolute URL Path (Recommended)**
**Example:** `<img src="/penguin.png">`
**When to use this:** If your asset lives inside of `public/`.
If you place your images inside of `public/` you can safely reference them by absolute URL path directly in your component templates. This is the simplest way to reference an asset that you can use today, and it is recommended for most users who are getting started with Astro.
**2. ESM Import**
**Example:** `import imgUrl from './penguin.png'`
**When to use this:** If your asset lives inside of the `src/` directory, and you want automatic optimization features like filename hashing.
This works inside of any JavaScript or Astro component, and returns a resolved URL to the final image. Once you have the resolved URL, you can use it anywhere inside of the component template.
```astro
---
// Example: Astro will include this image file in your final build
import imgUrl from './penguin.png';
---
<img src={imgUrl} />
```
Similar to how Astro handles CSS, the ESM import allows Astro to perform some simple build optimizations for you automatically. For example, any asset inside of `src/` that is imported using an ESM import (ex: `import imgUrl from './penguin.png'`) will have its filename hashed automatically. This can let you cache the file more aggressively on the server, improving user performance. In the future, Astro may add more optimizations like this.
**Tip:** If you dislike static ESM imports, Astro also supports dynamic ESM imports. We only recommend this option if you prefer this syntax: `<img src={(await import('./penguin.png')).default} />`.
### Deprecated: `<script>` Default Processing
Previously, all `<script>` elements were read from the final HTML output and processed + bundled automatically. This behavior is no longer the default. Starting in 0.24, you must opt-in to `<script>` element processing via the `hoist` attribute. The `type="module"` is also required for hoisted modules.
```astro
<script>
// Will be rendered into the HTML exactly as written!
// ESM imports will not be resolved relative to the file.
</script>
<script type="module" hoist>
// Processed! Bundled! ESM imports work, even to npm packages.
</script>
```
## Migrate to v0.23
### Missing Sass Error
```
Preprocessor dependency "sass" not found. Did you install it?
```
In our quest to reduce npm install size, we've moved [Sass](https://sass-lang.com/) out to an optional dependency. If you use Sass in your project, you'll want to make sure that you run `npm install sass --save-dev` to save it as a dependency.
### Deprecated: Unescaped HTML
In Astro v0.23+, unescaped HTML content in expressions is now deprecated.
In future releases, content within expressions will have strings escaped to protect against unintended HTML injection.
```astro del={1} ins={2}
<h1>{title}</h1> <!-- <h1>Hello <strong>World</strong></h1> -->
<h1>{title}</h1> <!-- <h1>Hello &lt;strong&gt;World&lt;/strong&gt;</h1> -->
```
To continue injecting unescaped HTML, you can now use `set:html`.
```astro del={1} ins={2}
<h1>{title}</h1>
<h1 set:html={title} />
```
To avoid a wrapper element, `set:html` can work alongside `<Fragment>`.
```astro del={1} ins={2}
<h1>{title}!</h1>
<h1><Fragment set:html={title}>!</h1>
```
You can also protect against unintended HTML injection with `set:text`.
```astro
<h1 set:text={title} /> <!-- <h1>Hello &lt;strong&gt;World&lt;/strong&gt;</h1> -->
```
## Migrate to v0.21
### Vite
Starting in v0.21, Astro is built with [Vite].
As a result, configurations written in `snowpack.config.mjs` should be moved into `astro.config.mjs`.
```js
// @ts-check
/** @type {import('astro').AstroUserConfig} */
export default {
renderers: [],
vite: {
plugins: [],
},
};
```
To learn more about configuring Vite, please visit their [configuration guide](https://vite.dev/config/).
#### Vite Plugins
In Astro v0.21+, Vite plugins may be configured within `astro.config.mjs`.
```js ins={4-6}
import { imagetools } from 'vite-imagetools';
export default {
vite: {
plugins: [imagetools()],
},
};
```
To learn more about Vite plugins, please visit their [plugin guide](https://vite.dev/guide/using-plugins.html).
#### Vite Changes to Renderers
In Astro v0.21+, plugins should now use `viteConfig()`.
```js del={8-9} ins={2,10-23}
// renderer-svelte/index.js
import { svelte } from '@sveltejs/vite-plugin-svelte';
export default {
name: '@astrojs/renderer-svelte',
client: './client.js',
server: './server.js',
snowpackPlugin: '@snowpack/plugin-svelte',
snowpackPluginOptions: { compilerOptions: { hydratable: true } },
viteConfig() {
return {
optimizeDeps: {
include: ['@astrojs/renderer-svelte/client.js', 'svelte', 'svelte/internal'],
exclude: ['@astrojs/renderer-svelte/server.js'],
},
plugins: [
svelte({
emitCss: true,
compilerOptions: { hydratable: true },
}),
],
};
},
}
```
To learn more about Vite plugins, please visit their [plugin guide](https://vite.dev/guide/using-plugins.html).
:::note
In prior releases, these were configured with `snowpackPlugin` or `snowpackPluginOptions`.
:::
### Aliasing
In Astro v0.21+, import aliases can be added in `tsconfig.json`.
```json add={4-6}
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/components/*": ["src/components/*"]
}
}
}
```
### File Extensions in Imports
In Astro v0.21+, files need to be referenced by their actual extension, exactly as it is on disk. In this example, `Div.tsx` would need to be referenced as `Div.tsx`, not `Div.jsx`.
```js del={1} ins={2}
import Div from './Div.jsx' // Astro v0.20
import Div from './Div.tsx' // Astro v0.21
```
This same change applies to a compile-to-css file like `Div.scss`:
```astro del={1} ins={2}
<link rel="stylesheet" href={Astro.resolve('./Div.css')}>
<link rel="stylesheet" href={Astro.resolve('./Div.scss')}>
```
### Removed: Components in Frontmatter
Previously, you could create mini Astro Components inside of the Astro Frontmatter, using JSX syntax instead of Astro’s component syntax. This was always a bit of a hack, but in the new compiler it became impossible to support. We hope to re-introduce this feature in a future release of Astro using a different, non-JSX API.
To migrate to v0.21+, please convert all JSX Astro components (that is, any Astro components created inside of another component’s frontmatter) to standalone components.
### Styling Changes
#### Autoprefixer
Autoprefixer is no longer run by default. To enable:
<Steps>
1. Install the latest version (`npm install autoprefixer`)
2. Create a `postcss.config.cjs` file at the root of your project with:
```js
module.exports = {
plugins: {
autoprefixer: {},
},
};
```
</Steps>
#### Tailwind CSS
Ensure you have PostCSS installed. This was optional in previous releases, but is required now:
<Steps>
1. Install the latest version of postcss (`npm install -D postcss`)
2. Create a `postcss.config.cjs` file at the root of your project with:
```js
module.exports = {
plugins: {
tailwindcss: {},
},
};
```
For more information, read the [Tailwind CSS documentation](https://tailwindcss.com/docs/installation#add-tailwind-as-a-post-css-plugin)
</Steps>
### Known Issues
#### Imports on top
In Astro v0.21+, a bug has been introduced that requires imports inside components to be at the top of your frontmatter.
```astro
---
import Component from '../components/Component.astro'
const whereShouldIPutMyImports = "on top!"
---
```
[vite]: https://vite.dev
---
File: /src/content/docs/en/guides/upgrade-to/v2.mdx
---
---
title: Upgrade to Astro v2
description: How to upgrade your project to the latest version of Astro.
sidebar:
label: v2.0
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import { FileTree } from '@astrojs/starlight/components';
import { Steps } from '@astrojs/starlight/components';
This guide will help you migrate from Astro v1 to Astro v2.
Need to upgrade an older project to v1? See our [older migration guide](/en/guides/upgrade-to/v1/).
## Upgrade Astro
Update your project's version of Astro to the latest version using your package manager. If you're using Astro integrations, please also update those to the latest version.
<PackageManagerTabs>
<Fragment slot="npm">
```shell
# Upgrade to Astro v2.x
npm install astro@latest
# Example: upgrade React and Tailwind integrations
npm install @astrojs/react@latest @astrojs/tailwind@latest
```
</Fragment>
<Fragment slot="pnpm">
```shell
# Upgrade to Astro v2.x
pnpm add astro@latest
# Example: upgrade React and Tailwind integrations
pnpm add @astrojs/react@latest @astrojs/tailwind@latest
```
</Fragment>
<Fragment slot="yarn">
```shell
# Upgrade to Astro v2.x
yarn add astro@latest
# Example: upgrade React and Tailwind integrations
yarn add @astrojs/react@latest @astrojs/tailwind@latest
```
</Fragment>
</PackageManagerTabs>
## Astro v2.0 Breaking Changes
Astro v2.0 includes some breaking changes, as well as the removal of some previously deprecated features. If your project doesn't work as expected after upgrading to v2.0, check this guide for an overview of all breaking changes and instructions on how to update your codebase.
See [the changelog](https://github.com/withastro/astro/blob/main/packages/astro/CHANGELOG.md) for full release notes.
### Removed: Support for Node 14
Node 14 is scheduled to reach its End of Life in April 2023.
Astro v2.0 drops Node 14 support entirely, so that all Astro users can take advantage of Node's more modern features.
#### What should I do?
Check that both your development environment and your deployment environment are using **Node `16.12.0` or later**.
<Steps>
1. Check your local version of Node using:
```sh
node -v
```
If your local development environment needs upgrading, [install Node](https://nodejs.org/en/download/).
2. Check your [deployment environment's](/en/guides/deploy/) own documentation to verify that they support Node 16.
You can specify Node `16.12.0` for your Astro project either in a dashboard configuration setting, or a `.nvmrc` file.
</Steps>
### Reserved: `src/content/`
Astro v2.0 now includes the Collections API for organizing your Markdown and MDX files into [content collections](/en/guides/content-collections/). This API reserves `src/content/` as a special folder.
#### What should I do?
Rename an existing `src/content/` folder to avoid conflicts. This folder, if it exists, can now only be used for [content collections](/en/guides/content-collections/).
### Changed: `Astro.site` trailing slash
In v1.x, Astro ensured the URL you set as `site` in `astro.config.mjs` always had a trailing slash when accessed using `Astro.site`.
Astro v2.0 no longer modifies the value of `site`. `Astro.site` will use the exact value defined, and a trailing slash must be specified if desired.
#### What should I do?
In `astro.config.mjs`, add a trailing slash to the URL set in `site`.
```js del={5} ins={6}
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
site: 'https://example.com',
site: 'https://example.com/',
});
```
### Changed: `_astro/` folder for build assets
In v1.x, assets were built to various locations, including `assets/`, `chunks/`, and to the root of the build output.
Astro v2.0 moves and unifies the location of all build output assets to a new `_astro/` folder.
<FileTree>
- dist/
- _astro
- client.9218e799.js
- index.df3f880e0.css
</FileTree>
You can control this location with the [new `build.assets` configuration option](/en/reference/configuration-reference/#buildassets).
#### What should I do?
Update your deployment platform configuration if it relies on the location of these assets.
### Changed: Markdown plugin configuration
#### Removed: `extendDefaultPlugins`
In v1.x, Astro used `markdown.extendDefaultPlugins` to re-enable Astro's default plugins when adding your own Markdown plugins.
Astro v2.0 removes this configuration option entirely because its behavior is now the default.
Applying remark and rehype plugins in your Markdown configuration **no longer disables Astro's default plugins**. GitHub-Flavored Markdown and Smartypants are now applied whether or not custom `remarkPlugins` or `rehypePlugins` are configured.
##### What should I do?
Remove `extendDefaultPlugins` in your configuration. This is now Astro's default behavior in v2.0, and you can delete this line without any replacement.
```js del={6}
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
markdown: {
extendDefaultPlugins,
}
});
```
#### Added: `gfm` and `smartypants`
In v1.x, you could choose to disable both of Astro's default Markdown plugins (GitHub-Flavored Markdown and SmartyPants) by setting `markdown.extendDefaultPlugins: false`.
Astro v2.0 replaces `markdown.extendDefaultPlugins: false` with separate Boolean options to individually control each of Astro's built-in default Markdown plugins. These are enabled by default and can be set to `false` independently.
##### What should I do?
Remove `extendDefaultPlugins: false` and add the flags to disable each plugin individually instead.
- `markdown.gfm: false` disables GitHub-Flavored Markdown
- `markdown.smartypants: false` disables SmartyPants
```js del={6} ins={7-8}
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
markdown: {
extendDefaultPlugins: false,
smartypants: false,
gfm: false,
}
});
```
### Changed: MDX plugin configuration
#### Replaced: `extendPlugins` changed to `extendMarkdownConfig`
In v1.x, the MDX integration’s `extendPlugins` option managed how your MDX files should inherit your Markdown configuration: all your Markdown configuration (`markdown`), or Astro's default plugins only (`default`).
Astro v2.0 replaces the behavior controlled by `mdx.extendPlugins` with three new, independently-configurable options that are `true` by default:
- **[`mdx.extendMarkdownConfig`](/en/guides/integrations-guide/mdx/#extendmarkdownconfig)** to inherit all or none of your Markdown configuration
- **`mdx.gfm`** to enable or disable GitHub-Flavored Markdown in MDX
- **`mdx.smartypants`** to enable or disable SmartyPants in MDX
##### What should I do?
Delete `extendPlugins: 'markdown'` in your configuration. This is now the default behavior.
```js del={8}
// astro.config.mjs
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
export default defineConfig({
integrations: [
mdx({
extendPlugins: 'markdown',
}),
],
});
```
Replace `extendPlugins: 'defaults'` with `extendMarkdownConfig: false` and add the separate options for GitHub-Flavored Markdown and SmartyPants to enable these default plugins individually in MDX.
```js del={8} ins={9-11}
// astro.config.mjs
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
export default defineConfig({
integrations: [
mdx({
extendPlugins: 'defaults',
extendMarkdownConfig: false,
smartypants: true,
gfm: true,
}),
],
});
```
#### Added: More MDX config options to match Markdown
Astro v2.0 allows you to now individually set [every available Markdown configuration option](/en/reference/configuration-reference/#markdown-options) (except `drafts`) separately in your MDX integration configuration.
```js
// astro.config.mjs
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
export default defineConfig({
markdown: {
remarkPlugins: [remarkPlugin1],
gfm: true,
},
integrations: [
mdx({
remarkPlugins: [remarkPlugin2],
gfm: false,
})
]
});
```
##### What should I do?
Revisit your Markdown and MDX configuration and compare your existing config with the new options available.
### Changed: Plugin access to frontmatter
In v1.x, remark and rehype plugins did not have access to user frontmatter. Astro merged plugin frontmatter with your file's frontmatter, without passing the file frontmatter to your plugins.
Astro v2.0 gives remark and rehype plugins access to user frontmatter via frontmatter injection. This allows plugin authors to modify a user's existing frontmatter, or compute new properties based on other properties.
#### What should I do?
Check any remark and rehype plugins you have written to see whether their behavior has changed. Note that `data.astro.frontmatter` is now the _complete_ Markdown or MDX document's frontmatter, rather than an empty object.
### Changed: RSS Configuration
In v1.x, Astro's RSS package allowed you to use `items: import.meta.glob(...)` to generate a list of RSS feed items. This usage is now deprecated and will eventually be removed.
Astro v2.0 introduces a `pagesGlobToRssItems()` wrapper to the `items` property.
#### What should I do?
Import, then wrap your existing function containing `import.meta.glob()` with the `pagesGlobToRssItems()` helper.
```js ins={3, 8, 10}
// src/pages/rss.xml.js
import rss, {
pagesGlobToRssItems
} from '@astrojs/rss';
export async function get(context) {
return rss({
items: await pagesGlobToRssItems(
import.meta.glob('./blog/*.{md,mdx}'),
),
});
}
```
### Changed: Svelte IDE support
Astro v2.0 requires a `svelte.config.js` file in your project if you are using [the `@astrojs/svelte` integration](/en/guides/integrations-guide/svelte/). This is needed to provide IDE autocompletion.
#### What should I do?
Add a `svelte.config.js` file to the root of your project:
```js
// svelte.config.js
import { vitePreprocess } from '@astrojs/svelte';
export default {
preprocess: vitePreprocess(),
};
```
For new users, this file will be added automatically when running `astro add svelte`.
### Removed: `legacy.astroFlavoredMarkdown`
In v1.0, Astro moved the old Astro-Flavored Markdown (also known as Components in Markdown) to a legacy feature.
Astro v2.0 removes the `legacy.astroFlavoredMarkdown` option completely. Importing and using components in `.md` files will no longer work.
#### What should I do?
Remove this legacy flag. It is no longer available in Astro.
```js del={3-5}
// astro.config.mjs
export default defineConfig({
legacy: {
astroFlavoredMarkdown: true,
},
})
```
If you were using this feature in v1.x, we recommend [using the MDX integration](/en/guides/integrations-guide/mdx/) which allows you to combine components and JSX expressions with Markdown syntax.
### Removed: `Astro.resolve()`
In v0.24, Astro deprecated `Astro.resolve()` for getting resolved URLs to assets that you might want to reference in the browser.
Astro v2.0 removes this option entirely. `Astro.resolve()` in your code will cause an error.
#### What should I do?
Resolve asset paths using `import` instead. For example:
```astro
---
// src/pages/index.astro
import 'style.css';
import imageUrl from './image.png';
---
<img src={imageUrl} />
```
### Removed: `Astro.fetchContent()`
In v0.26, Astro deprecated `Astro.fetchContent()` for fetching data from your local Markdown files.
Astro v2.0 removes this option entirely. `Astro.fetchContent()` in your code will cause an error.
#### What should I do?
Use `Astro.glob()` to fetch Markdown files, or convert to the [Content Collections](/en/guides/content-collections/) feature.
```astro
---
// src/pages/index.astro
const allPosts = await Astro.glob('./posts/*.md');
---
```
### Removed: `Astro.canonicalURL`
In v1.0, Astro deprecated `Astro.canonicalURL` for constructing a canonical URL.
Astro v2.0 removes this option entirely. `Astro.canonicalURL` in your code will cause an error.
#### What should I do?
Use `Astro.url` to construct a canonical URL.
```astro
---
// src/pages/index.astro
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
---
```
### Updated: Vite 4
Astro v2.0 upgrades from Vite 3 to [Vite 4](https://vite.dev/), released in December 2022.
#### What should I do?
There should be no changes to your code necessary! We've handled most of the upgrade for you inside of Astro; however, some subtle Vite behaviors may still change between versions.
Refer to the official [Vite Migration Guide](https://vite.dev/guide/migration.html) if you run into trouble.
## Astro v2.0 Experimental Flags Removed
Remove the following experimental flags from `astro.config.mjs`:
```js del={5-9}
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
experimental: {
contentCollections: true,
prerender: true,
errorOverlay: true,
},
})
```
These features are now available by default:
- [Content collections](/en/guides/content-collections/) as a way to manage your Markdown and MDX files with type-safety.
- [Prerendering individual pages to static HTML](/en/guides/on-demand-rendering/) when using SSR to improve speed and cacheability.
- A redesigned error message overlay.
## Known Issues
There are currently no known issues.
---
File: /src/content/docs/en/guides/upgrade-to/v3.mdx
---
---
title: Upgrade to Astro v3
description: How to upgrade your project to the latest version of Astro (v3.0).
sidebar:
label: v3.0
i18nReady: true
---
import { Steps } from '@astrojs/starlight/components';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
This guide will help you migrate from Astro v2 to Astro v3.
Need to upgrade an older project to v2? See our [older migration guide](/en/guides/upgrade-to/v2/).
## Upgrade Astro
Update your project's version of Astro to the latest version using your package manager. If you're using Astro integrations, please also update those to the latest version.
<PackageManagerTabs>
<Fragment slot="npm">
```shell
# Upgrade to Astro v3.x
npm install astro@latest
# Example: upgrade React and Tailwind integrations
npm install @astrojs/react@latest @astrojs/tailwind@latest
```
</Fragment>
<Fragment slot="pnpm">
```shell
# Upgrade to Astro v3.x
pnpm add astro@latest
# Example: upgrade React and Tailwind integrations
pnpm add @astrojs/react@latest @astrojs/tailwind@latest
```
</Fragment>
<Fragment slot="yarn">
```shell
# Upgrade to Astro v3.x
yarn add astro@latest
# Example: upgrade React and Tailwind integrations
yarn add @astrojs/react@latest @astrojs/tailwind@latest
```
</Fragment>
</PackageManagerTabs>
:::note[Need to continue?]
After upgrading Astro to the latest version, you may not need to make any changes to your project at all!
But, if you notice errors or unexpected behavior, please check below for what has changed that might need updating in your project.
:::
## Astro v3.0 Experimental Flags Removed
Remove the following experimental flags from `astro.config.mjs`:
```js del={5-8}
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
experimental: {
assets: true,
viewTransitions: true,
},
})
```
These features are now available by default:
- View Transitions for animated page transitions and persistent islands. See [view transitions API breaking changes and upgrading advice](#upgrade-view-transitions-to-v3) if you were using this experimental flag.
- A new image services API `astro:assets` for using images in Astro, including a new `<Image />` component and `getImage()` function. Please read the detailed [image upgrade advice](#upgrade-images-to-v3) **whether or not you were using this experimental flag** to see how this might affect your project.
Read more about these two exciting features and more in [the 3.0 Blog post](https://astro.build/blog/astro-3/)!
## Astro v3.0 Breaking Changes
Astro v3.0 includes some breaking changes, as well as the removal of some previously deprecated features. If your project doesn't work as expected after upgrading to v3.0, check this guide for an overview of all breaking changes and instructions on how to update your codebase.
See [the changelog](https://github.com/withastro/astro/blob/main/packages/astro/CHANGELOG.md) for full release notes.
### Removed: Support for Node 16
Node 16 is scheduled to reach its End of Life in September 2023.
Astro v3.0 drops Node 16 support entirely so that all Astro users can take advantage of Node's more modern features.
#### What should I do?
Check that both your development environment and your deployment environment are using **Node `18.14.1` or higher**.
<Steps>
1. Check your local version of Node using:
```sh
node -v
```
2. Check your [deployment environment's](/en/guides/deploy/) own documentation to verify that they support Node 18.
You can specify Node `18.14.1` for your Astro project either in a dashboard configuration setting or a `.nvmrc` file.
```bash title=".nvmrc"
18.14.1
```
</Steps>
### Removed: Support for TypeScript 4
In Astro v2.x, the `tsconfig.json` presets include support for both TypeScript 4.x and 5.x.
Astro v3.0 updates the `tsconfig.json` presets to only support TypeScript 5.x. Astro now assumes that you use TypeScript 5.0 (March 2023), or that your editor includes it (e.g. VS Code 1.77).
#### What should I do?
If you have installed TypeScript locally, update to at least v5.0.
```bash
npm install typescript@latest --save-dev
```
### Removed: `@astrojs/image`
In Astro v2.x, Astro offered an official image integration that included Astro `<Image />` and `<Picture />` components.
Astro v3.0 removes this integration from the codebase entirely. Astro's new solution for images is a built-in image services API: `astro:assets`.
#### What should I do?
Remove the `@astrojs/image` integration from your project. You will need to not only uninstall the integration but also update or remove any import statements and existing `<Image />` and `<Picture />` components. You might also need to configure a preferred default image processing service.
You will find [complete, step-by-step instructions for removing the old image integration](#remove-astrojsimage) in our Images guide.
Migrating to `astro:assets` will also bring some new image options and features that you may now wish to use. Please see the full [v3.0 Image Upgrade Advice](#upgrade-images-to-v3) for full details!
```js del={3,7}
// astro.config.mjs
import { defineConfig } from 'astro/config';
import image from '@astrojs/image';
export default defineConfig({
integrations: [
image(),
]
})
```
### Removed: `<Markdown />` component
In Astro v1.x, Astro deprecated the `<Markdown />` component and moved it to an external package.
Astro v3.0 completely removes the package `@astrojs/markdown-component`. Astro's `<Markdown />` component will no longer work in your project.
#### What should I do?
Remove all instances of the `@astrojs/markdown-component`.
```astro del={2} title="src/components/MyAstroComponent.astro"
---
import Markdown from '@astrojs/markdown-component';
---
```
To continue using a similar `<Markdown />` component in your code, consider using [community integrations](https://astro.build/integrations/) such as [`astro-remote`](https://github.com/natemoo-re/astro-remote). Be sure to update your `<Markdown />` component imports and attributes as necessary, according to the integration's own documentation.
Otherwise, delete all references to importing Astro's `<Markdown />` component and the component itself in your `.astro` files. You will need to rewrite your content as HTML directly or [import Markdown](/en/guides/markdown-content/#importing-markdown) from a `.md` file.
### Removed: deprecated 1.x APIs
In Astro v1.x, Astro deprecated our original configuration settings as well as `<style global>` and `<script hoist>` support. However, these were still supported for backwards compatibility.
Astro v3.0 removes these deprecated APIs entirely. The officially supported [configuration settings](/en/reference/configuration-reference/) and modern `<style is:global>` and `<script>` syntax should be used instead.
#### What should I do?
If you are continuing to use v1.x APIs, use the new APIs for each feature instead:
- Deprecated config options: See [the 0.26 migration guide](/en/guides/upgrade-to/v1/#new-configuration-api)
- Deprecated script/style attribute types: See [the 0.26 migration guide](/en/guides/upgrade-to/v1/#new-default-script-behavior)
### Removed: Partial shims for Web APIs in server code
In Astro v2.x, Astro provided partial shims for Web APIs such as `document` or `localStorage` in server-rendered code. These shims were often incomplete and unreliable.
Astro v3.0 removes these partial shims entirely. Web APIs are no longer available in server-rendered code.
#### What should I do?
If you are using Web APIs in server-rendered components, you will need to either make the usage of those APIs conditional or use [the `client:only` client directive](/en/reference/directives-reference/#clientonly).
### Removed: `image` from `astro:content` in content collections schema
In Astro v2.x, the content collections API deprecated an `image` export from `astro:content` for use in your content collections schemas.
Astro v3.0 removes this export entirely.
#### What should I do?
If you are using the deprecated `image()` from `astro:content`, remove it as this no longer exists. Validate images through [the `image` helper from `schema`](#update-content-collections-schemas) instead:
```ts title="src/content/config.ts" del={1} ins={2} "({ image })"
import { defineCollection, z, image } from "astro:content";
import { defineCollection, z } from "astro:content";
defineCollection({
schema: ({ image }) =>
z.object({
image: image(),
}),
});
```
### Removed: pre-0.14 Shiki theme names
In Astro v2.x, some Shiki theme names had been renamed, but the original names were kept for backwards compatibility.
Astro v3.0 removes the original names in favor of the renamed theme names.
#### What should I do?
If your project uses any of the themes below, rename them to their updated name:
- `material-darker` -> `material-theme-darker`
- `material-default` -> `material-theme`
- `material-lighter` -> `material-theme-lighter`
- `material-ocean` -> `material-theme-ocean`
- `material-palenight` -> `material-theme-palenight`
### Removed: `class:list` features
In Astro v2.x, the [`class:list` directive](/en/reference/directives-reference/#classlist) used a custom implementation inspired by [`clsx`](https://github.com/lukeed/clsx) with a few extra features like deduplication and `Set` support.
Astro v3.0 now uses `clsx` directly for `class:list`, which does not support deduplication or `Set` values.
#### What should I do?
Replace any `Set` elements passed to the `class:list` directive with a plain `Array`.
```astro title="src/components/MyAstroComponent.astro" del={4} ins={5}
<Component class:list={[
'a',
'b',
new Set(['c', 'd'])
['c', 'd']
]} />
```
### Removed: passing `class:list` as a prop
In Astro v2.x, [`class:list` values](/en/reference/directives-reference/#classlist) were sent to components via [`Astro.props['class:list']`](/en/reference/api-reference/#props).
Astro v3.0 normalizes `class:list` values into a string before being sent to components via `Astro.props['class']`
#### What should I do?
Remove any code that expects to receive the `class:list` prop.
```astro title="src/components/MyAstroComponent.astro" del={2,3,7} ins={4,8} "classList" "'class:list': classList"
---
import { clsx } from 'clsx';
const { class: className, 'class:list': classList } = Astro.props;
const { class: className } = Astro.props;
---
<div
class:list={[className, classList]}
class:list={[className]}
/>
```
### Removed: kebab-case transform for camelCase CSS variables
In Astro v2.x, camelCase [CSS variables](/en/guides/styling/#css-variables) passed to the `style` attribute were rendered as both camelCase (as written) and kebab-case (kept for backwards compatibility).
Astro v3.0 removes the kebab-case transform for these camelCase CSS variable names, and only the original camelCase CSS variable is rendered.
```astro "my-value"
---
// src/components/MyAstroComponent.astro
const myValue = "red"
---
<!-- input -->
<div style={{ "--myValue": myValue }}></div>
<!-- output (Astro 2.x) -->
<div style="--my-value:var(--myValue);--myValue:red"></div>
<!-- output (Astro 3.0) -->
<div style="--myValue:red"></div>
```
#### What should I do?
If you were relying on Astro to transform kebab-case in your styles, update your existing styles to camelCase to prevent missing styles. For example:
```astro del={3} ins={4} title="src/components/MyAstroComponent.astro"
<style>
div {
color: var(--my-value);
color: var(--myValue);
}
</style>
```
### Removed: automatic flattening of `getStaticPaths()`'s return value
In Astro v2.x, the return value of [`getStaticPaths()`](/en/reference/routing-reference/#getstaticpaths) was automatically flattened to allow you to return an array of arrays without errors.
Astro v3.0 removes automatic flattening of `getStaticPaths()`'s result.
#### What should I do?
If you're returning an array of arrays instead of an array of _objects_ (as is expected), `.flatMap` and `.flat` should now be used to ensure that you are returning a flat array.
An [error message indicating that `getStaticPath()`'s return value must be an array of objects](/en/reference/errors/invalid-get-static-paths-entry/#what-went-wrong) will be provided if you need to update your code.
### Moved: `astro check` now requires an external package
In Astro v2.x, [`astro check`](/en/reference/cli-reference/#astro-check) was included in Astro by default, and its dependencies were bundled in Astro. This meant a larger package whether or not you ever used `astro check`. This also prevented you from having control over the version of TypeScript and the Astro Language Server to use.
Astro v3.0 moves the `astro check` command out of Astro core and now requires an external package `@astrojs/check`. Additionally, you must install `typescript` in your project to use the `astro check` command.
#### What should I do?
Run the `astro check` command after upgrading to Astro v3.0 and follow the prompts to install the required dependencies, or manually install `@astrojs/check` and `typescript` into your project.
### Deprecated: `build.excludeMiddleware` and `build.split`
In Astro v2.x, `build.excludeMiddleware` and `build.split` were used to change how specific files were emitted when using an adapter in SSR mode.
Astro v3.0 replaces these build config options with new [SSR adapter configuration options](/en/guides/integrations-guide/#official-integrations) to perform the same tasks: `edgeMiddleware` and `functionPerRoute`.
#### What should I do?
Update the Astro config file to now use the new options **in the adapter configuration** directly.
```js title="astro.config.mjs" del={5-7} ins={9}
import { defineConfig } from "astro/config";
import vercel from "@astrojs/vercel/serverless";
export default defineConfig({
build: {
excludeMiddleware: true
},
adapter: vercel({
edgeMiddleware: true
}),
});
```
```js title="astro.config.mjs" del={5-7} ins={9}
import { defineConfig } from "astro/config";
import netlify from "@astrojs/netlify/functions";
export default defineConfig({
build: {
split: true
},
adapter: netlify({
functionPerRoute: true
}),
});
```
### Deprecated: `markdown.drafts`
In Astro v2.x, the `markdown.drafts` configuration allowed you to have draft pages that were available in when running the dev server, but not built in production.
Astro v3.0 deprecates this feature in favor of the content collections method of handling draft pages by filtering manually instead, which gives more control over the feature.
#### What should I do?
To continue to mark some pages in your project as drafts, [migrate to content collections](/en/guides/content-collections/) and manually filter out pages with the `draft: true` frontmatter property instead.
### Deprecated: returning simple object in endpoints
In Astro v2.x, endpoints could return a simple object, which would be converted to a JSON response.
Astro v3.0 deprecates this behavior in favor of returning a `Response` object directly.
#### What should I do?
Update your endpoints to return a `Response` object directly.
```ts title="endpoint.json.ts" del={2} ins={3}
export async function GET() {
return { body: { "title": "Bob's blog" }};
return new Response(JSON.stringify({ "title": "Bob's blog" }));
}
```
If you really need to keep the previous format, you can use the `ResponseWithEncoding` object but will be deprecated in the future.
```ts title="endpoint.json.ts" del={2} ins={3}
export async function GET() {
return { body: { "title": "Bob's blog" } };
return new ResponseWithEncoding({ body: { "title": "Bob's blog" }});
}
```
### Changed default: `verbatimModuleSyntax` in tsconfig.json presets
In Astro v2.x, the [`verbatimModuleSyntax`](https://www.typescriptlang.org/tsconfig#verbatimModuleSyntax) setting was off by default, with its TypeScript 4.x equivalent `importsNotUsedAsValues` being enabled in the `strict` preset.
In Astro v3.0, `verbatimModuleSyntax` is enabled in every preset.
#### What should I do?
This option requires that types are imported using the `import type` syntax.
```astro title="src/components/MyAstroComponent.astro" "type"
---
import { type CollectionEntry, getEntry } from "astro:content";
---
```
While we recommend keeping it on and properly making your type imports with `type` (as shown above), you can disable it by setting `verbatimModuleSyntax: false` in your `tsconfig.json` file if it causes any issues.
```json title="tsconfig.json" "false"
{
"compilerOptions": {
"verbatimModuleSyntax": false
}
}
```
### Changed default: port `3000`
In Astro v2.x, Astro ran on port `3000` by default.
Astro v3.0 changes the [default port](/en/reference/cli-reference/#--port-number) to `4321`. 🚀
#### What should I do?
Update any existing references to `localhost:3000`, for example in tests or in your `README`, to reflect the new port `localhost:4321`.
### Changed default: import.meta.env.BASE_URL `trailingSlash`
In Astro v2.x, `import.meta.env.BASE_URL` appended your [`base`](/en/reference/configuration-reference/#base) setting with a [trailingSlash](/en/reference/configuration-reference/#trailingslash) by default. `trailingSlash: "ignore"` also appended a trailing slash.
Astro v3.0 no longer appends `import.meta.env.BASE_URL` with a trailing slash by default, nor when `trailingSlash: "ignore"` is set. (The existing behavior of `base` in combination with `trailingSlash: "always"` or `trailingSlash: "never"` is unchanged.)
#### What should I do?
If your `base` already has a trailing slash, no change is needed.
If your `base` does not have a trailing slash, add one if you wish to preserve the previous default (or `trailingSlash: "ignore"`) behavior:
```js title="astro.config.mjs" del={4} ins={5}
import { defineConfig } from "astro/config";
export default defineConfig({
base: 'my-base',
base: 'my-base/',
});
```
### Changed default: `compressHTML`
In Astro v2.x, Astro only compressed your emitted HTML when [`compressHTML`](/en/reference/configuration-reference/#compresshtml) was explicitly set to `true`. The default value was `false`.
Astro v3.0 now compresses emitted HTML by default.
#### What should I do?
You can now remove `compressHTML: true` from your configuration as this is the new default behavior.
```js title="astro.config.mjs" del={4}
import { defineConfig } from "astro/config";
export default defineConfig({
compressHTML: true
})
```
You must now set `compressHTML: false` to opt out of HTML compression.
### Changed default: `scopedStyleStrategy`
In Astro v2.x, the default value of [`scopedStyleStrategy`](/en/reference/configuration-reference/#scopedstylestrategy) was `"where"`.
Astro v3.0 introduces a new, default value: `"attribute"`. By default, styles are now applied using `data-*` attributes.
#### What should I do?
To retain your project's current [style scoping](/en/guides/styling/#scoped-styles), update the configuration file to the previous default value:
```js title="astro.config.mjs" ins={4}
import { defineConfig } from "astro/config";
export default defineConfig({
scopedStyleStrategy: "where"
})
```
### Changed default: `inlineStyleSheets`
In Astro v2.x, all project stylesheets were sent as link tags by default. You could opt in to inlining them into `<style>` tags every time with `"always"`, or to inlining only stylesheets below a certain size with `"auto"` by setting the [`build.inlineStylesheets`](/en/reference/configuration-reference/#buildinlinestylesheets) configuration. The default setting was `"never"`.
Astro v3.0 changes the default value of `inlineStylesheets` to `"auto"`. Stylesheets smaller than `ViteConfig.build.assetsInlineLimit` (default: 4kb) are inlined by default. Otherwise, project styles are sent in external stylesheets.
#### What should I do?
If you want to keep your project's current behavior, set `build.inlineStylesheets` to the previous default, `"never"`:
```js title="astro.config.mjs" ins={4-6}
import { defineConfig } from "astro/config";
export default defineConfig({
build: {
inlineStylesheets: "never"
}
})
```
### Changed default: image service
In Astro v2.x, Squoosh was the [default image processing service](/en/guides/images/#default-image-service).
Astro v3.0 now includes Sharp as the default image processing service and instead provides a configuration option to use Squoosh.
#### What should I do?
:::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:
```bash
pnpm add sharp
```
:::
If you would prefer to continue to use Squoosh to transform your images, update your config with the following:
```ts title="astro.config.mjs" ins={4-6}
import { defineConfig, squooshImageService } from "astro/config";
export default defineConfig({
image: {
service: squooshImageService(),
}
})
```
### Changed: HTTP request methods case
In Astro v2.x, [HTTP request methods](/en/guides/endpoints/#http-methods) were written using lowercase function names: `get`, `post`, `put`, `all`, and `del`.
Astro v3.0 uses uppercase function names, including `DELETE` instead of `del`.
#### What should I do?
Rename all functions to their uppercase equivalent:
- `get` to `GET`
- `post` to `POST`
- `put` to `PUT`
- `all` to `ALL`
- `del` to `DELETE`
```diff lang="js" title="endpoint.ts"
-export function get() {
+export function GET() {
return new Response(JSON.stringify({ "title": "Bob's blog" }));
}
```
### Changed: Multiple JSX framework configuration
In Astro v2.x, you could use [multiple JSX framework integrations](/en/guides/integrations-guide/#official-integrations) (React, Solid, Preact) in the same project without needing to identify which files belonged to which framework.
Astro v3.0 now requires you to specify which framework to use for your files with new `include` and `exclude` integration config options when you have multiple JSX framework integrations installed. This allows Astro to better support single-framework usage, as well as advanced features like React Fast Refresh.
#### What should I do?
If you are using multiple JSX frameworks in the same project, set `include` (and optionally `exclude`) to an array of files and/or folders. Wildcards may be used to include multiple file paths.
We recommend placing common framework components in the same folder (e.g. `/components/react/` and `/components/solid/`) to make specifying your includes easier, but this is not required:
```js ins={13,16,19}
import { defineConfig } from 'astro/config';
import preact from '@astrojs/preact';
import react from '@astrojs/react';
import svelte from '@astrojs/svelte';
import vue from '@astrojs/vue';
import solid from '@astrojs/solid-js';
export default defineConfig({
// Enable many frameworks to support all different kinds of components.
// No `include` is needed if you are only using a single framework!
integrations: [
preact({
include: ['**/preact/*']
}),
react({
include: ['**/react/*']
}),
solid({
include: ['**/solid/*'],
}),
]
});
```
### Changed: `Astro.cookies.get(key)` can return `undefined`
In Astro v2.x, `Astro.cookies.get(key)` would always return an `AstroCookie` object, even if the cookie did not exist. To check for its existence, you needed to use `Astro.cookies.has(key)`.
Astro v3.0 returns `undefined` for `Astro.cookies.get(key)` if the cookie does not exist.
#### What should I do?
This change will not break any code that checks for the existence of the `Astro.cookie` object before using `Astro.cookies.get(key)`, but is now no longer required.
You can safely remove any code that uses `has()` to check if the value of `Astro.cookies` is `undefined`:
```js del={1-3} ins={5-7}
if (Astro.cookies.has(id)) {
const id = Astro.cookies.get(id)!;
}
const id = Astro.cookies.get(id);
if (id) {
}
```
### Changed: running the Astro CLI programmatically
In Astro v2.x, the `"astro"` package entrypoint exported and ran the Astro CLI directly. It is not recommended to run Astro this way in practice.
Astro v3.0 removes the CLI from the entrypoint, and exports a new set of experimental JavaScript APIs, including `dev()`, `build()`, `preview()`, and `sync()`.
#### What should I do?
To [run the Astro CLI programmatically](/en/reference/programmatic-reference/), use the new experimental JavaScript APIs:
```js
import { dev, build } from "astro";
// Start the Astro dev server
const devServer = await dev();
await devServer.stop();
// Build your Astro project
await build();
```
### Changed: internal Astro API entry point export paths
In Astro v2.x, you could import internal Astro APIs from `astro/internal/*` and `astro/runtime/server/*`.
Astro v3.0 removes the two entry points in favor of the existing `astro/runtime/*` entrypoint. Additionally, a new `astro/compiler-runtime` export has been added for compiler-specific runtime code.
#### What should I do?
These are entry points for Astro's internal API and should not affect your project. But if you do use these entrypoints, update as shown below:
```js del={1,4,10} ins={2,5,11}
import 'astro/internal/index.js';
import 'astro/runtime/server/index.js';
import 'astro/server/index.js';
import 'astro/runtime/server/index.js';
```
```js ins={5} del={4}
import { transform } from '@astrojs/compiler';
const result = await transform(source, {
internalURL: 'astro/runtime/server/index.js',
internalURL: 'astro/compiler-runtime',
// ...
});
```
## Feature Upgrades
### Upgrade images to v3
`astro:assets` is no longer behind an experimental flag in Astro v3.0.
`<Image />` is now a built-in component and the previous `@astrojs/image` integration has been removed.
These and other accompanying changes to using images in Astro may cause some breaking changes when you upgrade your Astro project from an earlier version.
Please follow the instructions below as appropriate to upgrade an Astro v2.x project to v3.0.
#### Upgrade from `experimental.assets`
If you had previously enabled the experimental flag for `astro:assets`, you will need to update your project for Astro v3.0 which now includes assets features by default.
##### Remove `experimental.assets` flag
Remove the experimental flag:
```js title="astro.config.mjs" del={4-6}
import { defineConfig } from 'astro/config';
export default defineConfig({
experimental: {
assets: true
}
});
```
If necessary, also update your `src/env.d.ts` file to replace the `astro/client-image` reference with `astro/client`:
```ts title="src/env.d.ts" del={1} ins={2}
/// <reference types="astro/client-image" />
/// <reference types="astro/client" />
```
##### Remove the `~/assets` import alias
This import alias is no longer included by default with `astro:assets`. If you were using this alias with experimental assets, you must convert them to relative file paths, or [create your own import aliases](/en/guides/imports/#aliases).
```astro title="src/pages/posts/post-1.astro" del={2} ins={3}
---
import rocket from '~/assets/rocket.png';
import rocket from '../../assets/rocket.png';
---
```
##### Add simple asset support for Cloudflare, Deno, Vercel Edge and Netlify Edge
Astro v3.0 allows `astro:assets` to work without errors in Cloudflare, Deno, Vercel Edge and Netlify Edge, which do not support Astro's built-in Squoosh and Sharp image optimization. Note that Astro does not perform any image transformation and processing in these environments. However, you can still enjoy the other benefits of using `astro:assets`, including no Cumulative Layout Shift (CLS), the enforced `alt` attribute, and a consistent authoring experience.
If you previously avoided using `astro:assets` because of these constraints, you can now use them without issues. You can configure the no-op image service to explicitly opt-in to this behavior:
```js title="astro.config.mjs" ins={4-8}
import { defineConfig } from 'astro/config';
export default defineConfig({
image: {
service: {
entrypoint: 'astro/assets/services/noop'
}
}
});
```
#### Decide where to store your images
See the Images guide to help you decide [where to store your images](/en/guides/images/#where-to-store-images). You may wish to take advantage of new options for storing your images with the added flexibility `astro:assets` brings. For example, relative images from your project `src/` can now be referenced in Markdown, MDX, and Markdoc using standard Markdown `![alt](src)` syntax.
#### Update existing `<img>` tags
Previously, importing an image would return a simple `string` with the path of the image. Now, imported image assets match the following signature:
```ts
interface ImageMetadata {
src: string;
width: number;
height: number;
format: string;
}
```
You must update the `src` attribute of any existing `<img>` tags (including any [images in UI framework components](/en/guides/images/#images-in-ui-framework-components)) and you may also update other attributes that are now available to you from the imported image.
```astro title="src/components/MyComponent.astro" ".src" ".width" ".height" del={4} ins={6}
---
import rocket from '../images/rocket.svg';
---
<img src={rocket} width="250" height="250" alt="A rocketship in space." />
<img src={rocket.src} width={rocket.width} height={rocket.height} alt="A rocketship in space." />
```
#### Update your Markdown, MDX, and Markdoc files
Relative images from your project `src/` can now be referenced in Markdown, MDX, and Markdoc using standard Markdown `![alt](src)` syntax.
This allows you to move your images from the `public/` directory to your project `src/` where they will now be processed and optimized. Your existing images in `public/` and remote images are still valid but are not optimized by Astro's build process.
```md title="src/pages/posts/post-1.md" "/_astro" ".hash" "../../assets/"
# My Markdown Page
<!-- Local images now possible! -->
![A starry night sky.](../../images/stars.png)
<!-- Keep your images next to your content! -->
![A starry night sky.](./stars.png)
```
If you require more control over your image attributes, we recommend using the `.mdx` file format, which allows you to include Astro's `<Image />` component or a JSX `<img />` tag in addition to the Markdown syntax. Use the [MDX integration](/en/guides/integrations-guide/mdx/) to add support for MDX to Astro.
#### Remove `@astrojs/image`
If you were using the image integration in Astro v2.x, complete the following steps:
<Steps>
1. Remove the `@astrojs/image` integration.
You must [remove the integration](/en/guides/integrations-guide/#removing-an-integration) by uninstalling and then removing it from your `astro.config.mjs` file.
```js del={3,7}
// astro.config.mjs
import { defineConfig } from 'astro/config';
import image from '@astrojs/image';
export default defineConfig({
integrations: [
image(),
]
})
```
2. Update types (if required).
If you had special types configured for `@astrojs/image` in `src/env.d.ts`, you may need to change them back to the default Astro types if your upgrade to v3 did not complete this step for you.
```ts title="src/env.d.ts" del={1} ins={2}
/// <reference types="@astrojs/image/client" />
/// <reference types="astro/client" />
```
Similarly, update `tsconfig.json` if necessary:
```json title="tsconfig.json" del={3} ins={4}
{
"compilerOptions": {
"types": ["@astrojs/image/client"]
"types": ["astro/client"]
}
}
```
3. Migrate any existing `<Image />` components.
Change all `import` statements from `@astrojs/image/components` to `astro:assets` in order to use the new built-in `<Image />` component.
Remove any component attributes that are not [currently supported image asset properties](/en/reference/modules/astro-assets/#image-properties).
For example, `aspectRatio` is no longer supported, as it is now automatically inferred from the `width` and `height` attributes.
```astro title="src/components/MyComponent.astro" del= {2,11} ins={3}
---
import { Image } from '@astrojs/image/components';
import { Image } from 'astro:assets';
import localImage from '../assets/logo.png';
const localAlt = 'The Astro Logo';
---
<Image
src={localImage}
width={300}
aspectRatio="16:9"
alt={localAlt}
/>
```
4. Choose a default image service.
[Sharp](https://github.com/lovell/sharp) is now the default image service used for `astro:assets`. If you would like to use Sharp, no configuration is required.
If you would prefer to use [Squoosh](https://github.com/GoogleChromeLabs/squoosh) to transform your images, update your config with the following `image.service` option:
```js title="astro.config.mjs" ins={4-6}
import { defineConfig, squooshImageService } from 'astro/config';
export default defineConfig({
image: {
service: squooshImageService(),
},
});
```
</Steps>
#### Update Content Collections schemas
You can now 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.
The new `image` helper for content collections lets you validate the image metadata using Zod. Learn more about [how to use images in content collections](/en/guides/images/#images-in-content-collections)
#### Navigating Image Imports in Astro v3.0
In Astro v3.0, if you have to preserve the old import behavior for images and require a string representation of the image's URL, append `?url` to the end of your image path when importing it. For example:
```astro title="src/pages/blog/MyImages.astro"
---
import Sprite from '../assets/logo.svg?url';
---
<svg>
<use xlink:href={Sprite + '#cart'} />
</svg>
```
This approach ensures you obtain the URL string. Keep in mind that during development, Astro uses a `src/` path, but upon building, it generates hashed paths like `/_astro/cat.a6737dd3.png`.
If you prefer to work directly with the image object itself, you can access the `.src` property. This approach is best for tasks like managing image dimensions for Core Web Vitals metrics and preventing CLS.
If you are transitioning into the new import behavior, combining `?url` and `.src` methods might be the right method for seamless image handling.
### Upgrade view transitions to v3
View transitions are no longer behind an experimental flag in Astro v3.0.
If you had **not** enabled this experimental flag in Astro 2.x, this will not cause any breaking changes to your project. The new View Transitions API has no effect on your existing code.
If you were previously using experimental view transitions, there may be some breaking changes when you upgrade your Astro project from an earlier version.
Please follow the instructions below as appropriate to upgrade **an Astro v2.x project configured with `experimental.viewTransitions: true`** to v3.0.
#### Upgrade from `experimental.viewTransitions`
If you had previously enabled the experimental flag for view transitions, you will need to update your project for Astro v3.0 which now allows view transitions by default.
##### Remove `experimental.viewTransitions` flag
Remove the experimental flag:
```js title="astro.config.mjs" del={4-6}
import { defineConfig } from 'astro/config';
export default defineConfig({
experimental: {
viewTransitions: true
}
});
```
##### Update import source
The `<ViewTransitions />` component has been moved from `astro:components` to `astro:transitions`. Update the import source across all occurrences in your project.
```astro title="src/layouts/BaseLayout.astro" del="astro:components" ins="astro:transitions"
---
import { ViewTransitions } from "astro:components astro:transitions"
---
<html lang="en">
<head>
<title>My Homepage</title>
<ViewTransitions />
</head>
<body>
<h1>Welcome to my website!</h1>
</body>
</html>
```
#### Update `transition:animate` directives
**Changed:** The `transition:animate` value `morph` has been renamed to `initial`. Also, this is no longer the default animation. If no `transition:animate` directive is specified, your animations will now default to `fade`.
1. Rename any `morph` animations to `initial`.
```astro title="src/components/MyComponent.astro" del="morph" ins="initial"
<div transition:name="name" transition:animate="morph initial" />
```
2. To keep any animations that were previously using `morph` by default, explicitly add `transition:animate="initial"`
```astro title="src/components/MyComponent.astro" ins='transition:animate="initial"'
<div transition:name="name" transition:animate="initial" />
```
3. You can safely remove any animations explicitly set to `fade`. This is now the default behavior:
```astro title="src/components/MyComponent.astro" del="transition:animate=\"fade\""
<div transition:name="name" transition:animate="fade" />
```
**Added:** Astro also supports a new `transition:animate` value, `none`. This value can be used on a page's `<html>` element to disable animated full-page transitions on an entire page. This will only override **default animation behavior** on page elements without an animation directive. You can still set animations on individual elements, and these specific animations will occur.
4. You may now disable all default transitions on an individual page, animating only elements that explicitly use a `transition:animate` directive:
```astro ins="transition:animate=\"none\""
<html transition:animate="none">
<head></head>
<body>
<h1>Hello world!</h1>
</body>
</html>
```
##### Update event names
The event `astro:load` has been renamed to `astro:page-load`. Rename all occurrences in your project.
```astro title="src/components/MyComponent.astro" del="astro:lo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment