W↓
All docs
🔑
Sign Up/Sign In
docs.astro.build/en/basics/astro-pages/ (+10)
Public Link
Apr 16, 2025, 3:05:28 PM - complete - 137.2 kB
Apr 16, 2025, 3:05:28 PM - complete - 137.2 kB
Apr 16, 2025, 3:03:49 PM - complete - 58.8 kB
Starting URLs:
https://docs.astro.build/en/basics/astro-pages/
https://docs.astro.build/en/guides/routing/
https://docs.astro.build/en/guides/endpoints/
https://docs.astro.build/en/guides/middleware/
https://docs.astro.build/en/guides/prefetch/
https://docs.astro.build/en/basics/astro-components/
https://docs.astro.build/en/basics/layouts/
https://docs.astro.build/en/guides/styling/
https://docs.astro.build/en/guides/fonts/
https://docs.astro.build/en/guides/client-side-scripts/
https://docs.astro.build/en/guides/integrations-guide/react/
CSS Selector:
.sl-markdown-content
## Page: https://docs.astro.build/en/basics/astro-pages/ **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 Section titled Supported page files Astro supports the following file types in the `src/pages/` directory: * `.astro` * `.md` * `.mdx` (with the MDX Integration installed) * `.html` * `.js`/`.ts` (as endpoints) ## File-based routing Section titled 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. This allows you to create pages even if your content lives outside of the special `/pages/` directory, such as in a content collection or a CMS. Read more about Routing in Astro. ### Link between pages Section titled Link between pages Write standard HTML `<a>` elements 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`: src/pages/index.astro Read more <a href="/authors/sonali/">about Sonali</a>. ## Astro Pages Section titled Astro Pages Astro pages use the `.astro` file extension and support the same features as Astro components. 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. To avoid repeating the same HTML elements on every page, you can move common `<head>` and `<body>` elements into your own layout components. You can use as many or as few layout components as you’d like. src/pages/index.astro ---import MySiteLayout from "../layouts/MySiteLayout.astro";---<MySiteLayout> <p>My page content, wrapped in a layout!</p></MySiteLayout> Read more about layout components in Astro. ## Markdown/MDX Pages Section titled 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, it also treats MDX (`.mdx`) files the same way. Tip Consider creating 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 that will wrap their Markdown content in a full `<html>...</html>` page document. src/pages/page.md ---layout: ../layouts/MySiteLayout.astrotitle: My Markdown page---# Title This is my page, written in **Markdown.** Read more about Markdown in Astro. ## HTML Pages Section titled 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. ## Custom 404 Error Page Section titled 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 will find and use it. ## Custom 500 Error Page Section titled Custom 500 Error Page For a custom 500 error page to show for pages that are rendered on demand, 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. **Added in:** `astro@4.10.3` 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` Section titled error **Added in:** `astro@4.11.0` `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: 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 Section titled Page Partials **Added in:** `astro@3.4.0` Caution Page partials are intended to be used in conjunction with a front-end library, such as htmx or Unpoly. 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, Stimulus, jQuery) 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 and `<script>` tags for building dynamic content in Astro. Page files that can export a value for `partial` (e.g. `.astro` and `.mdx`, but not `.md`) can be marked as partials. src/pages/partial.astro ---export const partial = true;---<li>I'm a partial!</li> ### Using with a library Section titled Using with a library Partials are used to dynamically update a section of a page using a library such as htmx. 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. src/pages/index.astro <html> <head> <title>My page</title> <script src="https://unpkg.com/htmx.org@1.9.6" 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: src/pages/partials/clicked.astro ---export const partial = true;---<div>I was clicked!</div> See the htmx documentation for more details on using htmx. Learn --- ## Page: https://docs.astro.build/en/guides/routing/ Astro uses **file-based routing** to generate your build URLs based on the file layout of your project `src/pages/` directory. ## Navigating between pages Section titled Navigating between pages Astro uses standard HTML `<a>` elements to navigate between routes. There is no framework-specific `<Link>` component provided. src/pages/index.astro <p>Read more <a href="/about/">about</a> Astro!</p> <!-- With `base: "/docs"` configured --><p>Learn more in our <a href="/docs/reference/">reference</a> section!</p> ## Static routes Section titled Static routes `.astro` page components as well as Markdown and MDX Files (`.md`, `.mdx`) within the `src/pages/` directory **automatically become pages on your website**. Each page’s route corresponds to its path and filename within the `src/pages/` directory. # Example: Static routessrc/pages/index.astro -> mysite.com/src/pages/about.astro -> mysite.com/aboutsrc/pages/about/index.astro -> mysite.com/aboutsrc/pages/about/me.astro -> mysite.com/about/mesrc/pages/posts/1.md -> mysite.com/posts/1 Tip There is no separate “routing config” to maintain in an Astro project! When you add a file to the `src/pages/` directory, a new route is automatically created for you. In static builds, you can customize the file output format using the `build.format` configuration option. ## Dynamic routes Section titled Dynamic routes An Astro page file can specify dynamic route parameters in its filename to generate multiple, matching pages. For example, `src/pages/authors/[author].astro` generates a bio page for every author on your blog. `author` becomes a _parameter_ that you can access from inside the page. In Astro’s default static output mode, these pages are generated at build time, and so you must predetermine the list of `author`s that get a corresponding file. In SSR mode, a page will be generated on request for any route that matches. ### Static (SSG) Mode Section titled Static (SSG) Mode Because all routes must be determined at build time, a dynamic route must export a `getStaticPaths()` that returns an array of objects with a `params` property. Each of these objects will generate a corresponding route. `[dog].astro` defines the dynamic `dog` parameter in its filename, so the objects returned by `getStaticPaths()` must include `dog` in their `params`. The page can then access this parameter using `Astro.params`. src/pages/dogs/\[dog\].astro ---export function getStaticPaths() { return [ { params: { dog: "clifford" }}, { params: { dog: "rover" }}, { params: { dog: "spot" }}, ];} const { dog } = Astro.params;---<div>Good dog, {dog}!</div> This will generate three pages: `/dogs/clifford`, `/dogs/rover`, and `/dogs/spot`, each displaying the corresponding dog name. The filename can include multiple parameters, which must all be included in the `params` objects in `getStaticPaths()`: src/pages/\[lang\]-\[version\]/info.astro ---export function getStaticPaths() { return [ { params: { lang: "en", version: "v1" }}, { params: { lang: "fr", version: "v2" }}, ];} const { lang, version } = Astro.params;--- This will generate `/en-v1/info` and `/fr-v2/info`. Parameters can be included in separate parts of the path. For example, the file `src/pages/[lang]/[version]/info.astro` with the same `getStaticPaths()` above will generate the routes `/en/v1/info` and `/fr/v2/info`. #### Decoding `params` Section titled Decoding params The `params` provided to the function `getStaticPaths()` function are not decoded. Use the function `decodeURI` when you need to decode parameter values. src/pages/\[slug\].astro ---export function getStaticPaths() { return [ { params: { slug: decodeURI("%5Bpage%5D") }}, // decodes to "[page]" ]}--- Learn more about `getStaticPaths()`.  **Related recipe:** Add i18n features #### Rest parameters Section titled Rest parameters If you need more flexibility in your URL routing, you can use a rest parameter (`[...path]`) in your `.astro` filename to match file paths of any depth: src/pages/sequences/\[...path\].astro ---export function getStaticPaths() { return [ { params: { path: "one/two/three" }}, { params: { path: "four" }}, { params: { path: undefined }} ]} const { path } = Astro.params;--- This will generate `/sequences/one/two/three`, `/sequences/four`, and `/sequences`. (Setting the rest parameter to `undefined` allows it to match the top level page.) Rest parameters can be used with **other named parameters**. For example, GitHub’s file viewer can be represented with the following dynamic route: /[org]/[repo]/tree/[branch]/[...file] In this example, a request for `/withastro/astro/tree/main/docs/public/favicon.svg` would be split into the following named parameters: { org: "withastro", repo: "astro", branch: "main", file: "docs/public/favicon.svg"} #### Example: Dynamic pages at multiple levels Section titled Example: Dynamic pages at multiple levels In the following example, a rest parameter (`[...slug]`) and the `props` feature of `getStaticPaths()` generate pages for slugs of different depths. src/pages/\[...slug\].astro ---export function getStaticPaths() { const pages = [ { slug: undefined, title: "Astro Store", text: "Welcome to the Astro store!", }, { slug: "products", title: "Astro products", text: "We have lots of products for you", }, { slug: "products/astro-handbook", title: "The ultimate Astro handbook", text: "If you want to learn Astro, you must read this book.", }, ]; return pages.map(({ slug, title, text }) => { return { params: { slug }, props: { title, text }, }; });} const { title, text } = Astro.props;---<html> <head> <title>{title}</title> </head> <body> <h1>{title}</h1> <p>{text}</p> </body></html> ### Server (SSR) Mode Section titled Server (SSR) Mode In SSR mode, dynamic routes are defined the same way: include `[param]` or `[...path]` brackets in your file names to match arbitrary strings or paths. But because the routes are no longer built ahead of time, the page will be served to any matching route. Since these are not “static” routes, `getStaticPaths` should not be used. For on-demand rendered routes, only one rest parameter using the spread notation may be used in the file name (e.g. `src/pages/[locale]/[...slug].astro` or `src/pages/[...locale]/[slug].astro`, but not `src/pages/[...locale]/[...slug].astro`). src/pages/resources/\[resource\]/\[id\].astro ---const { resource, id } = Astro.params;---<h1>{resource}: {id}</h1> This page will be served for any value of `resource` and `id`: `resources/users/1`, `resources/colors/blue`, etc. #### Modifying the `[...slug]` example for SSR Section titled Modifying the \[...slug\] example for SSR Because SSR pages can’t use `getStaticPaths()`, they can’t receive props. The previous example can be adapted for SSR mode by looking up the value of the `slug` param in an object. If the route is at the root (”/”), the `slug` param will be `undefined`. If the value doesn’t exist in the object, we redirect to a 404 page. src/pages/\[...slug\].astro ---const pages = [ { slug: undefined, title: 'Astro Store', text: 'Welcome to the Astro store!', }, { slug: 'products', title: 'Astro products', text: 'We have lots of products for you', }, { slug: 'products/astro-handbook', title: 'The ultimate Astro handbook', text: 'If you want to learn Astro, you must read this book.', }]; const { slug } = Astro.params;const page = pages.find((page) => page.slug === slug);if (!page) return Astro.redirect("/404");const { title, text } = page;---<html> <head> <title>{title}</title> </head> <body> <h1>{title}</h1> <p>{text}</p> </body></html> ## Redirects Section titled Redirects Sometimes you will need to redirect your readers to a new page, either permanently because your site structure has changed or in response to an action such as logging in to an authenticated route. You can define rules to redirect users to permanently-moved pages in your Astro config. Or, redirect users dynamically as they use your site. ### Configured Redirects Section titled Configured Redirects **Added in:** `astro@2.9.0` You can specify a mapping of permanent redirects in your Astro config with the `redirects` value. For internal redirects, this is a mapping of an old route path to the new route. As of Astro v5.2.0, it is also possible to redirect to external URLs that start with `http` or `https` and can be parsed: astro.config.mjs import { defineConfig } from "astro/config"; export default defineConfig({ redirects: { "/old-page": "/new-page", "/blog": "https://example.com/blog" }}); These redirects follow the same priority rules as file-based routes and will always take lower precedence than an existing page file of the same name in your project. For example, `/old-page` will not redirect to `/new-page` if your project contains the file `src/pages/old-page.astro`. Dynamic routes are allowed as long as both the new and old routes contain the same parameters, for example: { "/blog/[...slug]": "/articles/[...slug]"} Using SSR or a static adapter, you can also provide an object as the value, allowing you to specify the `status` code in addition to the new `destination`: astro.config.mjs import { defineConfig } from "astro/config"; export default defineConfig({ redirects: { "/old-page": { status: 302, destination: "/new-page" }, "/news": { status: 302, destination: "https://example.com/news" } }}); When running `astro build`, Astro will output HTML files with the meta refresh tag by default. Supported adapters will instead write out the host’s configuration file with the redirects. The status code is `301` by default. If building to HTML files the status code is not used by the server. ### Dynamic redirects Section titled Dynamic redirects On the `Astro` global, the `Astro.redirect` method allows you to redirect to another page dynamically. You might do this after checking if the user is logged in by getting their session from a cookie. src/pages/account.astro ---import { isLoggedIn } from "../utils"; const cookie = Astro.request.headers.get("cookie"); // If the user is not logged in, redirect them to the login pageif (!isLoggedIn(cookie)) { return Astro.redirect("/login");}--- ## Rewrites Section titled Rewrites **Added in:** `astro@4.13.0` A rewrite allows you to serve a different route without redirecting the browser to a different page. The browser will show the original address in the URL bar, but will instead display the content of the URL provided to `Astro.rewrite()`. Tip For content that has permanently moved, or to direct your user to a different page with a new URL (e.g. a user dashboard after logging in), use a redirect instead. Rewrites can be useful for showing the same content at multiple paths (e.g. `/products/shoes/men/` and `/products/men/shoes/`) without needing to maintain two different source files. Rewrites are also useful for SEO purposes and user experience. They allow you to display content that otherwise would require redirecting your visitor to a different page or would return a 404 status. One common use of rewrites is to show the same localized content for different variants of a language. The following example uses a rewrite to render the `/es/` version of a page when the `/es-CU/` (Cuban Spanish) URL path is visited. When a visitor navigates to the URL `/es-cu/articles/introduction`, Astro will render the content generated by the file `src/pages/es/articles/introduction.astro`. src/pages/es-cu/articles/introduction.astro ---return Astro.rewrite("/es/articles/introduction");--- Use `context.rewrite()` in your endpoint files to reroute to a different page: src/pages/api.js export function GET(context) { if (!context.locals.allowed) { return context.rewrite("/"); }} If the URL passed to `Astro.rewrite()` emits a runtime error, Astro will show the overlay error in development and return a 500 status code in production. If the URL does not exist in your project, a 404 status code will be returned. You can intentionally create a rewrite to render your `/404` page, for example to indicate that a product in your e-commerce shop is no longer available: src/pages/\[item\].astro ---const { item } = Astro.params; if (!itemExists(item)) { return Astro.rewrite("/404");}--- You can also conditionally rewrite based on an HTTP response status, for example to display a certain page on your site when visiting a URL that doesn’t exist: src/middleware.mjs export const onRequest = async (context, next) => { const response = await next(); if (response.status === 404) { return context.rewrite("/"); } return response;} Before displaying the content from the specified rewrite path, the function `Astro.rewrite()` will trigger a new, complete rendering phase. This re-executes any middleware for the new route/request. See the `Astro.rewrite()` API reference for more information. ## Route Priority Order Section titled Route Priority Order It’s possible for multiple defined routes to attempt to build the same URL path. For example, all of these routes could build `/posts/create`: * Directorysrc/pages/ * \[…slug\].astro * Directoryposts/ * create.astro * \[page\].astro * \[pid\].ts * \[…slug\].astro Astro needs to know which route should be used to build the page. To do so, it sorts them according to the following rules in order: * Astro reserved routes * Routes with more path segments will take precedence over less specific routes. In the example above, all routes under `/posts/` take precedence over `/[...slug].astro` at the root. * Static routes without path parameters will take precedence over dynamic routes. E.g. `/posts/create.astro` takes precedence over all the other routes in the example. * Dynamic routes using named parameters take precedence over rest parameters. E.g. `/posts/[page].astro` takes precedence over `/posts/[...slug].astro`. * Pre-rendered dynamic routes take precedence over server dynamic routes. * Endpoints take precedence over pages. * File-based routes take precedence over redirects. * If none of the rules above decide the order, routes are sorted alphabetically based on the default locale of your Node installation. Given the example above, here are a few examples of how the rules will match a requested URL to the route used to build the HTML: * `pages/posts/create.astro` - Will build only `/posts/create` * `pages/posts/[pid].ts` - Will build `/posts/abc`, `/posts/xyz`, etc. But not `/posts/create` * `pages/posts/[page].astro` - Will build `/posts/1`, `/posts/2`, etc. But not `/posts/create`, `/posts/abc` nor `/posts/xyz` * `pages/posts/[...slug].astro` - Will build `/posts/1/2`, `/posts/a/b/c`, etc. But not `/posts/create`, `/posts/1`, `/posts/abc`, etc. * `pages/[...slug].astro` - Will build `/abc`, `/xyz`, `/abc/xyz`, etc. But not `/posts/create`, `/posts/1`, `/posts/abc`, , etc. ### Reserved routes Section titled Reserved routes Internal routes take priority over any user-defined or integration-defined routes as they are required for Astro features to work. The following are Astro’s reserved routes: * `_astro/`: Serves all of the static assets to the client, including CSS documents, bundled client scripts, optimized images, and any Vite assets. * `_server_islands/`: Serves the dynamic components deferred into a server island. * `_actions/`: Serves any defined actions. ## Pagination Section titled Pagination Astro supports built-in pagination for large collections of data that need to be split into multiple pages. Astro will generate common pagination properties, including previous/next page URLs, total number of pages, and more. Paginated route names should use the same `[bracket]` syntax as a standard dynamic route. For instance, the file name `/astronauts/[page].astro` will generate routes for `/astronauts/1`, `/astronauts/2`, etc, where `[page]` is the generated page number. You can use the `paginate()` function to generate these pages for an array of values like so: src/pages/astronauts/\[page\].astro ---export function getStaticPaths({ paginate }) { const astronautPages = [ { astronaut: "Neil Armstrong" }, { astronaut: "Buzz Aldrin" }, { astronaut: "Sally Ride" }, { astronaut: "John Glenn" }, ]; // Generate pages from our array of astronauts, with 2 to a page return paginate(astronautPages, { pageSize: 2 });}// All paginated data is passed on the "page" propconst { page } = Astro.props;---<!-- Display the current page number. `Astro.params.page` can also be used! --><h1>Page {page.currentPage}</h1><ul> <!-- List the array of astronaut info --> {page.data.map(({ astronaut }) => <li>{astronaut}</li>)}</ul> This generates the following pages, with 2 items to a page: * `/astronauts/1` - Page 1: Displays “Neil Armstrong” and “Buzz Aldrin” * `/astronauts/2` - Page 2: Displays “Sally Ride” and “John Glenn” ### The `page` prop Section titled The page prop When you use the `paginate()` function, each page will be passed its data via a `page` prop. The `page` prop has many useful properties that you can use to build pages and links between them: interface Page<T = any> { /** array containing the page’s slice of data that you passed to the paginate() function */ data: T[]; /** metadata */ /** the count of the first item on the page, starting from 0 */ start: number; /** the count of the last item on the page, starting from 0 */ end: number; /** total number of results */ total: number; /** the current page number, starting from 1 */ currentPage: number; /** number of items per page (default: 10) */ size: number; /** number of last page */ lastPage: number; url: { /** url of the current page */ current: string; /** url of the previous page (if there is one) */ prev: string | undefined; /** url of the next page (if there is one) */ next: string | undefined; /** url of the first page (if the current page is not the first page) */ first: string | undefined; /** url of the last page (if the current page in not the last page) */ last: string | undefined; };} The following example displays current information for the page along with links to navigate between pages: src/pages/astronauts/\[page\].astro ---// Paginate same list of `{ astronaut }` objects as the previous exampleexport function getStaticPaths({ paginate }) { /* ... */ }const { page } = Astro.props;---<h1>Page {page.currentPage}</h1><ul> {page.data.map(({ astronaut }) => <li>{astronaut}</li>)}</ul>{page.url.first ? <a href={page.url.first}>First</a> : null}{page.url.prev ? <a href={page.url.prev}>Previous</a> : null}{page.url.next ? <a href={page.url.next}>Next</a> : null}{page.url.last ? <a href={page.url.last}>Last</a> : null} Learn more about the pagination `page` prop. ### Nested Pagination Section titled Nested Pagination A more advanced use-case for pagination is **nested pagination.** This is when pagination is combined with other dynamic route params. You can use nested pagination to group your paginated collection by some property or tag. For example, if you want to group your paginated Markdown posts by some tag, you would use nested pagination by creating a `/src/pages/[tag]/[page].astro` page that would match the following URLS: * `/red/1` (tag=red) * `/red/2` (tag=red) * `/blue/1` (tag=blue) * `/green/1` (tag=green) Nested pagination works by returning an array of `paginate()` results from `getStaticPaths()`, one for each grouping. In the following example, we will implement nested pagination to build the URLs listed above: src/pages/\[tag\]/\[page\].astro ---export function getStaticPaths({ paginate }) { const allTags = ["red", "blue", "green"]; const allPosts = Object.values(import.meta.glob("../pages/post/*.md", { eager: true })); // For every tag, return a `paginate()` result. // Make sure that you pass `{ params: { tag }}` to `paginate()` // so that Astro knows which tag grouping the result is for. return allTags.flatMap((tag) => { const filteredPosts = allPosts.filter((post) => post.frontmatter.tag === tag); return paginate(filteredPosts, { params: { tag }, pageSize: 10 }); });} const { page } = Astro.props;const params = Astro.params; ## Excluding pages Section titled Excluding pages You can exclude pages or directories within `src/pages` from being built by prefixing their names with an underscore (`_`). Files with the `_` prefix won’t be recognized by the router and won’t be placed into the `dist/` directory. You can use this to temporarily disable pages, and also to put tests, utilities, and components in the same folder as their related pages. In this example, only `src/pages/index.astro` and `src/pages/projects/project1.md` will be built as page routes and HTML files. * Directorysrc/pages/ * Directory\_hidden-directory/ * page1.md * page2.md * \_hidden-page.astro * **index.astro** * Directoryprojects/ * \_SomeComponent.astro * \_utils.js * **project1.md** Learn --- ## Page: https://docs.astro.build/en/guides/endpoints/ Astro lets you create custom endpoints to serve any kind of data. You can use this to generate images, expose an RSS document, or use them as API Routes to build a full API for your site. In statically-generated sites, your custom endpoints are called at build time to produce static files. If you opt in to SSR mode, custom endpoints turn into live server endpoints that are called on request. Static and SSR endpoints are defined similarly, but SSR endpoints support additional features. ## Static File Endpoints Section titled Static File Endpoints To create a custom endpoint, add a `.js` or `.ts` file to the `/pages` directory. The `.js` or `.ts` extension will be removed during the build process, so the name of the file should include the extension of the data you want to create. For example, `src/pages/data.json.ts` will build a `/data.json` endpoint. Endpoints export a `GET` function (optionally `async`) that receives a context object with properties similar to the `Astro` global. Here, it returns a `Response` object with a `name` and `url`, and Astro will call this at build time and use the contents of the body to generate the file. src/pages/builtwith.json.ts // Outputs: /builtwith.jsonexport function GET({ params, request }) { return new Response( JSON.stringify({ name: "Astro", url: "https://astro.build/", }), );} Since Astro v3.0, the returned `Response` object doesn’t have to include the `encoding` property anymore. For example, to produce a binary `.png` image: src/pages/astro-logo.png.ts export async function GET({ params, request }) { const response = await fetch( "https://docs.astro.build/assets/full-logo-light.png", ); return new Response(await response.arrayBuffer());} You can also type your endpoint functions using the `APIRoute` type: import type { APIRoute } from "astro"; export const GET: APIRoute = async ({ params, request }) => {...} ### `params` and Dynamic routing Section titled params and Dynamic routing Endpoints support the same dynamic routing features that pages do. Name your file with a bracketed parameter name and export a `getStaticPaths()` function. Then, you can access the parameter using the `params` property passed to the endpoint function: src/pages/api/\[id\].json.ts import type { APIRoute } from "astro"; const usernames = ["Sarah", "Chris", "Yan", "Elian"]; export const GET: APIRoute = ({ params, request }) => { const id = params.id; return new Response( JSON.stringify({ name: usernames[id], }), );}; export function getStaticPaths() { return [ { params: { id: "0" } }, { params: { id: "1" } }, { params: { id: "2" } }, { params: { id: "3" } }, ];} This will generate four JSON endpoints at build time: `/api/0.json`, `/api/1.json`, `/api/2.json` and `/api/3.json`. Dynamic routing with endpoints works the same as it does with pages, but because the endpoint is a function and not a component, props aren’t supported. ### `request` Section titled request All endpoints receive a `request` property, but in static mode, you only have access to `request.url`. This returns the full URL of the current endpoint and works the same as Astro.request.url does for pages. src/pages/request-path.json.ts import type { APIRoute } from "astro"; export const GET: APIRoute = ({ params, request }) => { return new Response( JSON.stringify({ path: new URL(request.url).pathname, }), );}; ## Server Endpoints (API Routes) Section titled Server Endpoints (API Routes) Everything described in the static file endpoints section can also be used in SSR mode: files can export a `GET` function which receives a context object with properties similar to the `Astro` global. But, unlike in `static` mode, when you enable on-demand rendering for a route, the endpoint will be built when it is requested. This unlocks new features that are unavailable at build time, and allows you to build API routes that listen for requests and securely execute code on the server at runtime. Your routes will be rendered on demand by default in `server` mode. In `static` mode, you must opt out of prerendering for each custom endpoint with `export const prerender = false`.  **Related recipe:** Call endpoints from the server Note Be sure to enable an on-demand rendering mode before trying these examples, and opt out of prerendering in `static` mode. Server endpoints can access `params` without exporting `getStaticPaths`, and they can return a `Response` object, allowing you to set status codes and headers: src/pages/\[id\].json.js import { getProduct } from "../db"; export async function GET({ params }) { const id = params.id; const product = await getProduct(id); if (!product) { return new Response(null, { status: 404, statusText: "Not found", }); } return new Response(JSON.stringify(product), { status: 200, headers: { "Content-Type": "application/json", }, });} This will respond to any request that matches the dynamic route. For example, if we navigate to `/helmet.json`, `params.id` will be set to `helmet`. If `helmet` exists in the mock product database, the endpoint will use a `Response` object to respond with JSON and return a successful HTTP status code. If not, it will use a `Response` object to respond with a `404`. In SSR mode, certain providers require the `Content-Type` header to return an image. In this case, use a `Response` object to specify a `headers` property. For example, to produce a binary `.png` image: src/pages/astro-logo.png.ts export async function GET({ params, request }) { const response = await fetch( "https://docs.astro.build/assets/full-logo-light.png", ); const buffer = Buffer.from(await response.arrayBuffer()); return new Response(buffer, { headers: { "Content-Type": "image/png" }, });} ### HTTP methods Section titled HTTP methods In addition to the `GET` function, you can export a function with the name of any HTTP method. When a request comes in, Astro will check the method and call the corresponding function. You can also export an `ALL` function to match any method that doesn’t have a corresponding exported function. If there is a request with no matching method, it will redirect to your site’s 404 page. src/pages/methods.json.ts export const GET: APIRoute = ({ params, request }) => { return new Response( JSON.stringify({ message: "This was a GET!", }), );}; export const POST: APIRoute = ({ request }) => { return new Response( JSON.stringify({ message: "This was a POST!", }), );}; export const DELETE: APIRoute = ({ request }) => { return new Response( JSON.stringify({ message: "This was a DELETE!", }), );}; export const ALL: APIRoute = ({ request }) => { return new Response( JSON.stringify({ message: `This was a ${request.method}!`, }), );}; If you define a `GET` function but no `HEAD` function, Astro will automatically handle `HEAD` requests by calling the `GET` function and stripping the body from the response.  **Related recipes** * Verify a Captcha * Build forms with API routes ### `request` Section titled request In SSR mode, the `request` property returns a fully usable `Request` object that refers to the current request. This allows you to accept data and check headers: src/pages/test-post.json.ts export const POST: APIRoute = async ({ request }) => { if (request.headers.get("Content-Type") === "application/json") { const body = await request.json(); const name = body.name; return new Response( JSON.stringify({ message: "Your name was: " + name, }), { status: 200, }, ); } return new Response(null, { status: 400 });}; ### Redirects Section titled Redirects The endpoint context exports a `redirect()` utility similar to `Astro.redirect`: src/pages/links/\[id\].js import { getLinkUrl } from "../db"; export async function GET({ params, redirect }) { const { id } = params; const link = await getLinkUrl(id); if (!link) { return new Response(null, { status: 404, statusText: "Not found", }); } return redirect(link, 307);} Learn --- ## Page: https://docs.astro.build/en/guides/middleware/ **Middleware** allows you to intercept requests and responses and inject behaviors dynamically every time a page or endpoint is about to be rendered. This rendering occurs at build time for all prerendered pages, but occurs when the route is requested for pages rendered on demand, making additional SSR features like cookies and headers available. Middleware also allows you to set and share request-specific information across endpoints and pages by mutating a `locals` object that is available in all Astro components and API endpoints. This object is available even when this middleware runs at build time. ## Basic Usage Section titled Basic Usage 1. Create `src/middleware.js|ts` (Alternatively, you can create `src/middleware/index.js|ts`.) 2. Inside this file, export an `onRequest()` function that can be passed a `context` object and `next()` function. This must not be a default export. src/middleware.js export function onRequest (context, next) { // intercept data from a request // optionally, modify the properties in `locals` context.locals.title = "New title"; // return a Response or the result of calling `next()` return next();}; 3. Inside any `.astro` file, access response data using `Astro.locals`. src/components/Component.astro ---const data = Astro.locals;---<h1>{data.title}</h1><p>This {data.property} is from middleware.</p> ### The `context` object Section titled The context object The `context` object includes information to be made available to other middleware, API routes and `.astro` routes during the rendering process. This is an optional argument passed to `onRequest()` that may contain the `locals` object as well as any additional properties to be shared during rendering. For example, the `context` object may include cookies used in authentication. ### Storing data in `context.locals` Section titled Storing data in context.locals `context.locals` is an object that can be manipulated inside the middleware. This `locals` object is forwarded across the request handling process and is available as a property to `APIContext` and `AstroGlobal`. This allows data to be shared between middlewares, API routes, and `.astro` pages. This is useful for storing request-specific data, such as user data, across the rendering step. Integration properties Integrations may set properties and provide functionality through the `locals` object. If you are using an integration, check its documentation to ensure you are not overriding any of its properties or doing unnecessary work. You can store any type of data inside `locals`: strings, numbers, and even complex data types such as functions and maps. src/middleware.js export function onRequest (context, next) { // intercept data from a request // optionally, modify the properties in `locals` context.locals.user.name = "John Wick"; context.locals.welcomeTitle = () => { return "Welcome back " + locals.user.name; }; // return a Response or the result of calling `next()` return next();}; Then you can use this information inside any `.astro` file with `Astro.locals`. src/pages/orders.astro ---const title = Astro.locals.welcomeTitle();const orders = Array.from(Astro.locals.orders.entries());const data = Astro.locals;---<h1>{title}</h1><p>This {data.property} is from middleware.</p><ul> {orders.map(order => { return <li>{/* do something with each order */}</li>; })}</ul> `locals` is an object that lives and dies within a single Astro route; when your route page is rendered, `locals` won’t exist anymore and a new one will be created. Information that needs to persist across multiple page requests must be stored elsewhere. Note The value of `locals` cannot be overridden at run time. Doing so would risk wiping out all the information stored by the user. Astro performs checks and will throw an error if `locals` are overridden. ## Example: redacting sensitive information Section titled Example: redacting sensitive information The example below uses middleware to replace “PRIVATE INFO” with the word “REDACTED” to allow you to render modified HTML on your page: src/middleware.js export const onRequest = async (context, next) => { const response = await next(); const html = await response.text(); const redactedHtml = html.replaceAll("PRIVATE INFO", "REDACTED"); return new Response(redactedHtml, { status: 200, headers: response.headers });}; ## Middleware types Section titled Middleware types You can import and use the utility function `defineMiddleware()` to take advantage of type safety: src/middleware.ts import { defineMiddleware } from "astro:middleware"; // `context` and `next` are automatically typedexport const onRequest = defineMiddleware((context, next) => { }); Instead, if you’re using JsDoc to take advantage of type safety, you can use `MiddlewareHandler`: src/middleware.js /** * @type {import("astro").MiddlewareHandler} */// `context` and `next` are automatically typedexport const onRequest = (context, next) => { }; To type the information inside `Astro.locals`, which gives you autocompletion inside `.astro` files and middleware code, declare a global namespace in the `env.d.ts` file: src/env.d.ts declare namespace App { interface Locals { user: { name: string }, welcomeTitle: () => string, orders: Map<string, object> }} Then, inside the middleware file, you can take advantage of autocompletion and type safety. ## Chaining middleware Section titled Chaining middleware Multiple middlewares can be joined in a specified order using `sequence()`: src/middleware.js import { sequence } from "astro:middleware"; async function validation(_, next) { console.log("validation request"); const response = await next(); console.log("validation response"); return response;} async function auth(_, next) { console.log("auth request"); const response = await next(); console.log("auth response"); return response;} async function greeting(_, next) { console.log("greeting request"); const response = await next(); console.log("greeting response"); return response;} export const onRequest = sequence(validation, auth, greeting); This will result in the following console order: Terminal window validation requestauth requestgreeting requestgreeting responseauth responsevalidation response ## Rewriting Section titled Rewriting **Added in:** `astro@4.13.0` The `APIContext` exposes a method called `rewrite()` which works the same way as Astro.rewrite. Use `context.rewrite()` inside middleware to display a different page’s content without redirecting your visitor to a new page. This will trigger a new rendering phase, causing any middleware to be re-executed. src/middleware.js import { isLoggedIn } from "~/auth.js"export function onRequest (context, next) { if (!isLoggedIn(context)) { // If the user is not logged in, update the Request to render the `/login` route and // add header to indicate where the user should be sent after a successful login. // Re-execute middleware. return context.rewrite(new Request("/login", { headers: { "x-redirect-to": context.url.pathname } })); } return next();}; You can also pass the `next()` function an optional URL path parameter to rewrite the current `Request` without retriggering a new rendering phase. The location of the rewrite path can be provided as a string, URL, or `Request`: src/middleware.js import { isLoggedIn } from "~/auth.js"export function onRequest (context, next) { if (!isLoggedIn(context)) { // If the user is not logged in, update the Request to render the `/login` route and // add header to indicate where the user should be sent after a successful login. // Return a new `context` to any following middlewares. return next(new Request("/login", { headers: { "x-redirect-to": context.url.pathname } })); } return next();}; The `next()` function accepts the same payload of the `Astro.rewrite()` function. The location of the rewrite path can be provided as a string, URL, or `Request`. When you have multiple middleware functions chained via sequence(), submitting a path to `next()` will rewrite the `Request` in place and the middleware will not execute again. The next middleware function in the chain will receive the new `Request` with its updated `context`: Calling `next()` with this signature will create a new `Request` object using the old `ctx.request`. This means that trying to consume `Request.body`, either before or after this rewrite, will throw a runtime error. This error is often raised with Astro Actions that use HTML forms. In these cases, we recommend handling rewrites from your Astro templates using `Astro.rewrite()` instead of using middleware. src/middleware.js // Current URL is https://example.com/blog // First middleware functionasync function first(_, next) { console.log(context.url.pathname) // this will log "/blog" // Rewrite to a new route, the homepage // Return updated `context` which is passed to next function return next("/")} // Current URL is still https://example.com/blog // Second middleware functionasync function second(context, next) { // Receives updated `context` console.log(context.url.pathname) // this will log "/" return next()} export const onRequest = sequence(first, second); ## Error pages Section titled Error pages Middleware will attempt to run for all on-demand rendered pages, even when a matching route cannot be found. This includes Astro’s default (blank) 404 page and any custom 404 pages. However, it is up to the adapter to decide whether that code runs. Some adapters may serve a platform-specific error page instead. Middleware will also attempt to run before serving a 500 error page, including a custom 500 page, unless the server error occurred in the execution of the middleware itself. If your middleware does not run successfully, then you will not have access to `Astro.locals` to render your 500 page. Learn --- ## Page: https://docs.astro.build/en/guides/prefetch/ Page load times play a big role in the usability and overall enjoyment of a site. Astro’s **opt-in prefetching** brings the benefits of near-instant page navigations to your multi-page application (MPA) as your visitors interact with the site. ## Enable prefetching Section titled Enable prefetching You can enable prefetching with the `prefetch` config: astro.config.mjs import { defineConfig } from 'astro/config'; export default defineConfig({ prefetch: true}); A prefetch script will be added to all pages of your site. You can then add the `data-astro-prefetch` attribute to any `<a />` links on your site to opt-in to prefetching. When you hover over the link, the script will fetch the page in the background. <a href="/about" data-astro-prefetch> Note that prefetching only works for links within your site, and not external links. ## Prefetch configuration Section titled Prefetch configuration The `prefetch` config also accepts an option object to further customize prefetching. ### Prefetch strategies Section titled Prefetch strategies Astro supports 4 prefetch strategies for various use cases: * `hover` (default): Prefetch when you hover over or focus on the link. * `tap`: Prefetch just before you click on the link. * `viewport`: Prefetch as the links enter the viewport. * `load`: Prefetch all links on the page after the page is loaded. You can specify a strategy for an individual link by passing it to the `data-astro-prefetch` attribute: <a href="/about" data-astro-prefetch="tap">About</a> Each strategy is fine-tuned to only prefetch when needed and save your users’ bandwidth. For example: * If a visitor is using data saver mode or has a slow connection, prefetch will fallback to the `tap` strategy. * Quickly hovering or scrolling over links will not prefetch them. ### Default prefetch strategy Section titled Default prefetch strategy The default prefetch strategy when adding the `data-astro-prefetch` attribute is `hover`. To change it, you can configure `prefetch.defaultStrategy` in your `astro.config.mjs` file: astro.config.mjs import { defineConfig } from 'astro/config'; export default defineConfig({ prefetch: { defaultStrategy: 'viewport' }}); ### Prefetch all links by default Section titled Prefetch all links by default If you want to prefetch all links, including those without the `data-astro-prefetch` attribute, you can set `prefetch.prefetchAll` to `true`: astro.config.mjs import { defineConfig } from 'astro/config'; export default defineConfig({ prefetch: { prefetchAll: true }}); You can then opt-out of prefetching for individual links by setting `data-astro-prefetch="false"`: <a href="/about" data-astro-prefetch="false">About</a> The default prefetch strategy for all links can be changed with `prefetch.defaultStrategy` as shown in the Default prefetch strategy section. ## Prefetch programmatically Section titled Prefetch programmatically As some navigation might not always appear as `<a />` links, you can also prefetch programmatically with the `prefetch()` API from the `astro:prefetch` module: <button id="btn">Click me</button> <script> import { prefetch } from 'astro:prefetch'; const btn = document.getElementById('btn'); btn.addEventListener('click', () => { prefetch('/about'); });</script> The `prefetch()` API includes the same data saver mode and slow connection detection so that it only prefetches when needed. To ignore slow connection detection, you can use the `ignoreSlowConnection` option: // Prefetch even on data saver mode or slow connectionprefetch('/about', { ignoreSlowConnection: true }); ### `eagerness` Section titled eagerness **Type:** `'immediate' | 'eager' | 'moderate' | 'conservative'` **Default:** `'immediate'` **Added in:** `astro@5.6.0` With the experimental `clientPrerender` flag enabled, you can use the `eagerness` option on `prefetch()` to suggest to the browser how eagerly it should prefetch/prerender link targets. This follows the same API described in the Speculation Rules API and defaults to `immediate` (the most eager option). In decreasing order of eagerness, the other options are `eager`, `moderate`, and `conservative`. The `eagerness` option allows you to balance the benefit of reduced wait times against bandwidth, memory, and CPU costs for your site visitors. Some browsers, such as Chrome, have limits in place to guard against over-speculating (prerendering/prefetching too many links). ------<script>// Control prefetching eagerness with `experimental.clientPrerender`import { prefetch } from 'astro:prefetch'; // This page is resource-intensiveprefetch('/data-heavy-dashboard', { eagerness: 'conservative' }); // This page is critical to the visitor's journeyprefetch('/getting-started'); // defaults to `{ eagerness: 'immediate' }` // This page may not be visitedprefetch('/terms-of-service', { eagerness: 'moderate' });</script> To use `prefetch()` programmatically with large sets of links, you can set `eagerness: 'moderate'` to take advantage of First In, First Out (FIFO) strategies and browser heuristics to let the browser decide when to prerender/prefetch them and in what order: <a class="link-moderate" href="/nice-link-1">A Nice Link 1</a><a class="link-moderate" href="/nice-link-2">A Nice Link 2</a><a class="link-moderate" href="/nice-link-3">A Nice Link 3</a><a class="link-moderate" href="/nice-link-4">A Nice Link 4</a>...<a class="link-moderate" href="/nice-link-20">A Nice Link 20</a> <script> import { prefetch } from 'astro:prefetch'; const linkModerate = document.getElementsByClassName('link-moderate'); linkModerate.forEach((link) => prefetch(link.getAttribute('href'), {eagerness: 'moderate'})); </script> Make sure to only import `prefetch()` in client-side scripts as it relies on browser APIs. ## Using with View Transitions Section titled Using with View Transitions When you use View Transitions on a page, prefetching will also be enabled by default. It sets a default configuration of `{ prefetchAll: true }` which enables prefetching for all links on the page. You can customize the prefetch configuration in `astro.config.mjs` to override the default. For example: astro.config.mjs import { defineConfig } from 'astro/config'; export default defineConfig({ // Disable prefetch completely prefetch: false}); astro.config.mjs import { defineConfig } from 'astro/config'; export default defineConfig({ // Keep prefetch, but only prefetch for links with `data-astro-prefetch` prefetch: { prefetchAll: false }}); ## Browser support Section titled Browser support Astro’s prefetching uses `<link rel="prefetch">` if supported by the browser, and falls back to the `fetch()` API otherwise. The most common browsers support Astro’s prefetching with subtle differences: ### Chrome Section titled Chrome Chrome supports `<link rel="prefetch">`. Prefetching works as intended. It also fully supports `<script type="speculationrules">` from the Speculation Rules API, which can be used to further describe prefetching strategies and rules, enhancing user experience for your Chrome users. You’ll need to enable `clientPrerender` experiment to utilize this functionality with `prefetch()` ### Firefox Section titled Firefox Firefox supports `<link rel="prefetch">` but may display errors or fail entirely: * Without an explicit cache header (e.g. `Cache-Control` or `Expires`), prefetching will error with `NS_BINDING_ABORTED`. * Even in the event of an error, if the response has a proper `ETag` header, it will be re-used on navigation. * Otherwise, if it errors with no other cache headers, the prefetch will not work. ### Safari Section titled Safari Safari does not support `<link rel="prefetch">` and will fall back to the `fetch()` API which requires cache headers (e.g. `Cache-Control`, `Expires`, and `ETag`) to be set. Otherwise, the prefetch will not work. **Edge case:** `ETag` headers do not work in private windows. ### Recommendations Section titled Recommendations To best support all browsers, make sure your pages have the proper cache headers. For static or prerendered pages, the `ETag` header is often automatically set by the deployment platform and is expected to work out of the box. For dynamic and server-side rendered pages, set the appropriate cache headers yourself based on the page content. Visit the MDN documentation on HTTP caching for more information. ## Migrating from `@astrojs/prefetch` Section titled Migrating from @astrojs/prefetch The `@astrojs/prefetch` integration was deprecated in v3.5.0 and will eventually be removed entirely. Use the following instructions to migrate to Astro’s built-in prefetching which replaces this integration. 1. Remove the `@astrojs/prefetch` integration and enable the `prefetch` config in `astro.config.mjs`: astro.config.mjs import { defineConfig } from 'astro/config';import prefetch from '@astrojs/prefetch'; export default defineConfig({ integrations: [prefetch()], prefetch: true}); 2. Convert from `@astrojs/prefetch`’s configuration options: * The deprecated integration used the `selector` config option to specify which links should be prefetched upon entering the viewport. Add `data-astro-prefetch="viewport"` to these individual links instead. <a href="/about" data-astro-prefetch="viewport"> * The deprecated integration used the `intentSelector` config option to specify which links should be prefetched when they were hovered over or focused. Add `data-astro-prefetch` or `data-astro-prefetch="hover"` to these individual links instead: <!-- You can omit the value if `defaultStrategy` is set to `hover` (default) --><a href="/about" data-astro-prefetch> <!-- Otherwise, you can explicitly define the prefetch strategy --><a href="/about" data-astro-prefetch="hover"> * The `throttles` option from `@astrojs/prefetch` is no longer needed as the new prefetch feature will automatically schedule and prefetch optimally. Learn --- ## Page: https://docs.astro.build/en/basics/astro-components/ **Astro components** are the basic building blocks of any Astro project. They are HTML-only templating components with no client-side runtime and use the `.astro` file extension. Note If you know HTML, you already know enough to write your first Astro component. Learn more in the Astro syntax reference. 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 or UI Framework 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. These “server islands” will render their content when it is available, without delaying the entire page load. ## Component Structure Section titled 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. src/components/EmptyComponent.astro ---// Component Script (JavaScript)---<!-- Component Template (HTML + JS Expressions) --> ### The Component Script Section titled 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 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 databaseconst 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. Read more about Astro’s built-in TypeScript support. ### The Component Template Section titled 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 also supports **JavaScript expressions**, Astro `<style>` and `<script>` tags, **imported components**, and **special Astro directives**. Data and values defined in the component script can be used in the component template to produce dynamically-created HTML. 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 Section titled 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: 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 Section titled 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. 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: 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`. 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. src/components/GreetingHeadline.astro ---const { greeting = "Hello", name = "Astronaut" } = Astro.props;---<h2>{greeting}, {name}!</h2> ## Slots Section titled 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. 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> 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: 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 Section titled 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: 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 />`. 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: src/components/CustomTable.astro ---// 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: src/components/StockTable.astro ---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! Note It is not possible to dynamically generate an Astro slot name, such as within a 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 Section titled 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. 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 Section titled Transferring slots Slots can be transferred to other components. For example, when creating nested layouts: src/layouts/BaseLayout.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} /> <slot name="head" /> </head> <body> <slot /> </body></html> 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. src/pages/index.astro ---import HomeLayout from '../layouts/HomeLayout.astro';---<HomeLayout> <title slot="head">Astro</title> <h1>Astro</h1></HomeLayout> ## HTML Components Section titled 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. Note A `<slot />` element inside an HTML component will work as it would in an Astro component. In order to use the HTML Web Component Slot element instead, add `is:inline` to your `<slot>` element. ## Next Steps Section titled Next Steps Read more about using UI framework components in your Astro project. Learn --- ## Page: https://docs.astro.build/en/basics/layouts/ **Layouts** are 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 with: * a **page shell** (`<html>`, `<head>` and `<body>` tags) * a **`<slot />`** to specify where individual page content should be injected. But, there is nothing special about a layout component! They can accept props and import and use other components like any other Astro component. They can include UI frameworks components and 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 `_`. ## Sample Layout Section titled Sample Layout 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> src/pages/index.astro ---import MySiteLayout from '../layouts/MySiteLayout.astro';---<MySiteLayout title="Home Page"> <p>My page content, wrapped in a layout!</p></MySiteLayout> Learn more about slots. ## Using TypeScript with layouts Section titled Using TypeScript with layouts Any Astro layout can be modified to introduce type safety & autocompletion by providing the types for your props: 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 Section titled 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 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 to query and render your content. src/pages/page.md ---layout: ../layouts/BlogPostLayout.astrotitle: "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 />` to indicate where the page’s Markdown content should be rendered. src/layouts/BlogPostLayout.astro ---// 1. The frontmatter prop gives access to frontmatter and other dataconst { 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 with the `MarkdownLayoutProps` helper: src/layouts/BlogPostLayout.astro ---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 safetyconst { 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 Section titled 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 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) Section titled 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: src/pages/posts/first-post.mdx ---layout: ../../layouts/BaseLayout.astrotitle: '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: src/layouts/BaseLayout.astro ---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. Learn more about Astro’s Markdown and MDX support in our Markdown guide. ## Nesting Layouts Section titled Nesting Layouts Layout components do not need to contain an entire page worth of HTML. You can break your layouts into smaller components, and combine layout components to create even more flexible, page templates. This pattern is useful when you want to share some code across multiple layouts. For example, a `BlogPostLayout.astro` layout component could style a post’s title, date and author. Then, a site-wide `BaseLayout.astro` could handle the rest of your page template, like navigation, footers, SEO meta tags, global styles, and fonts. You can also pass props received from your post to another layout, just like any other nested component. src/layouts/BlogPostLayout.astro ---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> Learn --- ## Page: https://docs.astro.build/en/guides/styling/ Astro was designed to make styling and writing CSS a breeze. Write your own CSS directly inside of an Astro component or import your favorite CSS library like Tailwind. Advanced styling languages like Sass and Less are also supported. ## Styling in Astro Section titled Styling in Astro Styling an Astro component is as easy as adding a `<style>` tag to your component or page template. When you place a `<style>` tag inside of an Astro component, Astro will detect the CSS and handle your styles for you, automatically. src/components/MyComponent.astro <style> h1 { color: red; }</style> ### Scoped Styles Section titled Scoped Styles Astro `<style>` CSS rules are automatically **scoped by default**. Scoped styles are compiled behind-the-scenes to only apply to HTML written inside of that same component. The CSS that you write inside of an Astro component is automatically encapsulated inside of that component. This CSS: src/pages/index.astro <style> h1 { color: red; } .text { color: blue; }</style> Compiles to this: <style> h1[data-astro-cid-hhnqfkh6] { color: red; } .text[data-astro-cid-hhnqfkh6] { color: blue; }</style> Scoped styles don’t leak and won’t impact the rest of your site. In Astro, it is okay to use low-specificity selectors like `h1 {}` or `p {}` because they will be compiled with scopes in the final output. Scoped styles also won’t apply to other Astro components contained inside of your template. If you need to style a child component, consider wrapping that component in a `<div>` (or other element) that you can then style. The specificity of scoped styles is preserved, allowing them to work consistently alongside other CSS files or CSS libraries while still preserving the exclusive boundaries that prevent styles from applying outside the component. ### Global Styles Section titled Global Styles While we recommend scoped styles for most components, you may eventually find a valid reason to write global, unscoped CSS. You can opt-out of automatic CSS scoping with the `<style is:global>` attribute. src/components/GlobalStyles.astro <style is:global> /* Unscoped, delivered as-is to the browser. Applies to all <h1> tags on your site. */ h1 { color: red; }</style> You can also mix global & scoped CSS rules together in the same `<style>` tag using the `:global()` selector. This becomes a powerful pattern for applying CSS styles to children of your component. src/components/MixedStyles.astro <style> /* Scoped to this component, only. */ h1 { color: red; } /* Mixed: Applies to child `h1` elements only. */ article :global(h1) { color: blue; }</style><h1>Title</h1><article><slot /></article> This is a great way to style things like blog posts, or documents with CMS-powered content where the contents live outside of Astro. But be careful: components whose appearance differs based on whether or not they have a certain parent component can become difficult to troubleshoot. Scoped styles should be used as often as possible. Global styles should be used only as-needed. ### Combining classes with `class:list` Section titled Combining classes with class:list If you need to combine classes on an element dynamically, you can use the `class:list` utility attribute in `.astro` files. src/components/ClassList.astro ---const { isRed } = Astro.props;---<!-- If `isRed` is truthy, class will be "box red". --><!-- If `isRed` is falsy, class will be "box". --><div class:list={['box', { red: isRed }]}><slot /></div> <style> .box { border: 1px solid blue; } .red { border-color: red; }</style> See our directives reference page to learn more about `class:list`. ### CSS Variables Section titled CSS Variables **Added in:** `astro@0.21.0` The Astro `<style>` can reference any CSS variables available on the page. You can also pass CSS variables directly from your component frontmatter using the `define:vars` directive. src/components/DefineVars.astro ---const foregroundColor = "rgb(221 243 228)";const backgroundColor = "rgb(24 121 78)";---<style define:vars={{ foregroundColor, backgroundColor }}> h1 { background-color: var(--backgroundColor); color: var(--foregroundColor); }</style><h1>Hello</h1> See our directives reference page to learn more about `define:vars`. ### Passing a `class` to a child component Section titled Passing a class to a child component In Astro, HTML attributes like `class` do not automatically pass through to child components. Instead, accept a `class` prop in the child component and apply it to the root element. When destructuring, you must rename it, because `class` is a reserved word in JavaScript. Using the default scoped style strategy, you must also pass the `data-astro-cid-*` attribute. You can do this by passing the `...rest` of the props to the component. If you have changed `scopedStyleStrategy` to `'class'` or `'where'`, the `...rest` prop is not necessary. src/components/MyComponent.astro ---const { class: className, ...rest } = Astro.props;---<div class={className} {...rest}> <slot/></div> src/pages/index.astro ---import MyComponent from "../components/MyComponent.astro"---<style> .red { color: red; }</style><MyComponent class="red">This will be red!</MyComponent> Scoped styles from parent components Because the `data-astro-cid-*` attribute includes the child in its parent’s scope, it is possible for styles to cascade from parent to child. To avoid this having unintended side effects, ensure you use unique class names in the child component. ### Inline styles Section titled Inline styles You can style HTML elements inline using the `style` attribute. This can be a CSS string or an object of CSS properties: src/pages/index.astro // These are equivalent:<p style={{ color: "brown", textDecoration: "underline" }}>My text</p><p style="color: brown; text-decoration: underline;">My text</p> ## External Styles Section titled External Styles There are two ways to resolve external global stylesheets: an ESM import for files located within your project source, and an absolute URL link for files in your `public/` directory, or hosted outside of your project. Read more about using static assets located in `public/` or `src/`. ### Import a local stylesheet Section titled Import a local stylesheet Using an npm package? You may need to update your `astro.config` when importing from npm packages. See the “import stylesheets from an npm package” section below. You can import stylesheets in your Astro component frontmatter using ESM import syntax. CSS imports work like any other ESM import in an Astro component, which should be referenced as **relative to the component** and must be written at the **top** of your component script, with any other imports. src/pages/index.astro ---// Astro will bundle and optimize this CSS for you automatically// This also works for preprocessor files like .scss, .styl, etc.import '../styles/utils.css';---<html><!-- Your page here --></html> CSS `import` via ESM are supported inside of any JavaScript file, including JSX components like React & Preact. This can be useful for writing granular, per-component styles for your React components. ### Import a stylesheet from an npm package Section titled Import a stylesheet from an npm package You may also need to load stylesheets from an external npm package. This is especially common for utilities like Open Props. If your package **recommends using a file extension** (i.e. `package-name/styles.css` instead of `package-name/styles`), this should work like any local stylesheet: src/pages/random-page.astro ---import 'package-name/styles.css';---<html><!-- Your page here --></html> If your package **does not suggest using a file extension** (i.e. `package-name/styles`), you’ll need to update your Astro config first! Say you are importing a CSS file from `package-name` called `normalize` (with the file extension omitted). To ensure we can prerender your page correctly, add `package-name` to the `vite.ssr.noExternal` array: astro.config.mjs import { defineConfig } from 'astro/config'; export default defineConfig({ vite: { ssr: { noExternal: ['package-name'], } }}) Note This is a Vite-specific setting that does _not_ relate to (or require) Astro SSR. Now, you are free to import `package-name/normalize`. This will be bundled and optimized by Astro like any other local stylesheet. src/pages/random-page.astro ---import 'package-name/normalize';---<html><!-- Your page here --></html> ### Load a static stylesheet via “link” tags Section titled Load a static stylesheet via “link” tags You can also use the `<link>` element to load a stylesheet on the page. This should be an absolute URL path to a CSS file located in your `/public` directory, or an URL to an external website. Relative `<link>` href values are not supported. src/pages/index.astro <head> <!-- Local: /public/styles/global.css --> <link rel="stylesheet" href="/styles/global.css" /> <!-- External --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.24.1/themes/prism-tomorrow.css" /></head> Because this approach uses the `public/` directory, it skips the normal CSS processing, bundling and optimizations that are provided by Astro. If you need these transformations, use the Import a Stylesheet method above. ## Cascading Order Section titled Cascading Order Astro components will sometimes have to evaluate multiple sources of CSS. For example, your component might import a CSS stylesheet, include its own `<style>` tag, _and_ be rendered inside a layout that imports CSS. When conflicting CSS rules apply to the same element, browsers first use _specificity_ and then _order of appearance_ to determine which value to show. If one rule is more _specific_ than another, no matter where the CSS rule appears, its value will take precedence: src/components/MyComponent.astro <style> h1 { color: red } div > h1 { color: purple }</style><div> <h1> This header will be purple! </h1></div> If two rules have the same specificity, then the _order of appearance_ is evaluated, and the last rule’s value will take precedence: src/components/MyComponent.astro <style> h1 { color: purple } h1 { color: red }</style><div> <h1> This header will be red! </h1></div> Astro CSS rules are evaluated in this order of appearance: * **`<link>` tags in the head** (lowest precedence) * **imported styles** * **scoped styles** (highest precedence) ### Scoped Styles Section titled Scoped Styles Depending on your chosen value for `scopedStyleStrategy`, scoped styles may or may not increase the CLASS column specificity. However, scoped styles will always come last in the order of appearance. These styles will therefore take precedence over other styles of the same specificity. For example, if you import a stylesheet that conflicts with a scoped style, the scoped style’s value will apply: src/components/make-it-purple.css h1 { color: purple;} src/components/MyComponent.astro ---import "./make-it-purple.css"---<style> h1 { color: red }</style><div> <h1> This header will be red! </h1></div> Scoped styles will be overwritten if the imported style is more specific. The style with a higher specificity will take precedence over the scoped style: src/components/make-it-purple.css #intro { color: purple;} src/components/MyComponent.astro ---import "./make-it-purple.css"---<style> h1 { color: red }</style><div> <h1 id="intro"> This header will be purple! </h1></div> ### Import Order Section titled Import Order When importing multiple stylesheets in an Astro component, the CSS rules are evaluated in the order that they are imported. A higher specificity will always determine which styles to show, no matter when the CSS is evaluated. But, when conflicting styles have the same specificity, the _last one imported_ wins: src/components/make-it-purple.css div > h1 { color: purple;} src/components/make-it-green.css div > h1 { color: green;} src/components/MyComponent.astro ---import "./make-it-green.css"import "./make-it-purple.css"---<style> h1 { color: red }</style><div> <h1> This header will be purple! </h1></div> While `<style>` tags are scoped and only apply to the component that declares them, _imported_ CSS can “leak”. Importing a component applies any CSS it imports, even if the component is never used: src/components/PurpleComponent.astro ---import "./make-it-purple.css"---<div> <h1>I import purple CSS.</h1></div> src/components/MyComponent.astro ---import "./make-it-green.css"import PurpleComponent from "./PurpleComponent.astro";---<style> h1 { color: red }</style><div> <h1> This header will be purple! </h1></div> Tip A common pattern in Astro is to import global CSS inside a Layout component. Be sure to import the Layout component before other imports so that it has the lowest precedence. ### Link Tags Section titled Link Tags Style sheets loaded via link tags are evaluated in order, before any other styles in an Astro file. Therefore, these styles will have lower precedence than imported stylesheets and scoped styles: src/pages/index.astro ---import "../components/make-it-purple.css"--- <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> <link rel="stylesheet" href="/styles/make-it-blue.css" /> </head> <body> <div> <h1>This will be purple</h1> </div> </body></html> ## CSS Integrations Section titled CSS Integrations Astro comes with support for adding popular CSS libraries, tools, and frameworks to your project like Tailwind and more! ### Tailwind Section titled Tailwind You can add Tailwind 4 support to your project with a CLI command, or install legacy dependencies manually for legacy Tailwind 3 support. In Astro `>=5.2.0`, use the `astro add tailwind` command for your package manager to install the official Vite Tailwind plugin. This provides support for Tailwind `>=4.0.0`. To add Tailwind 4 support to earlier versions of Astro, follow the instructions in the Tailwind docs to add this Vite plugin manually. (() => { class StarlightTabsRestore extends HTMLElement { connectedCallback() { const starlightTabs = this.closest('starlight-tabs'); if (!(starlightTabs instanceof HTMLElement) || typeof localStorage === 'undefined') return; const syncKey = starlightTabs.dataset.syncKey; if (!syncKey) return; const label = localStorage.getItem(\`starlight-synced-tabs\_\_${syncKey}\`); if (!label) return; const tabs = \[...starlightTabs?.querySelectorAll('\[role="tab"\]')\]; const tabIndexToRestore = tabs.findIndex( (tab) => tab instanceof HTMLAnchorElement && tab.textContent?.trim() === label ); const panels = starlightTabs?.querySelectorAll(':scope > \[role="tabpanel"\]'); const newTab = tabs\[tabIndexToRestore\]; const newPanel = panels\[tabIndexToRestore\]; if (tabIndexToRestore < 1 || !newTab || !newPanel) return; tabs\[0\]?.setAttribute('aria-selected', 'false'); tabs\[0\]?.setAttribute('tabindex', '-1'); panels?.\[0\]?.setAttribute('hidden', 'true'); newTab.removeAttribute('tabindex'); newTab.setAttribute('aria-selected', 'true'); newPanel.removeAttribute('hidden'); } } customElements.define('starlight-tabs-restore', StarlightTabsRestore); })() * npm * pnpm * Yarn Terminal window npx astro add tailwind Terminal window pnpm astro add tailwind Terminal window yarn astro add tailwind class r extends HTMLElement{static#e=new Map;#t;#n="starlight-synced-tabs\_\_";constructor(){super();const t=this.querySelector('\[role="tablist"\]');if(this.tabs=\[...t.querySelectorAll('\[role="tab"\]')\],this.panels=\[...this.querySelectorAll(':scope > \[role="tabpanel"\]')\],this.#t=this.dataset.syncKey,this.#t){const i=r.#e.get(this.#t)??\[\];i.push(this),r.#e.set(this.#t,i)}this.tabs.forEach((i,c)=>{i.addEventListener("click",e=>{e.preventDefault();const n=t.querySelector('\[aria-selected="true"\]');e.currentTarget!==n&&this.switchTab(e.currentTarget,c)}),i.addEventListener("keydown",e=>{const n=this.tabs.indexOf(e.currentTarget),s=e.key==="ArrowLeft"?n-1:e.key==="ArrowRight"?n+1:e.key==="Home"?0:e.key==="End"?this.tabs.length-1:null;s!==null&&this.tabs\[s\]&&(e.preventDefault(),this.switchTab(this.tabs\[s\],s))})})}switchTab(t,i,c=!0){if(!t)return;const e=c?this.getBoundingClientRect().top:0;this.tabs.forEach(s=>{s.setAttribute("aria-selected","false"),s.setAttribute("tabindex","-1")}),this.panels.forEach(s=>{s.hidden=!0});const n=this.panels\[i\];n&&(n.hidden=!1),t.removeAttribute("tabindex"),t.setAttribute("aria-selected","true"),c&&(t.focus(),r.#r(this,t),window.scrollTo({top:window.scrollY+(this.getBoundingClientRect().top-e),behavior:"instant"}))}#i(t){!this.#t||typeof localStorage>"u"||localStorage.setItem(this.#n+this.#t,t)}static#r(t,i){const c=t.#t,e=r.#s(i);if(!c||!e)return;const n=r.#e.get(c);if(n){for(const s of n){if(s===t)continue;const a=s.tabs.findIndex(o=>r.#s(o)===e);a!==-1&&s.switchTab(s.tabs\[a\],a,!1)}t.#i(e)}}static#s(t){return t.textContent?.trim()}}customElements.define("starlight-tabs",r); Then, import `tailwindcss` into `src/styles/global.css` (or another CSS file of your choosing) to make Tailwind classes available to your Astro project. This file including the import will be created by default if you used the `astro add tailwind` command to install the Vite plugin. src/styles/global.css @import "tailwindcss"; Import this file in the pages where you want Tailwind to apply. This is often done in a layout component so that Tailwind styles can be used on all pages sharing that layout: src/layouts/Layout.astro ---import "../styles/global.css";--- #### Legacy Tailwind 3 support Section titled Legacy Tailwind 3 support To add (or keep) support for Tailwind 3, you will need to have both `tailwindcss@3` and the official Astro Tailwind integration `@astrojs/tailwind` installed. Installing these dependencies manually is only used for legacy Tailwind 3 compatibility, and is not required for Tailwind 4. You will also need a legacy Tailwind configuration: 1. Install Tailwind and the Astro Tailwind integration to your project dependencies using your preferred package manager: * npm * pnpm * Yarn Terminal window npm install tailwindcss@3 @astrojs/tailwind Terminal window pnpm add tailwindcss@3 @astrojs/tailwind Terminal window yarn add tailwindcss@3 @astrojs/tailwind 2. Import the integration to your `astro.config.mjs` file, and add it to your `integrations[]` array: astro.config.mjs import { defineConfig } from 'astro/config';import tailwind from '@astrojs/tailwind'; export default defineConfig({ // ... integrations: [tailwind()], // ...}); 3. Create a `tailwind.config.mjs` file in your project’s root directory. You can use the following command to generate a basic configuration file for you: * npm * pnpm * Yarn Terminal window npx tailwindcss init Terminal window pnpm dlx tailwindcss init Terminal window yarn dlx tailwindcss init 4. Add the following basic configuration to your `tailwind.config.mjs` file: tailwind.config.mjs /** @type {import('tailwindcss').Config} */export default { content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], theme: { extend: {}, }, plugins: [],};  **Related recipe:** Style rendered Markdown with Tailwind Typography ## CSS Preprocessors Section titled CSS Preprocessors Astro supports CSS preprocessors such as Sass, Stylus, and Less through Vite. ### Sass and SCSS Section titled Sass and SCSS Terminal window npm install sass Use `<style lang="scss">` or `<style lang="sass">` in `.astro` files. ### Stylus Section titled Stylus Terminal window npm install stylus Use `<style lang="styl">` or `<style lang="stylus">` in `.astro` files. ### Less Section titled Less Terminal window npm install less Use `<style lang="less">` in `.astro` files. ### LightningCSS Section titled LightningCSS Terminal window npm install lightningcss Update your `vite` configuration in `astro.config.mjs`: astro.config.mjs import { defineConfig } from 'astro/config' export default defineConfig({ vite: { css: { transformer: "lightningcss", }, },}) ### In framework components Section titled In framework components You can also use all of the above CSS preprocessors within JS frameworks as well! Be sure to follow the patterns each framework recommends: * **React** / **Preact**: `import Styles from './styles.module.scss';` * **Vue**: `<style lang="scss">` * **Svelte**: `<style lang="scss">` ## PostCSS Section titled PostCSS Astro comes with PostCSS included as part of Vite. To configure PostCSS for your project, create a `postcss.config.cjs` file in the project root. You can import plugins using `require()` after installing them (for example `npm install autoprefixer`). postcss.config.cjs module.exports = { plugins: [ require('autoprefixer'), require('cssnano'), ],}; ## Frameworks and Libraries Section titled Frameworks and Libraries ### 📘 React / Preact Section titled 📘 React / Preact `.jsx` files support both global CSS and CSS Modules. To enable the latter, use the `.module.css` extension (or `.module.scss`/`.module.sass` if using Sass). src/components/MyReactComponent.jsx import './global.css'; // include global CSSimport Styles from './styles.module.css'; // Use CSS Modules (must end in `.module.css`, `.module.scss`, or `.module.sass`!) ### 📗 Vue Section titled 📗 Vue Vue in Astro supports the same methods as `vue-loader` does: * vue-loader - Scoped CSS * vue-loader - CSS Modules ### 📕 Svelte Section titled 📕 Svelte Svelte in Astro also works exactly as expected: Svelte Styling Docs. ## Markdown Styling Section titled Markdown Styling Any Astro styling methods are available to a Markdown layout component, but different methods will have different styling effects on your page. You can apply global styles to your Markdown content by adding imported stylesheets to the layout that wraps your page content. It is also possible to style your Markdown with `<style is:global>` tags in the layout component. Note that any styles added are subject to Astro’s cascading order, and you should check your rendered page carefully to ensure your styles are being applied as intended. You can also add CSS integrations including Tailwind. If you are using Tailwind, the typography plugin can be useful for styling Markdown. ## Production Section titled Production ### Bundle control Section titled Bundle control When Astro builds your site for production deployment, it minifies and combines your CSS into chunks. Each page on your site gets its own chunk, and additionally, CSS that is shared between multiple pages is further split off into their own chunks for reuse. However, when you have several pages sharing styles, some shared chunks can become really small. If all of them were sent separately, it would lead to many stylesheets requests and affect site performance. Therefore, by default Astro will link only those in your HTML above 4kB in size as `<link rel="stylesheet">` tags, while inlining smaller ones into `<style type="text/css">`. This approach provides a balance between the number of additional requests and the volume of CSS that can be cached between pages. You can configure the size at which stylesheets will be linked externally (in bytes) using the `assetsInlineLimit` vite build option. Note that this option affects script and image inlining as well. astro.config.mjs import { defineConfig } from 'astro/config'; export default defineConfig({ vite: { build: { assetsInlineLimit: 1024, } };}); If you would rather all project styles remain external, you can configure the `inlineStylesheets` build option. astro.config.mjs import { defineConfig } from 'astro/config'; export default defineConfig({ build: { inlineStylesheets: 'never' }}); You can also set this option to `'always'` which will inline all stylesheets. ## Advanced Section titled Advanced Caution Be careful when bypassing Astro’s built-in CSS bundling! Styles won’t be automatically included in the built output, and it is your responsibility to make sure that the referenced file is properly included in the final page output. ### `?raw` CSS Imports Section titled ?raw CSS Imports For advanced use cases, CSS can be read directly from disk without being bundled or optimized by Astro. This can be useful when you need complete control over some snippet of CSS, and need to bypass Astro’s automatic CSS handling. This is not recommended for most users. src/components/RawInlineStyles.astro ---// Advanced example! Not recommended for most users.import rawStylesCSS from '../styles/main.css?raw';---<style is:inline set:html={rawStylesCSS}></style> See Vite’s docs for full details. ### `?url` CSS Imports Section titled ?url CSS Imports For advanced use cases, you can import a direct URL reference for a CSS file inside of your project `src/` directory. This can be useful when you need complete control over how a CSS file is loaded on the page. However, this will prevent the optimization of that CSS file with the rest of your page CSS . This is not recommended for most users. Instead, place your CSS files inside of `public/` to get a consistent URL reference. Caution Importing a smaller CSS file with `?url` may return the base64 encoded contents of the CSS file as a data URL in your final build. Either write your code to support encoded data URLs (`data:text/css;base64,...`) or set the `vite.build.assetsInlineLimit` config option to `0` to disable this feature. src/components/RawStylesUrl.astro ---// Advanced example! Not recommended for most users.import stylesUrl from '../styles/main.css?url';---<link rel="preload" href={stylesUrl} as="style"><link rel="stylesheet" href={stylesUrl}> See Vite’s docs for full details. Learn --- ## Page: https://docs.astro.build/en/guides/fonts/ This guide will show you how to add web fonts to your project and use them in your components. Experimental Fonts API Learn about Astro’s experimental Fonts API that allows you to use fonts from your filesystem and various font providers through a unified, fully customizable, and type-safe API. ## Using a local font file Section titled Using a local font file This example will demonstrate adding a custom font using the font file `DistantGalaxy.woff`. 1. Add your font file to `public/fonts/`. 2. Add the following `@font-face` statement to your CSS. This could be in a global `.css` file you import, a `<style is:global>` block, or a `<style>` block in a specific layout or component where you want to use this font. /* Register your custom font family and tell the browser where to find it. */@font-face { font-family: 'DistantGalaxy'; src: url('/fonts/DistantGalaxy.woff') format('woff'); font-weight: normal; font-style: normal; font-display: swap;} 3. Use the `font-family` value from the `@font-face` statement to style elements in your component or layout. In this example, the `<h1>` heading will have the custom font applied, while the paragraph `<p>` will not. src/pages/example.astro ------ <h1>In a galaxy far, far away...</h1> <p>Custom fonts make my headings much cooler!</p> <style>h1 { font-family: 'DistantGalaxy', sans-serif;}</style> ## Using Fontsource Section titled Using Fontsource The Fontsource project simplifies using Google Fonts and other open-source fonts. It provides npm modules you can install for the fonts you want to use. 1. Find the font you want to use in Fontsource’s catalog. This example will use Twinkle Star. 2. Install the package for your chosen font. (() => { class StarlightTabsRestore extends HTMLElement { connectedCallback() { const starlightTabs = this.closest('starlight-tabs'); if (!(starlightTabs instanceof HTMLElement) || typeof localStorage === 'undefined') return; const syncKey = starlightTabs.dataset.syncKey; if (!syncKey) return; const label = localStorage.getItem(\`starlight-synced-tabs\_\_${syncKey}\`); if (!label) return; const tabs = \[...starlightTabs?.querySelectorAll('\[role="tab"\]')\]; const tabIndexToRestore = tabs.findIndex( (tab) => tab instanceof HTMLAnchorElement && tab.textContent?.trim() === label ); const panels = starlightTabs?.querySelectorAll(':scope > \[role="tabpanel"\]'); const newTab = tabs\[tabIndexToRestore\]; const newPanel = panels\[tabIndexToRestore\]; if (tabIndexToRestore < 1 || !newTab || !newPanel) return; tabs\[0\]?.setAttribute('aria-selected', 'false'); tabs\[0\]?.setAttribute('tabindex', '-1'); panels?.\[0\]?.setAttribute('hidden', 'true'); newTab.removeAttribute('tabindex'); newTab.setAttribute('aria-selected', 'true'); newPanel.removeAttribute('hidden'); } } customElements.define('starlight-tabs-restore', StarlightTabsRestore); })() * npm * pnpm * Yarn Terminal window npm install @fontsource/twinkle-star Terminal window pnpm add @fontsource/twinkle-star Terminal window yarn add @fontsource/twinkle-star class r extends HTMLElement{static#e=new Map;#t;#n="starlight-synced-tabs\_\_";constructor(){super();const t=this.querySelector('\[role="tablist"\]');if(this.tabs=\[...t.querySelectorAll('\[role="tab"\]')\],this.panels=\[...this.querySelectorAll(':scope > \[role="tabpanel"\]')\],this.#t=this.dataset.syncKey,this.#t){const i=r.#e.get(this.#t)??\[\];i.push(this),r.#e.set(this.#t,i)}this.tabs.forEach((i,c)=>{i.addEventListener("click",e=>{e.preventDefault();const n=t.querySelector('\[aria-selected="true"\]');e.currentTarget!==n&&this.switchTab(e.currentTarget,c)}),i.addEventListener("keydown",e=>{const n=this.tabs.indexOf(e.currentTarget),s=e.key==="ArrowLeft"?n-1:e.key==="ArrowRight"?n+1:e.key==="Home"?0:e.key==="End"?this.tabs.length-1:null;s!==null&&this.tabs\[s\]&&(e.preventDefault(),this.switchTab(this.tabs\[s\],s))})})}switchTab(t,i,c=!0){if(!t)return;const e=c?this.getBoundingClientRect().top:0;this.tabs.forEach(s=>{s.setAttribute("aria-selected","false"),s.setAttribute("tabindex","-1")}),this.panels.forEach(s=>{s.hidden=!0});const n=this.panels\[i\];n&&(n.hidden=!1),t.removeAttribute("tabindex"),t.setAttribute("aria-selected","true"),c&&(t.focus(),r.#r(this,t),window.scrollTo({top:window.scrollY+(this.getBoundingClientRect().top-e),behavior:"instant"}))}#i(t){!this.#t||typeof localStorage>"u"||localStorage.setItem(this.#n+this.#t,t)}static#r(t,i){const c=t.#t,e=r.#s(i);if(!c||!e)return;const n=r.#e.get(c);if(n){for(const s of n){if(s===t)continue;const a=s.tabs.findIndex(o=>r.#s(o)===e);a!==-1&&s.switchTab(s.tabs\[a\],a,!1)}t.#i(e)}}static#s(t){return t.textContent?.trim()}}customElements.define("starlight-tabs",r); Tip You’ll find the correct package name in the “Quick Installation” section of each font page on Fontsource’s website. It will start with `@fontsource/` or `@fontsource-variable/` followed by the name of the font. 3. Import the font package in the component where you want to use the font. Usually, you will want to do this in a common layout component to make sure the font is available across your site. The import will automatically add the necessary `@font-face` rules needed to set up the font. src/layouts/BaseLayout.astro ---import '@fontsource/twinkle-star';--- 4. Use the font’s name as shown in the `body` example on its Fontsource page as the `font-family` value. This will work anywhere you can write CSS in your Astro project. h1 { font-family: "Twinkle Star", cursive;} To optimize your website’s rendering times, you may want to preload fonts that are essential for the initial page display. See the Fontsource guide to preloading fonts for more information and usage. ## Register fonts in Tailwind Section titled Register fonts in Tailwind If you are using Tailwind, you can use either of the previous methods on this page to install your font, with some modifications. You can either add an `@font-face` statement for a local font or use Fontsource’s `import` strategy to install your font. To register your font in Tailwind: 1. Follow either of the guides above, but skip the final step of adding `font-family` to your CSS. 2. Add the typeface name to `src/styles/global.css`. This example adds `Inter` to the sans-serif font stack. src/styles/global.css @import 'tailwindcss'; @theme { --font-sans: 'Inter', 'sans-serif';} Now, all sans-serif text (the default with Tailwind) in your project will use your chosen font and the `font-sans` class will also apply the Inter font. See Tailwind’s docs on adding custom font families for more information. ## More resources Section titled More resources * Learn how web fonts work in MDN’s web fonts guide. * Generate CSS for your font with Font Squirrel’s Webfont Generator. Learn --- ## Page: https://docs.astro.build/en/guides/client-side-scripts/ You can add interactivity to your Astro components without using a UI framework like React, Svelte, Vue, etc. using standard HTML `<script>` tags. This allows you to send JavaScript to run in the browser and add functionality to your Astro components. ## Client-Side Scripts Section titled Client-Side Scripts Scripts can be used to add event listeners, send analytics data, play animations, and everything else JavaScript can do on the web. src/components/ConfettiButton.astro <button data-confetti-button>Celebrate!</button> <script> // Import npm modules. import confetti from 'canvas-confetti'; // Find our component DOM on the page. const buttons = document.querySelectorAll('[data-confetti-button]'); // Add event listeners to fire confetti when a button is clicked. buttons.forEach((button) => { button.addEventListener('click', () => confetti()); });</script> By default, Astro processes and bundles `<script>` tags, adding support for importing npm modules, writing TypeScript, and more. ## Using `<script>` in Astro Section titled Using <script> in Astro In `.astro` files, you can add client-side JavaScript by adding one (or more) `<script>` tags. In this example, adding the `<Hello />` component to a page will log a message to the browser console. src/components/Hello.astro <h1>Welcome, world!</h1> <script> console.log('Welcome, browser console!');</script> ### Script processing Section titled Script processing By default, `<script>` tags are processed by Astro. * Any imports will be bundled, allowing you to import local files or Node modules. * The processed script will be injected at where it’s declared with `type="module"`. * TypeScript is fully supported, including importing TypeScript files. * If your component is used several times on a page, the script will only be included once. src/components/Example.astro <script> // Processed! Bundled! TypeScript-supported! // Importing local scripts and Node modules works.</script> The `type="module"` attribute makes the browser treat the script as a JavaScript module. This has several performance benefits: * Rendering is not blocked. The browser continues to process the rest of the HTML while the module script and its dependencies load. * The browser waits for HTML to be processed before executing module scripts. You do not need to listen for the “load” event. * `async` and `defer` attributes are unnecessary. Module scripts are always deferred. Note The `async` attribute is valuable for normal scripts because it prevents them from blocking rendering. However, module scripts already have this behavior. Adding `async` to a module script will cause it to execute before the page has fully loaded. This is probably not what you want. ### Opting out of processing Section titled Opting out of processing To prevent Astro from processing a script, add the `is:inline` directive. src/components/InlineScript.astro <script is:inline> // Will be rendered into the HTML exactly as written! // Local imports are not resolved and will not work. // If in a component, repeats each time the component is used.</script> Note Astro will not process your script tags in some situations. In particular, adding `type="module"` or any attribute other than `src` to a `<script>` tag will cause Astro to treat the tag as if it had an `is:inline` directive. See our directives reference page for more information about the directives available on `<script>` tags. ### Include JavaScript files on your page Section titled Include JavaScript files on your page You may want to write your scripts as separate `.js`/`.ts` files or need to reference an external script on another server. You can do this by referencing these in a `<script>` tag’s `src` attribute. #### Import local scripts Section titled Import local scripts **When to use this:** when your script lives inside of `src/`. Astro will build, optimize, and add these scripts to the page for you, following its script processing rules. src/components/LocalScripts.astro <!-- relative path to script at `src/scripts/local.js` --><script src="../scripts/local.js"></script> <!-- also works for local TypeScript files --><script src="./script-with-types.ts"></script> #### Load external scripts Section titled Load external scripts **When to use this:** when your JavaScript file lives inside of `public/` or on a CDN. To load scripts outside of your project’s `src/` folder, include the `is:inline` directive. This approach skips the JavaScript processing, bundling, and optimizations that are provided by Astro when you import scripts as described above. src/components/ExternalScripts.astro <!-- absolute path to a script at `public/my-script.js` --><script is:inline src="/my-script.js"></script> <!-- full URL to a script on a remote server --><script is:inline src="https://my-analytics.com/script.js"></script> ## Common script patterns Section titled Common script patterns ### Handle `onclick` and other events Section titled Handle onclick and other events Some UI frameworks use custom syntax for event handling like `onClick={...}` (React/Preact) or `@click="..."` (Vue). Astro follows standard HTML more closely and does not use custom syntax for events. Instead, you can use `addEventListener` in a `<script>` tag to handle user interactions. src/components/AlertButton.astro <button class="alert">Click me!</button> <script> // Find all buttons with the `alert` class on the page. const buttons = document.querySelectorAll('button.alert'); // Handle clicks on each button. buttons.forEach((button) => { button.addEventListener('click', () => { alert('Button was clicked!'); }); });</script> Note If you have multiple `<AlertButton />` components on a page, Astro will not run the script multiple times. Scripts are bundled and only included once per page. Using `querySelectorAll` ensures that this script attaches the event listener to every button with the `alert` class found on the page. ### Web components with custom elements Section titled Web components with custom elements You can create your own HTML elements with custom behavior using the Web Components standard. Defining a custom element in a `.astro` component allows you to build interactive components without needing a UI framework library. In this example, we define a new `<astro-heart>` HTML element that tracks how many times you click the heart button and updates the `<span>` with the latest count. src/components/AstroHeart.astro <!-- Wrap the component elements in our custom element “astro-heart”. --><astro-heart> <button aria-label="Heart">💜</button> × <span>0</span></astro-heart> <script> // Define the behaviour for our new type of HTML element. class AstroHeart extends HTMLElement { connectedCallback() { let count = 0; const heartButton = this.querySelector('button'); const countSpan = this.querySelector('span'); // Each time the button is clicked, update the count. heartButton.addEventListener('click', () => { count++; countSpan.textContent = count.toString(); }); } } // Tell the browser to use our AstroHeart class for <astro-heart> elements. customElements.define('astro-heart', AstroHeart);</script> There are two advantages to using a custom element here: 1. Instead of searching the whole page using `document.querySelector()`, you can use `this.querySelector()`, which only searches within the current custom element instance. This makes it easier to work with only the children of one component instance at a time. 2. Although a `<script>` only runs once, the browser will run our custom element’s `constructor()` method each time it finds `<astro-heart>` on the page. This means you can safely write code for one component at a time, even if you intend to use this component multiple times on a page. You can learn more about custom elements in web.dev’s Reusable Web Components guide and MDN’s introduction to custom elements. ### Pass frontmatter variables to scripts Section titled Pass frontmatter variables to scripts In Astro components, the code in the frontmatter between the `---` fences runs on the server and is not available in the browser. To send variables from the server to the client, we need a way to store our variables and then read them when JavaScript runs in the browser. One way to do this is to use `data-*` attributes to store the value of variables in your HTML output. Scripts, including custom elements, can then read these attributes using an element’s `dataset` property once your HTML loads in the browser. In this example component, a `message` prop is stored in a `data-message` attribute, so the custom element can read `this.dataset.message` and get the value of the prop in the browser. src/components/AstroGreet.astro ---const { message = 'Welcome, world!' } = Astro.props;--- <!-- Store the message prop as a data attribute. --><astro-greet data-message={message}> <button>Say hi!</button></astro-greet> <script> class AstroGreet extends HTMLElement { connectedCallback() { // Read the message from the data attribute. const message = this.dataset.message; const button = this.querySelector('button'); button.addEventListener('click', () => { alert(message); }); } } customElements.define('astro-greet', AstroGreet);</script> Now we can use our component multiple times and be greeted by a different message for each one. src/pages/example.astro ---import AstroGreet from '../components/AstroGreet.astro';--- <!-- Use the default message: “Welcome, world!” --><AstroGreet /> <!-- Use custom messages passed as a props. --><AstroGreet message="Lovely day to build components!" /><AstroGreet message="Glad you made it! 👋" /> Did you know? This is actually what Astro does behind the scenes when you pass props to a component written using a UI framework like React! For components with a `client:*` directive, Astro creates an `<astro-island>` custom element with a `props` attribute that stores your server-side props in the HTML output. ### Combining scripts and UI Frameworks Section titled Combining scripts and UI Frameworks Elements rendered by a UI framework may not be available yet when a `<script>` tag executes. If your script also needs to handle UI framework components, using a custom element is recommended. Learn --- ## Page: https://docs.astro.build/en/guides/integrations-guide/react/ This **Astro integration** enables rendering and client-side hydration for your React components. ## Installation Section titled Installation Astro includes an `astro add` command to automate the setup of official integrations. If you prefer, you can install integrations manually instead. To install `@astrojs/react`, run the following from your project directory and follow the prompts: (() => { class StarlightTabsRestore extends HTMLElement { connectedCallback() { const starlightTabs = this.closest('starlight-tabs'); if (!(starlightTabs instanceof HTMLElement) || typeof localStorage === 'undefined') return; const syncKey = starlightTabs.dataset.syncKey; if (!syncKey) return; const label = localStorage.getItem(\`starlight-synced-tabs\_\_${syncKey}\`); if (!label) return; const tabs = \[...starlightTabs?.querySelectorAll('\[role="tab"\]')\]; const tabIndexToRestore = tabs.findIndex( (tab) => tab instanceof HTMLAnchorElement && tab.textContent?.trim() === label ); const panels = starlightTabs?.querySelectorAll(':scope > \[role="tabpanel"\]'); const newTab = tabs\[tabIndexToRestore\]; const newPanel = panels\[tabIndexToRestore\]; if (tabIndexToRestore < 1 || !newTab || !newPanel) return; tabs\[0\]?.setAttribute('aria-selected', 'false'); tabs\[0\]?.setAttribute('tabindex', '-1'); panels?.\[0\]?.setAttribute('hidden', 'true'); newTab.removeAttribute('tabindex'); newTab.setAttribute('aria-selected', 'true'); newPanel.removeAttribute('hidden'); } } customElements.define('starlight-tabs-restore', StarlightTabsRestore); })() * npm * pnpm * Yarn Terminal window npx astro add react Terminal window pnpm astro add react Terminal window yarn astro add react class r extends HTMLElement{static#e=new Map;#t;#n="starlight-synced-tabs\_\_";constructor(){super();const t=this.querySelector('\[role="tablist"\]');if(this.tabs=\[...t.querySelectorAll('\[role="tab"\]')\],this.panels=\[...this.querySelectorAll(':scope > \[role="tabpanel"\]')\],this.#t=this.dataset.syncKey,this.#t){const i=r.#e.get(this.#t)??\[\];i.push(this),r.#e.set(this.#t,i)}this.tabs.forEach((i,c)=>{i.addEventListener("click",e=>{e.preventDefault();const n=t.querySelector('\[aria-selected="true"\]');e.currentTarget!==n&&this.switchTab(e.currentTarget,c)}),i.addEventListener("keydown",e=>{const n=this.tabs.indexOf(e.currentTarget),s=e.key==="ArrowLeft"?n-1:e.key==="ArrowRight"?n+1:e.key==="Home"?0:e.key==="End"?this.tabs.length-1:null;s!==null&&this.tabs\[s\]&&(e.preventDefault(),this.switchTab(this.tabs\[s\],s))})})}switchTab(t,i,c=!0){if(!t)return;const e=c?this.getBoundingClientRect().top:0;this.tabs.forEach(s=>{s.setAttribute("aria-selected","false"),s.setAttribute("tabindex","-1")}),this.panels.forEach(s=>{s.hidden=!0});const n=this.panels\[i\];n&&(n.hidden=!1),t.removeAttribute("tabindex"),t.setAttribute("aria-selected","true"),c&&(t.focus(),r.#r(this,t),window.scrollTo({top:window.scrollY+(this.getBoundingClientRect().top-e),behavior:"instant"}))}#i(t){!this.#t||typeof localStorage>"u"||localStorage.setItem(this.#n+this.#t,t)}static#r(t,i){const c=t.#t,e=r.#s(i);if(!c||!e)return;const n=r.#e.get(c);if(n){for(const s of n){if(s===t)continue;const a=s.tabs.findIndex(o=>r.#s(o)===e);a!==-1&&s.switchTab(s.tabs\[a\],a,!1)}t.#i(e)}}static#s(t){return t.textContent?.trim()}}customElements.define("starlight-tabs",r); If you run into any issues, feel free to report them to us on GitHub and try the manual installation steps below. ### Manual Install Section titled Manual Install First, install the `@astrojs/react` package: * npm * pnpm * Yarn Terminal window npm install @astrojs/react Terminal window pnpm add @astrojs/react Terminal window yarn add @astrojs/react 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: * npm * pnpm * Yarn Terminal window npm install react react-dom @types/react @types/react-dom Terminal window pnpm add react react-dom @types/react @types/react-dom Terminal window yarn add react react-dom @types/react @types/react-dom Then, apply the integration to your `astro.config.*` file using the `integrations` property: 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. tsconfig.json { "extends": "astro/tsconfigs/strict", "include": [".astro/types.d.ts", "**/*"], "exclude": ["dist"], "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "react" }} ## Getting started Section titled Getting started To use your first React component in Astro, head to our UI framework documentation. You’ll explore: * 📦 how framework components are loaded, * 💧 client-side hydration options, and * 🤝 opportunities to mix and nest frameworks together ## Options Section titled Options ### Combining multiple JSX frameworks Section titled 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: 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 Section titled 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: ---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: astro.config.mjs import { defineConfig } from 'astro/config';import react from '@astrojs/react'; export default defineConfig({ // ... integrations: [ react({ experimentalReactChildren: true, }), ],}); ### Disable streaming (experimental) Section titled 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`: astro.config.mjs import { defineConfig } from 'astro/config';import react from '@astrojs/react'; export default defineConfig({ // ... integrations: [ react({ experimentalDisableStreaming: true, }), ],}); ## More integrations ### Front-end frameworks *  ### @astrojs/alpinejs *  ### @astrojs/preact *  ### @astrojs/react *  ### @astrojs/solid-js *  ### @astrojs/svelte *  ### @astrojs/vue ### SSR adapters *  ### @astrojs/cloudflare *  ### @astrojs/netlify *  ### @astrojs/node *  ### @astrojs/vercel ### Other integrations *  ### @astrojs/db *  ### @astrojs/mdx *  ### @astrojs/markdoc *  ### @astrojs/partytown *  ### @astrojs/sitemap Learn