Layouts
Layouts wrap pages with shared UI. They nest automatically based on the directory structure.
Creating a layout
Place a layout.tsx inside any directory under app/pages/. It wraps every page in that directory and all subdirectories. It receives a children prop that renders the current page.
app/pages/layout.tsxtypescript
import Nav from '../components/Nav'
import Footer from '../components/Footer'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<>
<Nav />
<main>{children}</main>
<Footer />
</>
)
}Nested layouts
Layouts compose automatically by directory depth. A page at blog/[slug].tsx gets wrapped by both the root layout and blog/layout.tsx:
app/pages/blog/layout.tsxtypescript
export default function BlogLayout({ children }: { children: React.ReactNode }) {
return (
<div className="blog-grid">
<aside>
<BlogSidebar />
</aside>
<section>{children}</section>
</div>
)
}The full render tree for /blog/hello-world is:
RootLayout ← app/pages/layout.tsx
└── BlogLayout ← app/pages/blog/layout.tsx
└── BlogPost ← app/pages/blog/[slug].tsxTitle templates
Layouts can compose page titles using useHtml(). Pass a function that receives the page's title and returns the final string:
app/pages/layout.tsxtypescript
import { useHtml } from 'nukejs'
export default function RootLayout({ children }: { children: React.ReactNode }) {
useHtml({ title: (prev) => `${prev} | Acme Corp` })
return <>{children}</>
}app/pages/about.tsxtypescript
import { useHtml } from 'nukejs'
export default function About() {
useHtml({ title: 'About Us' })
// Final <title>: "About Us | Acme Corp"
return <h1>About</h1>
}The page title is the base value. Layout functions wrap it outward, from innermost to outermost layout.
Layouts can also be asyncJust like pages, layouts can be async server components. Useful for fetching user session data, feature flags, or anything you need across all pages in a section.