Layout Resolution
Layout resolution is the process of converting a layout reference (like @docs/doc_style1) into an actual Astro component file path.
Resolution Flow
site.yaml config Resolved component path
───────────────── ─────────────────────────
@docs/doc_style1 ────▶ src/layouts/docs/styles/doc_style1/Layout.astro
@blog/blog_style1 ────▶ src/layouts/blogs/styles/blog_style1/*.astro
@custom/home ────▶ src/layouts/custom/styles/home/Layout.astro
Path Pattern
All layout types follow a standardized path pattern:
src/layouts/{type}/styles/{style}/Layout.astro
| Component | Description | Examples |
|---|---|---|
{type} |
Layout category | docs, blogs, custom |
{style} |
Style variant | doc_style1, blog_style1, home |
Resolution in [...slug].astro
The dynamic route handler resolves layouts using glob imports:
// Auto-discover all available layouts
const docsLayouts = import.meta.glob(
'/src/layouts/docs/styles/*/Layout.astro'
);
const blogIndexLayouts = import.meta.glob(
'/src/layouts/blogs/styles/*/IndexLayout.astro'
);
const blogPostLayouts = import.meta.glob(
'/src/layouts/blogs/styles/*/PostLayout.astro'
);
const customLayouts = import.meta.glob(
'/src/layouts/custom/styles/*/Layout.astro'
);
Resolution Function
function resolveLayout(layoutRef: string, pageType: string) {
// Parse the reference: "@docs/doc_style1"
const match = layoutRef.match(/^@(\w+)\/(\w+)$/);
if (!match) {
throw new Error(`Invalid layout reference: ${layoutRef}`);
}
const [, type, style] = match;
// Map type to layout collection
const layoutMap = {
docs: docsLayouts,
blog: blogIndexLayouts, // or blogPostLayouts based on context
custom: customLayouts,
};
const layouts = layoutMap[type];
if (!layouts) {
throw new Error(`Unknown layout type: ${type}`);
}
// Build expected path
const expectedPath = `/src/layouts/${type}/styles/${style}/Layout.astro`;
// Check if layout exists
if (!layouts[expectedPath]) {
const available = Object.keys(layouts)
.map(p => p.match(/styles\/(\w+)\//)?.[1])
.filter(Boolean);
throw new Error(
`Layout "${style}" not found for type "${type}". ` +
`Available: ${available.join(', ')}`
);
}
return layouts[expectedPath];
}
Blog Layout Special Case
Blog pages have two layouts (index and post), resolved based on context:
// In [...slug].astro getStaticPaths()
if (pageConfig.type === 'blog') {
const posts = await loadContent(dataPath, 'blog');
// Index page uses IndexLayout
paths.push({
params: { slug: baseUrl },
props: {
layout: resolveLayout(pageConfig.layout, 'blog-index'),
// ...
}
});
// Each post uses PostLayout
for (const post of posts) {
paths.push({
params: { slug: `${baseUrl}/${post.slug}` },
props: {
layout: resolveLayout(pageConfig.layout, 'blog-post'),
// ...
}
});
}
}
Alias Resolution
The @ prefix is resolved by the alias system:
// src/loaders/alias.ts
export function getLayoutType(layoutRef: string): string | null {
const match = layoutRef.match(/^@(\w+)\//);
return match ? match[1] : null;
}
// Mapping
'@docs/doc_style1' → type: 'docs', style: 'doc_style1'
'@blog/blog_style1' → type: 'blog', style: 'blog_style1'
'@custom/home' → type: 'custom', style: 'home'
Error Handling
The system provides descriptive errors at build time:
Invalid Format
[LAYOUT ERROR] Invalid layout reference format.
Value: "docs/style1"
Expected: "@type/style" (e.g., "@docs/doc_style1")
Unknown Type
[LAYOUT ERROR] Unknown layout type "pages".
Valid types: docs, blog, custom
Missing Layout
[LAYOUT ERROR] Docs layout "doc_style99" does not exist.
Page: docs
Config: @docs/doc_style99
Expected: src/layouts/docs/styles/doc_style99/Layout.astro
Available: doc_style1, doc_style2
Why Glob Imports?
Using import.meta.glob() provides:
- Automatic Discovery - New layouts are found without registry updates
- Build-Time Validation - Missing layouts fail at build, not runtime
- Code Splitting - Only used layouts are bundled
- Type Safety - Glob patterns are validated by Vite
Adding a New Layout
To add a layout that's automatically discovered:
- Create folder:
src/layouts/docs/styles/doc_style3/ - Create file:
Layout.astro - Reference in config:
layout: "@docs/doc_style3"
No code changes required in the resolution system.
Full Resolution Example
# site.yaml
pages:
docs:
base_url: "/docs"
type: docs
layout: "@docs/doc_style1"
data: "@data/docs"
Resolution steps:
1. Read config: layout: "@docs/doc_style1"
│
2. Parse reference: type: "docs", style: "doc_style1"
│
3. Build path: /src/layouts/docs/styles/doc_style1/Layout.astro
│
4. Glob lookup: docsLayouts[path] → Component
│
5. Validate: Component exists? ✓
│
6. Import: const Layout = await docsLayouts[path]()
│
7. Render: <Layout {...props} />