Layout

Layout

Breadcrumbs, navigation buttons, form sections, and confirmation dialogs.

The layout package provides workflow chrome around feature screens: route-derived wayfinding, back navigation, form sections, empty states, utility actions, and a promise-based confirmation dialog.

It does not own routing, data fetching, or business rules. The host application supplies those concerns.

When to use it

ComponentUse when
BreadcrumbsHierarchical routes with a shared trail
GoBackButtonDetail pages that should return to a list or parent
FormSection / FormLayoutGroup fields with title, description, and spacing
EmptyState / ContentEmptyZero-data placeholders with optional actions
ConfirmationDialogProviderAny destructive or irreversible action
UtilityIconButtonDocs, refresh, and info affordances in toolbars
DashboardShellFull app chrome: sidebar, header, breadcrumbs, account menu

Installation

Registry CLI

Requires a one-time components.json registry entry for @feature-kit/layout.

pnpm dlx shadcn@latest add https://feature-kits.vercel.app/r/layout.json

Namespaced: npx shadcn@latest add @feature-kit/layout

Manual installation

Monorepo consumers can depend on workspace:* packages instead of the registry CLI.

pnpm add layout ui

Dependencies

// apps/your-app/package.json
"dependencies": {
    "layout": "workspace:*"
    "ui": "workspace:*"
}

Transpile (Next.js)

// next.config.ts
transpilePackages: ["layout", "ui"]

Import

import { FormSection, EmptyState, GoBackButton } from "@/lib/feature-kit/layout"
import { ConfirmationDialogProvider, useConfirmationDialog } from "@/lib/feature-kit/layout/confirmation-dialog"

Register ConfirmationDialogProvider once near the root (before any useConfirmationDialog call):

// apps/web/src/app/providers.tsx
<ConfirmationDialogProvider>
  {children}
</ConfirmationDialogProvider>

Mental model

route → Breadcrumbs / GoBackButton
screen structure → FormSection / FormLayout
application states → EmptyState
irreversible action → ConfirmationDialogProvider
toolbar affordances → UtilityIconButton

Use the package when a screen needs consistent workflow behavior around a form, table, or import flow. Use lower-level ui primitives when you need a one-off visual element.

Usage

Dashboard shell

Overview

Sidebar, in-flow header, and route-derived breadcrumbs.

Replace this card with your feature screen. The shell owns chrome; your route owns data.

DashboardShell composes ui sidebar primitives with route-based Breadcrumbs, a collapsible nav group helper, skeleton state, and DashboardAccountMenu. On mobile, the sidebar opens in a sheet via SidebarProvider.

Live preview with sample data in the docs. Dashboard shell guideLive preview with sample data in the docs. All shell scenarios

Breadcrumbs derive from the current path. Open another docs example to see compression, intermediate links, and copy-URL.

Breadcrumbs generates labels from route segments, compresses long trails into an ellipsis menu, and includes a copy-URL action. It is hidden below the medium breakpoint. The optional locale is reserved for host-app home resolution.

Pair with GoBackButton on nested detail routes:

import { GoBackButton } from "layout"

<GoBackButton fallbackHref="/docs" />

Form sections

import { FormSection } from "layout"

<FormSection title="Account" description="Identifiers visible to admins.">
  {/* FormKit.Field children */}
</FormSection>

FormLayout and FormLayoutActionsHint help align action bars on long forms (sticky footers, scroll hints). FormSection is only structure; FormKit owns field values and submission.

Empty states

Use EmptyState for an actionable screen state, not as a replacement for a table's loading or error handling:

<EmptyState
  title="No accounts yet"
  description="Create an account to start managing access."
  action={{ label: "Create account", onClick: openCreateDialog }}
/>

Keep the distinction clear in copy:

  • no data: explain what the screen represents and offer setup
  • filtered empty: offer a clear-filters action
  • no permission: explain access without exposing protected details

Confirmation dialog

"use client"

import { useConfirmationDialog } from "layout/confirmation-dialog"

const { confirm } = useConfirmationDialog()

const confirmed = await confirm({
  title: "Delete row?",
  description: "This cannot be undone.",
  variant: "destructive",
  confirmLabel: "Delete",
})

if (confirmed) {
  await deleteRow()
}

confirm() resolves to a boolean. The provider renders the dialog and handles focus; your action remains responsible for authorization, mutation, pending state, and error feedback. Calling the hook outside ConfirmationDialogProvider throws.

API reference

ConfirmationOptions

confirm() options
PropTypeDescription
titlestringDialog title; falls back to common.confirmAction.
descriptionstringRequired body copy.
variant"default" | "destructive"Destructive styles for delete flows.
confirmLabelstringPrimary button; defaults to common.continue.
cancelLabelstringSecondary button; defaults to common.cancel.

EmptyState

EmptyState props
PropTypeDescription
titleReactNodePrimary message.
descriptionReactNodeSupporting text.
iconEmptyStateIconPreset or custom icon slot.
actionEmptyStateActionOptional CTA button config.

Utility buttons

ExportPurpose
UtilityIconButtonGeneric icon button with tooltip
DocsIconButton / InfoIconButtonStandard help affordances
RefreshIconButtonToolbar refresh with loading state

DocsIconButton, InfoIconButton, and RefreshIconButton are specialized UtilityIconButton instances. They are client components and use tooltips, so keep an accessible label and register the app's tooltip provider.

Gotchas

i18n for confirms

Confirmation copy uses next-intl keys under common.* when labels are omitted. Load messages in the host app like the web playground does.

Breadcrumb data

Breadcrumbs expect your router or layout to expose segments. See layout/breadcrumb utilities for URL helpers.

Live preview with sample data in the docs. Breadcrumb component guideLive preview with sample data in the docs. Utility buttons

Subpaths: layout/breadcrumb, layout/navigation, layout/confirmation-dialog, layout/form-layout.

Accessibility and troubleshooting

Breadcrumb links, the current-page label, and the copy action have accessible names. Back and utility buttons expose labels through translations and tooltips. Confirmation dialogs should be triggered by a labeled control so keyboard users can identify the action.

If confirmation fails at runtime, check that the provider is mounted above the calling client component. If breadcrumbs show unexpected labels, inspect the pathname segments and the host app's home URL configuration; labels are derived from the URL, not a custom item list.

On this page