Frappe UIFrappe UI

PageHeader

App-shell headers: a desktop PageHeader, a mobile PageHeaderMobile, and the PageHeaderBase primitive they share. A page declares its header; it renders into the layout's <PageHeaderTarget />.

vue
<script setup lang="ts">
import { Breadcrumbs, Button, PageHeader } from 'frappe-ui'
</script>

<template>
  <div
    class="w-full overflow-hidden border-t border-l border-r bg-surface-base"
  >
    <PageHeader>
      <Breadcrumbs
        :items="[
          { label: 'Spaces', route: '#' },
          { label: 'Engineering', route: '#' },
        ]"
      />
      <Button label="New discussion" variant="solid" />
    </PageHeader>
  </div>
</template>

A single click on the header's empty area scrolls the page to the top. Interactive elements are ignored — links, buttons, form controls, and anything with role="button". Opt a non-interactive element out with data-no-scroll-top:

vue
<PageHeader>
  <Breadcrumbs data-no-scroll-top :items="items" />
</PageHeader>

PageHeaderMobile

Keeps its title centered regardless of the #prefix / #suffix control widths, on a single line with a trailing ellipsis. The header is 52px tall, fixed. There is no CSS variable for the height in v1; a taller strip is PageHeaderBase with your own class.

PageHeaderBackButton goes back through history, the way the browser's own back button does. It only navigates to fallbackRoute when there is no in-app history to go back to, so give it a fallback that makes the page recoverable on a cold load.

A discussion title that is long enough to need centering

vue
<script setup lang="ts">
import { Button, PageHeaderMobile, PageHeaderMobileTitle } from 'frappe-ui'
</script>

<template>
  <div class="w-80 overflow-hidden border border-b-0 bg-surface-base">
    <PageHeaderMobile>
      <template #prefix>
        <Button
          variant="ghost"
          size="md"
          icon="lucide-chevron-left"
          label="Back"
        />
      </template>
      <PageHeaderMobileTitle
        title="A discussion title that is long enough to need centering"
      />
      <template #suffix>
        <Button variant="ghost" icon="lucide-more-horizontal" label="Options" />
      </template>
    </PageHeaderMobile>
  </div>
</template>

PageHeaderBase

The unstyled <header> primitive behind both. Use it directly for a custom strip — a toolbar or a second row — that shares the same target.

API Reference

PageHeader

default

The header's contents — title, breadcrumbs, actions.

PageHeaderBase

default

The header's contents.

PageHeaderTitle

Show types
typescript
import type { RouteDestination } from '../shared/route'

export interface PageHeaderTitleProps {
  /** Title text. Overridden by the default slot. */
  title?: string
}

export interface PageHeaderMobileProps {
  /** Title shown centered in the header. Overridden by the default slot. */
  title?: string
}

export interface PageHeaderMobileTitleProps {
  /** Title text. Overridden by the default slot. */
  title?: string
}

export interface PageHeaderBackButtonProps {
  /**
   * Fallback destination, used only when there is no in-app history to go back to
   * (a cold load onto a deep link). Taps otherwise go back through history.
   */
  fallbackRoute?: RouteDestination

  /** Accessible label for the button. */
  label?: string
}
title
string

Title text. Overridden by the default slot.

default

Overrides `title` with custom markup.

PageHeaderMobile

Show types
typescript
import type { RouteDestination } from '../shared/route'

export interface PageHeaderTitleProps {
  /** Title text. Overridden by the default slot. */
  title?: string
}

export interface PageHeaderMobileProps {
  /** Title shown centered in the header. Overridden by the default slot. */
  title?: string
}

export interface PageHeaderMobileTitleProps {
  /** Title text. Overridden by the default slot. */
  title?: string
}

export interface PageHeaderBackButtonProps {
  /**
   * Fallback destination, used only when there is no in-app history to go back to
   * (a cold load onto a deep link). Taps otherwise go back through history.
   */
  fallbackRoute?: RouteDestination

  /** Accessible label for the button. */
  label?: string
}
title
string

Title shown centered in the header. Overridden by the default slot.

prefix

A control leading the centered title — usually a `PageHeaderBackButton`.

default

The centered title. Overrides `title`; usually a `PageHeaderMobileTitle`.

suffix

A control trailing the centered title — usually an action button.

PageHeaderMobileTitle

Show types
typescript
import type { RouteDestination } from '../shared/route'

export interface PageHeaderTitleProps {
  /** Title text. Overridden by the default slot. */
  title?: string
}

export interface PageHeaderMobileProps {
  /** Title shown centered in the header. Overridden by the default slot. */
  title?: string
}

export interface PageHeaderMobileTitleProps {
  /** Title text. Overridden by the default slot. */
  title?: string
}

export interface PageHeaderBackButtonProps {
  /**
   * Fallback destination, used only when there is no in-app history to go back to
   * (a cold load onto a deep link). Taps otherwise go back through history.
   */
  fallbackRoute?: RouteDestination

  /** Accessible label for the button. */
  label?: string
}
title
string

Title text. Overridden by the default slot.

prefix

A leading glyph next to the title text — keeps its natural size while the text truncates.

default

Overrides `title` with custom markup.

PageHeaderBackButton

Show types
typescript
import type { RouteDestination } from '../shared/route'

export interface PageHeaderTitleProps {
  /** Title text. Overridden by the default slot. */
  title?: string
}

export interface PageHeaderMobileProps {
  /** Title shown centered in the header. Overridden by the default slot. */
  title?: string
}

export interface PageHeaderMobileTitleProps {
  /** Title text. Overridden by the default slot. */
  title?: string
}

export interface PageHeaderBackButtonProps {
  /**
   * Fallback destination, used only when there is no in-app history to go back to
   * (a cold load onto a deep link). Taps otherwise go back through history.
   */
  fallbackRoute?: RouteDestination

  /** Accessible label for the button. */
  label?: string
}
fallbackRoute
RouteDestination

Fallback destination, used only when there is no in-app history to go back to (a cold load onto a deep link). Taps otherwise go back through history.

label
= "Back"
string

Accessible label for the button.