Layouts
A _layout.tsx file wraps every route in its directory and all subdirectories. Layouts are the right place for the HTML shell, navigation, sidebars, headers, footers, and any CSS that should apply globally or section-wide.
Defining a layout
Create _layout.tsx and export a handler via defineLayout. The handler receives children (the rendered page content) and ctx (the full request context):
// src/routes/_layout.tsx
import { defineLayout } from 'davaux'
import type { LayoutProps } from 'davaux'
export default defineLayout(({ children, ctx }: LayoutProps) => {
return (
<html lang="en">
<head>
<meta charset="utf-8" />
<title>{ctx.head.title ?? 'My App'}</title>
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
<main>{children}</main>
</body>
</html>
)
})
children is typed as Promise<string> — the inner page HTML, already rendered. The JSX runtime awaits it and injects it without HTML-escaping, so {children} works directly. In template literal layouts (returning a string rather than JSX), use const content = await children explicitly.
Nesting layouts
You can have layouts at any directory level. Davaux applies them from outermost (closest to the root) to innermost (closest to the route file):
src/routes/
├── _layout.tsx ← root layout (HTML shell, global nav)
├── index.page.tsx
└── dashboard/
├── _layout.tsx ← dashboard layout (sidebar)
└── settings.page.tsx
When a request hits /dashboard/settings, Davaux:
- Renders
settings.page.tsx - Wraps it with
dashboard/_layout.tsx - Wraps that with the root
_layout.tsx
Each layout receives the output of the inner layout (or page) as children.
Using ctx.head in a layout
Page handlers set values on ctx.head before the layout runs. Read them in your layout to produce proper <head> tags:
export default defineLayout(({ children, ctx }: LayoutProps) => {
return (
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{ctx.head.title ?? 'My App'}</title>
{ctx.head.description && (
<meta name="description" content={ctx.head.description} />
)}
{Object.entries(ctx.head.meta ?? {}).map(([name, content]) => (
<meta name={name} content={content} />
))}
{ctx.head.stylesheets?.map((href) => (
<link rel="stylesheet" href={href} />
))}
</head>
<body>
{children}
{ctx.head.scripts?.map((src) => (
<script src={src} defer />
))}
</body>
</html>
)
})
CSS imports
Import .css files as side effects anywhere in your routes, layouts, or islands. Davaux collects them all and bundles them into a single /_davaux/styles.css file served automatically — no configuration needed.
// src/routes/about.page.tsx
import './about.css'
import { definePage } from 'davaux'
export default definePage((ctx) => <main>...</main>)
// src/islands/Counter.tsx
import './Counter.css'
import { createSignal } from 'davaux/client'
export default function Counter() { ... }
// src/routes/_layout.tsx
import './layout.css'
import { defineLayout } from 'davaux'
export default defineLayout(({ children, ctx }) => {
return <html>...</html>
})
The <link> tag pointing to the compiled stylesheet is injected into the page automatically, before any island scripts.
Nested layout CSS composes correctly — subdirectory layouts can import section-specific styles without affecting the root layout.
TypeScript
To suppress TypeScript errors on CSS imports, add a src/env.d.ts file:
// src/env.d.ts
declare module '*.css'
Full HTML shell example
A complete root layout suitable for a typical application:
// src/routes/_layout.tsx
import { defineLayout, createLink } from 'davaux'
import type { LayoutProps } from 'davaux'
import './app.css'
export default defineLayout(({ children, ctx }: LayoutProps) => {
const { Link } = createLink(ctx)
const year = new Date().getFullYear()
return (
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
<title>{ctx.head.title ? `${ctx.head.title} — My App` : 'My App'}</title>
{ctx.head.description && (
<meta name="description" content={ctx.head.description} />
)}
{ctx.head.stylesheets?.map((href) => (
<link rel="stylesheet" href={href} />
))}
</head>
<body>
<header class="site-header">
<Link href="/" class="logo">My App</Link>
<nav>
<Link href="/docs" activeClass="active">Docs</Link>
<Link href="/blog" activeClass="active">Blog</Link>
<Link href="/login">Sign in</Link>
</nav>
</header>
<div class="page-content">
{children}
</div>
<footer>
<p>© {year} My App. All rights reserved.</p>
</footer>
{ctx.head.scripts?.map((src) => (
<script src={src} defer />
))}
</body>
</html>
)
})