# Pages and Layouts
Astral discovers pages from the configured `pages/` directory and renders them into static HTML routes.
## Markdown pages
Markdown pages use MDEx and YAML frontmatter:
```markdown
---
title: About
layout: default.html
---
# About
```
Routes are derived from file paths:
```text
pages/index.md -> /
pages/about.md -> /about/
pages/blog/post.md -> /blog/post/
```
Set `permalink` to override the route:
```markdown
---
title: About
permalink: /about-us/
---
```
Set `layout: false` to render a page without a layout.
## Components in Markdown
Markdown pages and collection entries can use local `.astral` components through MDEx's HEEx integration:
```md
# Project
<.callout>
This block is rendered by `components/callout.astral`.
</.callout>
```
Components are discovered from the configured `components/` directory, the same as `.astral` pages and layouts. Use HEEx local component syntax (`<.name>`) rather than MDX imports.
Assigns are available in Markdown with HEEx expression syntax:
```md
<p>{@metadata["title"]}</p>
<p>{@entry.data.title}</p>
```
Prefer static Markdown heading text. Heading ids are generated by the Markdown parser before HEEx expressions are evaluated.
## HTML pages
Plain `.html` files in `pages/` are supported for pages that do not need Markdown processing.
## Dynamic file routes
Use bracket segments for collection-backed dynamic pages:
```text
pages/blog/[slug].astral -> /blog/:slug
pages/docs/[...path].md -> /docs/*path
```
Bracket filenames are portable file-route sugar. Astral converts them to Elixir/Phoenix-style route patterns internally.
Dynamic page templates render once for each collection entry whose route matches the file route pattern. Route params are exposed as string-keyed `@params` assigns:
```astral
<h1>{@entry.data.title}</h1>
<p>Slug: {@params["slug"]}</p>
```
```eex
<p>Path: <%= @params["path"] %></p>
```
A dynamic file route overrides the default collection entry page for the same route, letting the page template own the final HTML.
## Layouts
Layouts live in the configured layouts directory. EEx layouts use `@content` where the page HTML should be inserted:
```html
<!doctype html>
<html lang="en">
<head>
<title><%= @page.title || "My Site" %></title>
</head>
<body>
<main data-route="<%= @route %>">
<%= @content %>
</main>
</body>
</html>
```
Common assigns:
- `@content` — rendered page HTML.
- `@page` — current `%Astral.Content{}`.
- `@metadata` — decoded frontmatter map.
- `@route` — route path such as `/about/`.
- `@params` — string-keyed route params for dynamic file routes.
- `@site` — discovered `%Astral.Site{}`.
- `@collections` — content entries grouped by collection name.
- `@entry` — current collection entry, otherwise `nil`.
- `@routes` — generated routes.
## Heading anchors
Markdown headings get stable `id` attributes. Heading metadata is available as `@page.headings` for table-of-contents layouts:
```eex
<nav>
<%= for heading <- @page.headings do %>
<a href="#<%= heading.id %>"><%= heading.text %></a>
<% end %>
</nav>
```