Static Routes
Static routes are the foundation of Astro’s file-based routing system. Any .astro, .md, or .mdx file you place inside src/pages/ automatically becomes a page, and its location on disk maps directly to its URL. There are no router configs to maintain and no boilerplate to register — the file system is the route table. Because Astro ships zero JavaScript by default, each of these pages renders to a fast, static HTML document unless you opt into interactivity with islands.
How files map to URLs
Astro takes the path of a file relative to src/pages/, strips its extension, and uses the result as the URL. Folders become path segments, so the directory structure mirrors your site’s information architecture.
| File path | Generated URL |
|---|---|
src/pages/index.astro | / |
src/pages/about.astro | /about |
src/pages/blog/index.astro | /blog |
src/pages/blog/hello.md | /blog/hello |
src/pages/legal/privacy.mdx | /legal/privacy |
The key rule: a file named index resolves to the root of its folder, while any other name resolves to a segment matching that name. That single convention covers the vast majority of pages you’ll build.
By default Astro builds “pretty” URLs without a trailing slash or
.htmlextension. You can change this with thebuild.formatandtrailingSlashoptions inastro.config.mjsif your host requires.htmlfiles or strict trailing slashes.
A basic page
A static .astro page is just a component. The optional fenced block at the top (the component script) runs at build time on the server, and everything below renders to HTML.
---
// src/pages/about.astro
const team = ["Ada", "Linus", "Grace"];
---
<html lang="en">
<head>
<title>About us</title>
</head>
<body>
<h1>About us</h1>
<ul>
{team.map((name) => <li>{name}</li>)}
</ul>
</body>
</html>
This compiles to plain HTML with no client-side JavaScript shipped. The team array is evaluated once during the build, not in the browser.
Index files and nested folders
Index files let you give a folder its own landing page. Consider this structure:
src/pages/
├── index.astro → /
├── about.astro → /about
└── products/
├── index.astro → /products
├── pricing.astro → /products/pricing
└── support/
└── index.astro → /products/support
Folders can nest as deeply as you like, and each level can have its own index page plus any number of named pages. This makes it easy to model section landing pages (/products) alongside their children (/products/pricing).
Markdown and MDX pages
You don’t need a .astro file to create a route. Markdown and MDX files in src/pages/ become pages too, with frontmatter driving their metadata. Pair them with a layout to wrap the content in your site shell.
---
layout: ../../layouts/PostLayout.astro
title: "Hello world"
pubDate: 2026-06-14
---
# Hello world
This Markdown file lives at `src/pages/blog/hello.md`
and is served at **/blog/hello** automatically.
The layout frontmatter key points to an .astro layout that receives the rendered Markdown via <slot /> and the frontmatter via Astro.props.frontmatter.
For collections of related content (a blog, docs, changelog), prefer content collections in
src/content/over loose Markdown insrc/pages/. Collections give you schema validation and type safety, and you render them through a single dynamic route.
Linking between pages
Reference static routes with ordinary root-relative href values. Astro does not require a special <Link> component.
---
// src/pages/index.astro
const links = [
{ href: "/about", label: "About" },
{ href: "/products", label: "Products" },
{ href: "/products/pricing", label: "Pricing" },
];
---
<nav>
{links.map((l) => <a href={l.href}>{l.label}</a>)}
</nav>
Inspecting generated routes
When you build the site, Astro prints each route it emitted, which is the fastest way to confirm your files mapped where you expect.
npm run build
Output:
▶ src/pages/index.astro
└─ /index.html
▶ src/pages/about.astro
└─ /about/index.html
▶ src/pages/products/index.astro
└─ /products/index.html
▶ src/pages/products/pricing.astro
└─ /products/pricing/index.html
Best practices
- Use
index.astrofor section landing pages and reserve named files for leaf pages — it keeps the URL hierarchy intuitive. - Keep one-off, hand-authored pages (about, pricing, contact) as static routes; move large repeating datasets into content collections with a dynamic route.
- Mirror your URL structure with your folder structure so the file system stays self-documenting.
- Add interactivity only where needed by mounting islands with
client:*directives, preserving the zero-JS-by-default baseline. - Set
trailingSlashandbuild.formatexplicitly inastro.config.mjsto match your host and avoid surprise redirects. - Run
npm run buildand read the route list whenever you’re unsure how a file resolved.