Skip to content

Site UI Configuration

Configure ArdoRoot, header, footer, edit links, last-updated metadata, and JSX sidebar sections from root.tsx.

Build-time options belong in ardo() config. Site chrome belongs in React. Define the header, left rail, sidebar panels, footer, edit links, and last-updated metadata in your app's root.tsx.

Minimal Root

Every Ardo app needs the generated config. Navigation is composed with React components.

import {
  ArdoGeneratedSidebar,
  ArdoHeader,
  ArdoNav,
  ArdoNavLink,
  ArdoRoot,
  ArdoRootLayout,
  ArdoSidebar,
  ArdoSidebarSection,
} from "ardo/ui"
import config from "virtual:ardo/config"
import "ardo/ui/styles.css"

export function Layout({ children }: { children: React.ReactNode }) {
  return <ArdoRootLayout>{children}</ArdoRootLayout>
}

export default function Root() {
  return (
    <ArdoRoot config={config}>
      <ArdoHeader>
        <ArdoNav>
          <ArdoNavLink to="/guide/getting-started">Guide</ArdoNavLink>
        </ArdoNav>
      </ArdoHeader>

      <ArdoSidebar>
        <ArdoSidebarSection id="guide" label="Guide" to="/guide/getting-started">
          <ArdoGeneratedSidebar section="guide" />
        </ArdoSidebarSection>
      </ArdoSidebar>
    </ArdoRoot>
  )
}

ArdoRootLayout owns the document-level theme bootstrap. ArdoRoot owns the React Router outlet, Ardo providers, and the shell slots supplied as JSX children.

RootLayout Theme Policy

By default, ArdoRootLayout uses theme="auto": it applies a stored ardo-theme preference first and falls back to prefers-color-scheme. Set an explicit theme policy when a site should not be user-selectable:

export function Layout({ children }: { children: React.ReactNode }) {
  return <ArdoRootLayout theme="light">{children}</ArdoRootLayout>
}
Theme policyBehavior
autoStored user preference wins; system preference is the fallback. Default.
lightAlways render light mode and clear stale stored theme preferences.
darkAlways render dark mode and clear stale theme preferences.
systemAlways follow prefers-color-scheme and clear stored preferences.

ArdoRoot

ArdoRoot accepts site-wide content settings as props and shell components as children.

PropTypePurpose
configArdoConfigGenerated site config from virtual:ardo/config.
editLink{ pattern: string; text?: string }Site-wide edit-link pattern for Markdown pages.
lastUpdated{ enabled?: boolean; text?: string; formatOptions?: Intl.DateTimeFormatOptions }Site-wide last-updated display settings.
labelsArdoLabelsInputOverride default UI chrome strings.
tocLabelstringLabel for the table-of-contents panel.
classNamestringReplace the default layout class.
childrenReactNodeArdoHeader, ArdoSidebar, ArdoFooter, and optional content overrides.

Use ArdoHeader for the default responsive header. Put ArdoNav links directly inside it. Use ArdoHeaderActions for right-side actions.

import { ArdoHeader, ArdoHeaderActions, ArdoNav, ArdoNavLink, ArdoSocialLink } from "ardo/ui"
import logo from "./assets/logo.svg"
;<ArdoHeader logo={logo} title="My Docs" searchPlaceholder="Search documentation...">
  <ArdoNav>
    <ArdoNavLink to="/guide/getting-started">Guide</ArdoNavLink>
    <ArdoNavLink to="/api-reference">API</ArdoNavLink>
  </ArdoNav>
  <ArdoHeaderActions>
    <ArdoSocialLink href="https://github.com/example/project" icon="github" />
  </ArdoHeaderActions>
</ArdoHeader>

If brand.logo is configured in ardo(), ArdoRoot passes it to the default header unless the header sets its own logo.

Header propPurpose
logoString URL or { light, dark } logo variants.
titleHeader title. Defaults to config.title.
searchSet to false to hide search. Defaults to true.
searchPlaceholderPlaceholder for the search input.
themeToggleSet to false to hide the theme toggle. Defaults to true.
classNameReplace the default header class.

The left navigation is always a rail plus a panel. Define both with ArdoSidebarSection: the section props create the rail item, and the section children create the panel.

import { ArdoGeneratedSidebar, ArdoSidebar, ArdoSidebarSection } from "ardo/ui"
import { BookOpen, Code2 } from "lucide-react"
;<ArdoSidebar>
  <ArdoSidebarSection
    id="guide"
    label="Guide"
    to="/guide/getting-started"
    icon={<BookOpen size={18} />}
  >
    <ArdoGeneratedSidebar section="guide" />
  </ArdoSidebarSection>

  <ArdoSidebarSection id="api-reference" label="API" to="/api-reference" icon={<Code2 size={18} />}>
    <ArdoGeneratedSidebar section="api-reference" />
  </ArdoSidebarSection>
</ArdoSidebar>

ArdoGeneratedSidebar renders the Markdown and TypeDoc navigation generated from route files for the given top-level section. For fully manual navigation, compose links and groups directly:

<ArdoSidebarSection id="guide" label="Guide" to="/guide/getting-started">
  <ArdoSidebarGroup title="Guide">
    <ArdoSidebarLink to="/guide/getting-started">Getting Started</ArdoSidebarLink>
    <ArdoSidebarLink to="/guide/configuration">Configuration</ArdoSidebarLink>
  </ArdoSidebarGroup>
</ArdoSidebarSection>

Pass ArdoFooter as a child of ArdoRoot.

<ArdoFooter
  sponsor={{ text: "Example Co", link: "https://example.com" }}
  message="Released under the MIT License."
  copyright="Copyright 2026 Example Co"
/>

editLink and lastUpdated are site-wide content settings. Ardo applies them to Markdown and MDX document pages, and page frontmatter can opt out with editLink: false or lastUpdated: false.

<ArdoRoot
  config={config}
  editLink={{
    pattern: "https://github.com/example/project/edit/main/docs/app/routes/:path",
    text: "Edit this page on GitHub",
  }}
  lastUpdated={{
    enabled: true,
    text: "Last updated",
    formatOptions: { dateStyle: "medium" },
  }}
  tocLabel="On this page"
>
  {/* Header, sidebar, and footer here */}
</ArdoRoot>

The :path placeholder receives the route file path relative to the generated Markdown route root.

Route-Level Chrome

For route-level chrome suppression, export a handle from the route:

app/routes/landing.tsx
export const handle = { layout: "bare", chrome: false }

chrome: false removes header and footer for that route. layout: "bare" removes sidebar and rail rendering.