Layouts

Layouts in Sapling provide a way to create consistent page structures and share common elements across multiple pages.

Basic Layout

A basic layout in Sapling is a TypeScript function that wraps your page content. Here's a simple example:

import { Layout as SaplingLayout, html, type LayoutProps } from "@sapling/sapling";

export type BaseLayoutProps = LayoutProps & {
  title?: string;
  description?: string;
};

export default async function Layout(props: BaseLayoutProps) {
  return await SaplingLayout({
    head: html`
      <title>${props.title}</title>
      <meta name="description" content="${props.description}" />
    `,
    children: html`
      <div>
        ${props.children}
      </div>
    `
  });
}

Layout Properties

Layouts accept properties that extend Sapling's base LayoutProps:

type LayoutProps = {
  head?: unknown;        // Additional head content
  bodyClass?: string;    // Classes to apply to the body
  children?: unknown;    // Page content
  unoConfig?: unknown;   // UnoCSS configuration
};

Using Components in Layouts

Layouts can import and use components to create consistent page structures:

import { Nav } from "../components/Nav.ts";
import { Footer } from "../components/Footer.ts";

export default async function Layout(props: BaseLayoutProps) {
  return await SaplingLayout({
    // ... other props
    children: html`
      <div class="relative z-10">
        ${Nav()}
        ${props.children}
        ${Footer()}
      </div>
    `
  });
}

Styling with UnoCSS

Sapling layouts can include UnoCSS configuration for consistent styling:

import { config } from "../uno.config.ts";

export default async function Layout(props: BaseLayoutProps) {
  return await SaplingLayout({
    unoConfig: config,
    bodyClass: `font-sans @dark:bg-black @dark:text-white ${props.bodyClass ?? ''}`,
    // ... other props
  });
}

Nested Layouts

You can create specialized layouts that extend your base layout:

import BaseLayout from "./Layout.ts";

export default async function DocsLayout(props: DocsLayoutProps) {
  return await BaseLayout({
    ...props,
    children: html`
      <div class="docs-container">
        <aside>${props.leftNav}</aside>
        <main>${props.children}</main>
        <aside>${props.tableOfContents}</aside>
      </div>
    `
  });
}

Using Layouts in Pages

To use a layout in a page component:

import Layout from "../layouts/Layout.ts";

export default async function HomePage() {
  return await Layout({
    title: "Home - My Site",
    description: "Welcome to my website",
    children: html`
      <main class="p-4">
        <h1>Welcome!</h1>
      </main>
    `
  });
}

Best Practices

  1. Type Safety: Define proper TypeScript interfaces for your layout props
  2. Modularity: Break down layouts into reusable components
  3. Flexibility: Make layouts adaptable with optional props
  4. Dark Mode: Include dark mode support using the @dark: prefix
  5. Performance: Keep layouts lightweight and avoid unnecessary nesting