Layout Areas

Layout areas render on every page. They provide site-wide navigation, branding, and persistent UI elements like sidebars.

Overview

Layout areas live in the layout/ directory, parallel to pages/:

site/
├── layout/
│   ├── header.md             # Renders at top of every page
│   ├── footer.md             # Renders at bottom of every page
│   ├── left.md               # Left sidebar (if layout supports)
│   └── right.md              # Right sidebar (if layout supports)
├── pages/
│   └── home/                 # Regular page
│       └── hero.md

Unlike regular pages, layout areas:

  • Don't create navigable routes
  • Render on all pages automatically
  • Can be hidden per-page via layout.hide

The conventional area names are header, footer, left, and right. These are promoted by templates and documentation, but foundations can define any area names they need — topbar, sidebar, statusbar, or anything else. See Custom Layouts for details.

How the layout/ folder is read

The folder's structure alone decides what each entry is. It never depends on which foundation the site uses:

PathIs
layout/<area>.mdAn area of the default layout, with one section
layout/<name>/A named layout — every folder directly under layout/
layout/<name>/<area>.mdAn area of that layout, with one section
layout/<name>/<area>/An area with several sections, rendered in filename order
layout/default/<area>/An area of the default layout with several sections

An area is a group of sections, not a page: it has no page.yml. Order its sections with numeric filename prefixes, and let each section declare its own data with query: or fetch:.


How They Work

Rendering Order

┌─────────────────────────────────────┐
│           header                    │
├──────────┬─────────────┬────────────┤
│          │             │            │
│  left    │   Page      │  right     │
│          │   Content   │            │
│          │             │            │
├──────────┴─────────────┴────────────┤
│           footer                    │
└─────────────────────────────────────┘

The foundation's Layout component controls where areas appear:

// src/layouts/docs/index.jsx — the layout named `docs`
export default function DocsLayout({ header, footer, left, right, body }) {
  return (
    <div className="layout">
      {header}
      <div className="main">
        {left && <aside className="left">{left}</aside>}
        <main>{body}</main>
        {right && <aside className="right">{right}</aside>}
      </div>
      {footer}
    </div>
  )
}

Content Flow

  1. Site build collects layout areas from layout/
  2. Runtime loads them alongside the active page
  3. Foundation Layout receives them as props
  4. Each page's layout.hide controls which areas are suppressed

Area Content

Renders at the top of every page, typically containing:

  • Logo and site name
  • Main navigation
  • Search button
  • Language switcher
  • Dark mode toggle
<!-- layout/header.md -->
---
type: Header
sticky: true
---

![Logo](/logo.svg){role=icon}

- [Home](/)
- [Products](/products)
- [About](/about)
- [Contact](/contact)

Renders at the bottom of every page, typically containing:

  • Site links organized by category
  • Legal links (privacy, terms)
  • Social media links
  • Copyright notice
<!-- layout/footer.md -->
---
type: Footer
---

## Company

- [About](/about)
- [Careers](/careers)
- [Contact](/contact)

## Legal

- [Privacy](/privacy)
- [Terms](/terms)

---

© 2025 Acme Corp. All rights reserved.

left / right

Side areas for documentation sites, dashboards, or complex layouts:

<!-- layout/left.md -->
---
type: Sidebar
---

```yaml:nav
- label: Getting Started
  children:
    - label: Installation
      href: /docs/install
    - label: Quick Start
      href: /docs/quickstart
- label: Guides
  children:
    - label: Components
      href: /docs/components

---

## Multiple Sections

An area with several sections is a folder inside a layout's folder. For the default layout, that folder is `layout/default/`:

layout/default/header/ ├── 1-topbar.md # Announcement bar └── 2-navbar.md # Main navigation


The sections render in filename order, giving you flexibility for complex headers:

```jsx
// 1-topbar.md
---
type: TopBar
---
New feature released! [Learn more](/blog/new-feature)

// 2-navbar.md
---
type: Navbar
sticky: true
---
<!-- Main navigation -->

Note: a folder directly under layout/ is always a named layout. layout/header/ is the layout named header, not the default layout's header area — the build warns when a layout folder is named like a common area. Write layout/header.md for a one-section header, or layout/default/header/ for several sections.

An area folder holds only its section files: no page.yml, no @-prefixed child sections, and no folders of markdown inside it. The build stops with a message naming the file if it finds one.


Per-Page Layout Control

Pages can hide areas via layout.hide in page.yml:

# pages/landing/page.yml
title: Landing Page
layout:
  hide: [header, footer]

This is useful for:

  • Full-screen landing pages
  • Print layouts
  • Embedded content
  • Admin interfaces

You can also select a different layout and hide specific areas:

# pages/reference/page.yml
title: Quick Reference
layout:
  name: docs
  hide: [left, right]

Named Layout Areas

When a foundation provides multiple layouts, each layout's area content lives in a folder of layout/ named after the layout:

site/layout/
├── header.md            ← default layout areas
├── footer.md
├── left.md
├── marketing/           ← areas for the `marketing` layout
│   ├── header.md
│   └── footer.md
└── dashboard/           ← areas for the `dashboard` layout
    ├── topbar.md
    ├── sidebar/         ← an area with several sections
    │   ├── 1-nav.md
    │   └── 2-filters.md
    └── statusbar.md

The folder name is the layout name, matched regardless of case and with or without a trailing Layout: layout/marketing/ holds the areas of pages whose layout is marketing, Marketing or the foundation's MarketingLayout. A named layout's areas are its own — a page on the marketing layout gets no left area from the default layout's left.md. See Custom Layouts for the full guide.


Layout areas often build navigation from the page hierarchy. Pages can opt out of nav while staying reachable:

# pages/admin/page.yml
title: Admin Dashboard
hideIn: ['*']          # Reachable by URL, but shown in no nav menu
# pages/legal/page.yml
title: Privacy Policy
hideIn: [header]       # Hide from the header nav (still shown in footer)

hideIn is nav-only — the page stays published and routed. To exclude a page (and its subtree) from the published site entirely — e.g. a draft — use hidden: true instead.

See Page Configuration for all visibility options.


Component Patterns

Header with Automatic Navigation

// src/sections/Header/index.jsx
import { useWebsite } from '@uniweb/kit'

export default function Header({ content }) {
  const { website } = useWebsite()

  // Manual nav from content (if provided)
  const manualNav = content.data?.nav

  // Automatic nav from page structure
  const autoNav = website.getPageHierarchy({ for: 'header' })

  const navItems = manualNav || autoNav.map(p => ({
    label: p.label || p.title,
    href: p.route,
    children: p.children?.map(c => ({
      label: c.label || c.title,
      href: c.route
    }))
  }))

  return (
    <header>
      <nav>
        {navItems.map(item => (
          <NavItem key={item.href} {...item} />
        ))}
      </nav>
    </header>
  )
}
// src/sections/Footer/index.jsx
import { useWebsite, Link, getLocaleLabel } from '@uniweb/kit'

export default function Footer({ content }) {
  const { website } = useWebsite()

  return (
    <footer>
      {/* Footer content */}

      {website.hasMultipleLocales() && (
        <div className="locale-switcher">
          {website.getLocales().map(locale => (
            // `reload` is required: getLocaleUrl() omits the deployment base
            // path, and switching language needs a full page load.
            <Link
              reload
              key={locale.code}
              href={website.getLocaleUrl(locale.code)}
              className={locale.code === website.getActiveLocale() ? 'active' : ''}
            >
              {getLocaleLabel(locale)}
            </Link>
          ))}
        </div>
      )}

      <p>{content.paragraphs[0]}</p>
    </footer>
  )
}
// src/sections/Sidebar/index.jsx
import { useVersion } from '@uniweb/kit'

export default function Sidebar({ content }) {
  const { isVersioned, currentVersion, isDeprecatedVersion } = useVersion()

  return (
    <aside>
      {isVersioned && (
        <div className="version-badge">
          {currentVersion?.label}
          {isDeprecatedVersion && <span className="deprecated">Legacy</span>}
        </div>
      )}

      <nav>
        {/* Sidebar navigation */}
      </nav>
    </aside>
  )
}

Sticky Headers

For headers that stick to the top while scrolling:

---
type: Header
sticky: true
---

The foundation component handles the sticky behavior:

export default function Header({ content, params }) {
  const { sticky = false } = params

  return (
    <header className={sticky ? 'sticky top-0 z-50' : ''}>
      {/* header content */}
    </header>
  )
}

And define the param in meta.js:

export default {
  title: 'Header',
  params: {
    sticky: {
      type: 'boolean',
      label: 'Sticky Header',
      default: false
    }
  }
}

Best Practices

  1. Keep content minimal: Layout areas render on every page — keep them lightweight

  2. Support both modes: Allow both automatic (from page hierarchy) and manual (from content) navigation

  3. Handle empty states: Layout areas might not exist in all sites — check props before rendering

  4. Consider mobile: Layout areas often need responsive behavior (hamburger menus, collapsible sidebars)

  5. Version awareness: In docs sites, show version context in headers/sidebars


See Also