Layouting

Wrapping pages in layouts

Every route in app/pages can sit inside layers of shared markup — a site-wide shell, a section-wide frame, or nothing at all — without repeating that markup in every page.

The three files

  • app/pages/index.htmx — the outer HTML document (<html>, <head>, styles, htmx.js). It wraps <hx-root/> and only exists once, at the root.
  • layout.htmx — wraps every page under its folder (and every nested folder) in place of anything above it, via <hx-content/>.
  • partialLayout.htmx — the same idea, but nests inside the root layout.htmx instead of replacing it.

layout.htmx vs. partialLayout.htmx

A route picks up the nearest one of these two files walking up from its own folder to app/pages — layout.htmx wins over partialLayout.htmx at the same level. The difference is what happens above that point:

  • layout.htmx opts a folder out of the site-wide layout entirely — its own output becomes the page body.
  • partialLayout.htmx still gets wrapped by the root app/pages/layout.htmx afterwards, so the header and footer stay.

This page is a good example: app/pages/docs/components/partialLayout.htmx adds the docs sidebar around every route under /docs/components/*, while the site header and footer from the root layout are still there.

<!-- app/pages/docs/components/partialLayout.htmx -->
<import src="@/components/docs-sidebar" as="docs-sidebar"/>
<div class="mx-auto flex max-w-5xl gap-12 px-6 py-16">
  <aside class="hidden w-56 shrink-0 md:block">
    <docs-sidebar menu-items=menuItems />
  </aside>
  <div class="min-w-0 flex-1">
    <hx-content />
  </div>
</div>

Slots

Both layout kinds use <hx-content/> as the slot for the wrapped page. The root shell uses <hx-root/> the same way, one level up, for whatever the layout chain produced.