Layouts
How Avalon's layout system works, including nested layouts and skipLayouts.
What is a layout?
A layout is a wrapper component that wraps one or more pages. Layouts live in src/layouts/ and are automatically applied based on directory structure.
Root layout
src/layouts/_layout.tsx wraps every page in your app:
import type { LayoutProps } from '@useavalon/avalon';
export default function RootLayout({ children, frontmatter }: LayoutProps) {
return (
<html lang="en">
<head>
<title>{frontmatter?.title ?? 'My Site'}</title>
</head>
<body>
<header>...</header>
<main>{children}</main>
</body>
</html>
);
}
Nested layouts
Layouts nest based on directory structure. A layout at src/layouts/blog/_layout.tsx wraps all pages under src/pages/blog/:
src/layouts/
_layout.tsx ← wraps everything
blog/
_layout.tsx ← wraps src/pages/blog/* (nested inside root)
docs/
_layout.tsx ← wraps src/pages/docs/* (nested inside root)
The nesting is automatic — the blog layout renders inside the root layout's {children}.
Skipping layouts
To bypass the root layout for a specific page, export a layoutConfig object:
export const layoutConfig = {
skipLayouts: ['_layout'],
};
export default function LandingPage() {
return (
<html>
{/* Full custom HTML — no layout wrapper */}
</html>
);
}
This is useful for landing pages that need complete control over the HTML structure, including their own <html>, <head>, and navigation.
Frontmatter
Layouts receive a frontmatter prop containing metadata from the page. For MDX pages, this comes from the YAML front matter block:
---
title: My Page
description: A description for SEO
currentPath: /docs/my-page
---
For .tsx pages, export a frontmatter object:
export const frontmatter = {
title: 'My Page',
description: 'A description for SEO',
};
Layout props type
import type { LayoutProps } from '@useavalon/avalon';
export default function MyLayout({ children, frontmatter }: LayoutProps) {
// frontmatter is Record<string, unknown>
}