Skip to content

PageLayout

Purpose

PageLayout combines PageHeader with AsyncContent for consistent page structure:

  • Page title and subtitle
  • Optional back button
  • Action buttons area
  • Automatic loading, error, and empty state handling

When to use

Use PageLayout for:

  • List pages with async data
  • Detail pages with header and content
  • Any page that needs consistent header + content structure

Basic usage

With async data

tsx
import { PageLayout } from '@/shared/ui/components/PageLayout'
import { FolderKanban, Plus } from 'lucide-react'

export function BoardsPage() {
  const { data: boards, isLoading, error } = useBoards()

  return (
    <PageLayout
      title="Boards"
      subtitle="Manage your kanban boards"
      actions={<Button onClick={handleCreate}><Plus /> New Board</Button>}
      isLoading={isLoading}
      error={error}
      data={boards}
      loadingFallback={<BoardsSkeleton />}
      errorConfig={{
        title: 'Failed to load boards',
        icon: FolderKanban,
        onRetry: refetch,
      }}
      emptyConfig={{
        title: 'No boards yet',
        description: 'Create your first board to get started',
        action: { label: 'Create Board', onClick: handleCreate, icon: Plus },
      }}
    >
      {(boards) => <BoardList boards={boards} />}
    </PageLayout>
  )
}

Static content (no async)

tsx
import { PageLayout } from '@/shared/ui/components/PageLayout'

export function SettingsPage() {
  return (
    <PageLayout
      title="Settings"
      subtitle="Manage your preferences"
      backButton={{ to: '/dashboard', label: 'Back' }}
    >
      <SettingsForm />
    </PageLayout>
  )
}

Props

Base props (always available)

PropTypeDescription
titlestring | ReactNodePage title
subtitlestring | ReactNodeOptional subtitle
backButton{ to?: string, label?: string, onClick?: () => void }Back navigation
actionsReactNodeAction buttons (right side)
classNamestringContainer class
contentClassNamestringContent area class
gap'none' | 'sm' | 'md' | 'lg'Gap between header and content

Async props (for data-driven pages)

PropTypeDescription
isLoadingbooleanLoading state
errorError | nullError object
dataTThe data to render
isEmptyboolean | ((data: T) => boolean)Custom empty check
loadingFallbackReactNodeLoading skeleton
errorConfigErrorConfigError state configuration
emptyConfigEmptyConfigEmpty state configuration
children(data: T) => ReactNodeRender function

ErrorConfig

PropertyTypeDescription
titlestringError title
descriptionstringError description
iconLucideIconCustom icon
onRetry() => voidRetry callback
retryTextstringRetry button text

EmptyConfig

PropertyTypeDescription
titlestringEmpty state title
descriptionstringEmpty state description
iconLucideIconCustom icon
action{ label, onClick, icon? }Primary action button

Notes

  • For async usage, children must be a render function
  • For static usage, children is rendered directly
  • The header is always visible, even during loading/error states
  • Uses AsyncContent internally for state handling