Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelogContact
Components
Buttons & Actions
ButtonButton GroupCopy ButtonDelete ButtonTheme ToggleToggleToggle Group
Inputs
Avatar UploadCalendarCheckboxColor PickerComboboxCountry SelectCron PickerCurrency InputDate PickerDate Range PickerDate Time PickerDuration PickerEmail InputEmoji PickerFile UploadFilter BuilderInputInput GroupListboxMasked InputMention InputMonth PickerMulti SelectNumber InputOTP InputPassword InputPhone InputRadio GroupRatingSearch InputSegmented ControlSelectSignature PadSliderSwitchTag InputTextareaTime PickerTimezone SelectTree Select
Forms
Address InputBooking WidgetCSV ImportFieldForm WizardInline EditInvoice line itemsRich Text EditorSchema Builder
Navigation
Back To TopBounce SidebarBreadcrumbCommand MenuGlobal SearchGooey NavHook SidebarNav MenuNav TabsPaginationProximity SidebarScroll ProgressSidebarStepsTable Of ContentsTabsTopbarUser MenuWorkspace Switcher
Overlays
Alert DialogContext MenuDialogDrawerDropdown MenuFamily DrawerHover CardPopoverShare DialogSheetShortcuts DialogTooltipTour
Feedback
AlertBannerCookie ConsentEmoji ReactionEmpty StateNotification BellNotification CenterProgressRoute ProgressSetup ChecklistSkeletonSpinnerToast
Data Display
Approval FlowAvatarBadgeCalendar ViewCode BlockComment ThreadContact cardCountdownDescription ListDiff ViewerFile ListFile managerFolderGanttJSON ViewerKbdLog ViewerMarkdownPresenceSchedulerTagTimelineTree View
Tables & Lists
Audit LogData TableDeal PipelineEditable GridKanbanListMembers tablePermissions MatrixTableTask ListVirtual List
Charts & Stats
Animated CounterArea chartBar chartDonut chartFunnel chartGaugeGitHub ActivityHeatmap calendarLeaderboardLine chartRadar chartRadial barSparklineStat CardTrackerTrend badge
Layout
AccordionApp ShellCardCollapsibleDetail LayoutEntity HeaderPage HeaderScroll AreaSeparatorSettings LayoutSplit ViewToolbar
Media
Audio PlayerCarouselCSV ViewerDOCX EditorDOCX ViewerExcel ViewerFile SystemImage compareImage CropperImage GalleryImage LightboxLocation MapPDF ViewerPowerPoint ViewerQR CodeStep PlayerVideo Player
AI Kit
Agent Status CardChat MessageChat ThreadChat WidgetConversation ListFluid OrbGrid RevealMatrix OrbModel PickerPrompt InputReasoning IndicatorRule BuilderSuggestion ChipsTool Call TimelineUsage MeterVoice InputWorkflow Canvas
Text & Effects
Background PatternGravity LettersMarqueeTerminalText AnimateText Rotate
Mobile
Action SheetBottom Tab BarFABHaptic ButtonHero HeaderInstall PromptLarge Title HeaderNotification BannerOnboarding CarouselPicker WheelPull To RefreshSlide To ConfirmSticky CTA BarSwipe DeckSwipeable Tabs
Commerce
Cart drawerCategory pickerCheckout stepsCoupon inputFavorite buttonFilter panelListing cardListing gridOrder trackerPricePrice rangePricing tableProduct galleryQuantity stepperReviewsSeller card
Marketing Sections
App Download SectionBlog Cards SectionCTA SectionFAQ SectionFeature GridFeature ShowcaseHero SectionIntegrations SectionLogo CloudMarketing FooterMarketing HeaderMarketing SectionNewsletter SectionPricing SectionStats SectionSteps SectionTeam SectionTestimonials SectionTimeline SectionVideo Section
Blocks
Admin Dashboard PageAdmin ShellAI Chat PageAPI Keys PageAutomation Builder PageCheckout PageCRM Contact PageCRM Pipeline PageError PagesInbox PageInvoice PageKPI DashboardListing Detail PageLogin PageMarketplace Home PageMarketplace Search PageMarketplace ShellMobile App HomeOnboarding FlowOrder History PagePost Listing WizardPricing PageProfile PageRegister PageSettings PageUsers Admin Page
Templates
About PageAcademy PageAccessibility PageAffiliate PageAgency PageAI Product PageAppointment Booking PageBlog PageBlog Post PageBug Bounty PageCareers PageChangelog PageCommunity PageCompare PageContact PageCustomer Story PageCustomers PageDemo Booking PageDevelopers PageDocs Home PageDownload PageEnterprise PageEvents PageExperts PageFAQ PageFeature PageGlossary PageHelp Article PageHelp Center PageHome Services PageImpact PageIntegration Detail PageIntegrations PageJob Detail PageLanding PageLaunch PageLegal PageManifesto PagePortfolio PagePress PageProduct Tour PagePromo PageReal Estate PageReferral PageResearch Report PageRestaurant PageRoadmap PageROI Calculator PageSecurity PageSolutions PageStartups PageStatus PageStorefront PageSwitch PageTemplate Gallery PageTravel PageWaitlist PageYear in Review Page
Hooks & Utilities
AI ChatChart kitCommerceDate utilsFile exportFile typesFiltersForm validationHapticsInvoicingMotion presetsNavigationNumber formatRelative timeRomanian locationsSpreadsheetText matchTonesUIProvideruseClipboarduseConsentuseControllableStateuseCooldownuseDebounceuseKeyboardInsetuseLocaleuseLongPressuseMediaControlsuseMediaQueryuseOverflowItemsusePersistedStateuseScrollEdgesuseScrollStateuseSelectionuseShortcutuseStickToBottom
GalleryTheme

Layout

Detail Layout

Layout

Detail Layout React component for Tailwind CSS v4

CollapsibleEntity Header
Live props
Layout
asidePosition
asideWidth360px
breakpoint
mobileAside
maxWidth
Behavior
asideSticky
stickyHeader
Appearance
gap
sectionVariant
State
loading
defaultTab
Code
import { DetailLayout } from "@/components/ui/detail-layout"

<DetailLayout />
  1. Home
  2. Components
  3. Layout
  4. Detail Layout
Pro

Detail Layout React component for Tailwind CSS v4

v1.0.0·Added in 1.0.0

A React detail layout for record pages (a client, a contact, an invoice): header, a main column of sections, a side panel and section cards.

Read a record the way it is built: the header on top, the main sections (activity, notes, invoices) on the left and the side panel (details, contacts, files) beside them once the layout itself is wide enough (960px of content), which sticks while the long column scrolls and scrolls on its own when it is taller than the screen. Below the breakpoint the panel moves under the main column, above it, or into a second tab: a full-width segmented control that sticks under the top bar, where a tap (or Tab then the arrow keys) switches panes with a short fade and both panes keep their scroll and state. Every section card has a title with a count, a pencil to edit and a ⋯ menu (an action sheet on phones); a collapsible section opens and closes from anywhere on its header row, with a mouse, a tap, or Enter / Space on the focused title, and its chevron turns. Loading sections show skeleton lines and fade into their content; empty ones show a small empty state with an action. Tab order is always header, main, side panel.

Detail Layout is a Pro React component in the React layout components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add detail-layout, together with Badge, Button, Collapsible, Dropdown Menu, Empty State and 5 more and the npm packages motion and lucide-react.

Add to a project

Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup

npx wingo-ui@latest add detail-layout

Detail Layout is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans

Usage

Import DetailLayout from @/components/ui/detail-layout.

import { Activity, FolderOpen, Info, Pencil, Receipt, Trash2 } from "lucide-react"
import { DetailLayout, DetailSection } from "@/components/ui/detail-layout"
import { EntityHeader } from "@/components/ui/entity-header"

export function ClientPage() {
  return (
    <div className="px-4 pb-12 md:px-6">
      <DetailLayout
        header={
          <EntityHeader
            eyebrow="Client · Company"
            title="Tech Solutions Inc."
            avatar={{ name: "Tech Solutions Inc.", radius: "md" }}
            status={{ label: "Active", tone: "success" }}
            facts={[
              { label: "Balance", value: "$12,480" },
              { label: "Tax ID", value: "12-3456789", copyValue: "12-3456789" },
            ]}
          />
        }
        mobileAside="tab"
        labels={{ asideRegion: "Client details" }}
        aside={
          <>
            <DetailSection title="Details" icon={<Info />} onEdit={() => {}}>
              <p className="text-sm">Tax ID 12-3456789 · Chicago · paid in 30 days</p>
            </DetailSection>
            <DetailSection title="Files" icon={<FolderOpen />} count={3} collapsible>
              <p className="text-sm">Contract 2026.pdf</p>
            </DetailSection>
          </>
        }
      >
        <DetailSection
          title="Activity"
          icon={<Activity />}
          count={8}
          actions={[
            { label: "Edit", icon: <Pencil /> },
            { label: "Delete", icon: <Trash2 />, tone: "danger" },
          ]}
        >
          <p className="text-sm">Invoice #2026-0412 issued today, 4:35 PM.</p>
        </DetailSection>
        <DetailSection title="Invoices" icon={<Receipt />} empty labels={{ empty: "No invoices issued" }} />
      </DetailLayout>
    </div>
  )
}

Props

  • header
    ReactNode

    The top of the page, usually an EntityHeader. Nothing is reserved when it is left out.

  • children
    ReactNode

    The main column, usually DetailSection cards (activity, notes, invoices).

  • aside
    ReactNode

    The side panel's content (details, contacts, files). A <DetailAside> passed here is used as the panel itself, so it can carry its own props. None: a single column at maxWidth.

  • asidePositiondefault "end"
    "end" | "start"

    Which side the panel takes from the breakpoint. The DOM stays main then aside, so reading and Tab order never change; start only reorders the grid.

  • asideWidthdefault 360
    number

    The side panel's width in px once the layout is 960px wide; with breakpoint md it is 300px between 720 and 960px.

  • asideStickydefault true
    boolean

    The panel sticks under the top bar (--topbar-offset), the sticky header and --detail-sticky-offset (room for a bar that pins itself, like EntityHeader's 49px condensed bar) while the main column scrolls, and scrolls inside itself when it is taller than the viewport (--detail-viewport-height when the page scrolls inside a box).

  • stickyHeaderdefault false
    boolean

    The header sticks at the top on a glass bar (use it when the header has no condensed bar of its own) and writes its height to --detail-header-height, so the panel and the phone tabs stick under it.

  • breakpointdefault "lg"
    "md" | "lg"

    Where the two columns start, measured on the layout's own width (a container query, so it works beside an app sidebar or in a panel): lg at 960px of content (a 1024px screen minus gutters; records need the width) or md at 720px, with a 300px panel up to 960px.

  • mobileAsidedefault "below"
    "below" | "above" | "tab"

    Below the breakpoint the panel goes after the main column, before it, or into a second tab (a sticky segmented control with labels.main / labels.aside; the hidden pane stays mounted).

  • gapdefault "md"
    "sm" | "md" | "lg"

    16 / 24 / 32px between the columns, the header and the main sections; the panel's cards sit a step closer.

  • maxWidthdefault "xl"
    "none" | "lg" | "xl" | "2xl"

    The content width, centered: max-w-5xl / 6xl / 7xl, or the full width. The page provides the side gutters.

  • sectionVariantdefault "surface"
    "surface" | "outline" | "plain"

    The default look of every DetailSection inside: a raised panel, a hairline outline, or plain on the page with a top hairline. A section's own variant wins.

  • loadingdefault false
    boolean

    Every DetailSection inside shows its skeleton (unless it sets loading itself), then fades into its content.

  • tab
    "main" | "aside"

    The controlled phone tab (mobileAside="tab").

  • defaultTabdefault "main"
    "main" | "aside"

    The phone tab to start on while uncontrolled (mobileAside="tab").

  • onTabChange
    (tab: DetailLayoutTab) => void

    Called when the phone tab changes.

  • labels
    Partial<DetailLayoutLabels>

    main ("Activity") and aside ("Details") name the phone tabs, asideRegion ("Details") names the <aside> landmark, tabs ("Page sections") names the tab group. English defaults in DEFAULT_DETAIL_LAYOUT_LABELS.

  • classNames
    Partial<Record<"header" | "body" | "main" | "aside" | "tabs", string>>

    Extra classes for the header wrapper, the grid, the main column, the side panel and the phone tab bar (e.g. a negative margin so the glass bars reach the page edge).

  • className
    string

    Classes for the root, which also takes the rest of the div props. Nested DetailLayouts are not supported.

Parts

  • DetailSection

    component

    The section card (a <section aria-labelledby> with an <h2>). title, description, icon (16px, muted), count (a number renders a soft badge), actions (a node, or MenuEntry[] for a ⋯ menu that is an action sheet on phones), onEdit (a pencil), collapsible with open / defaultOpen = true / onOpenChange (the whole header row toggles, content stays mounted and find-in-page opens it), variant "surface" | "outline" | "plain" (default: the layout's sectionVariant), padding "none" | "sm" | "md" = "md" (none for tables that bleed to the edges), loading, skeleton, empty, emptyState, footer (a "View all (12)" row), headingLevel 2 | 3 | 4 = 2, labels { edit, expand, collapse, actions, empty, emptyDescription ("" hides the line) }, classNames (header, icon, title, count, description, actions, edit, toggle, body, empty, footer). Reads useDefaults("detail-section"). Props: DetailSectionProps.

  • DetailAside

    component

    The side column as an <aside aria-label>; pass it as aside when the panel needs its own props. Inside a DetailLayout it follows the layout's position, stickiness and phone tab. Props: ComponentProps<"aside">.

  • DEFAULT_DETAIL_LAYOUT_LABELS

    constant

    The English layout strings, to spread under a translation. Type: DetailLayoutLabels.

  • DEFAULT_DETAIL_SECTION_LABELS

    constant

    The English section strings: edit "Edit", expand "Expand", collapse "Collapse", actions "More actions", empty "Nothing here yet", emptyDescription "When there is something to show, it will appear here.". Type: DetailSectionLabels.

Examples

Phone tabs

mobileAside="tab" in a 390px frame: Activity and Details as two tabs that keep their state.

Contact page

The person on the start side in a 300px column (on top when narrow), activity in the main one.

Collapsible sections

Counts, descriptions and ⋯ menus; the whole header row toggles.

Loading

The layout's loading flag puts every section into its skeleton.

Empty sections

A new prospect: each empty section offers the action that fills it.

Sticky header

A plain page-header sticks through the layout; the side panel sticks under it.

Dependencies

motionlucide-reactbadgebuttoncollapsibledropdown-menuempty-statesegmented-controlskeletonuse-controllable-statemotionui-config

Files

  • components/ui/detail-layout.tsx

FAQ

Is the React Detail Layout component free?

The preview, props and docs are free; copying the source needs a Pro plan. See plans.

How do I install Detail Layout?

Run npx wingo-ui@latest add detail-layout. It also adds the Wingo UI items it builds on (Badge, Button, Collapsible, Dropdown Menu, Empty State and 5 more) and the npm packages motion and lucide-react. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/detail-layout.

How does Detail Layout behave on phones?

Every section card has a title with a count, a pencil to edit and a ⋯ menu (an action sheet on phones); a collapsible section opens and closes from anywhere on its header row, with a mouse, a tap, or Enter / Space on the focused title, and its chevron turns.

See also

Entity HeaderCardCollapsibleSplit ViewContact card

Used in

CRM Contact Page

More React layout components

Page HeaderScroll AreaSettings LayoutToolbarSeparatorApp Shell
All 12 React layout components
PreviousCollapsibleNext Entity Header
AAcme CRM
  1. Clients
  2. Tech Solutions Inc.
Clients
TI

Tech Solutions Inc.Tech Solutions Inc.

Client · CompanyActiveVIP
Balance
$12,480
Owner
Emma Carter
Tax ID
12-3456789
City
Chicago
Client since
Mar 12, 2024
Last invoice
Sep 18, 2026
  • Overview
  • Invoices12
  • Quotes3
  • Contacts4
  • Files
  • Activity

breakpoint: lg · aside: sticky, 360px · phone: aside below, tab main · header: scrolls · last: none

Activity8

09/24/2026
  1. Invoice #2026-0412 issued
    09/24/2026, 4:35 PM
    $7,271.42 · due Oct 24, 2026
  2. Call with Daniel Brooks
    09/24/2026, 11:20 AM
    14 min · wants 5 more licenses from October
09/23/2026
  1. Email sent: Quote Q-2026-118
    09/23/2026, 3:05 PM
    CRM licenses, 25 users · $18,000
  2. Note: @Liam Walker takes over the e-invoicing integration
    09/23/2026, 9:40 AM
09/19/2026
  1. Payment received
    09/19/2026, 10:12 AM
    $6,049.50 for invoice #2026-0371
09/17/2026
  1. Meeting at the client's office
    09/17/2026, 1:00 PM
    Reports module demo, 233 S Wacker Dr
09/12/2026
  1. New contact: Laura Price
    09/12/2026, 8:55 AM
    Chief accountant
09/02/2026
  1. Master agreement renewed
    09/02/2026, 12:30 PM
    5% volume discount above $20,000 a quarter

Notes2

  1. Liam Walker09/23/2026, 10:05 AM
    They usually pay in 30 days. Invoices go by email to accounting, with the purchase order attached. @Emma Carter should we send them through e-invoicing too?
  2. Emma Carter09/23/2026, 11:42 AM
    Yes, from October everything goes through e-invoicing. I also saved Laura Price as the contact for confirmations.
    👍2
CtrlEnterControl Enterto send

Invoices12

Tech Solutions Inc. invoices
InvoiceIssuedDueTotalStatus
Sep 24, 2026Oct 24, 2026$7,271.42Issued
Sep 2, 2026Oct 2, 2026$2,150.50Unpaid
Aug 14, 2026Sep 13, 2026$6,049.50Paid
Jul 29, 2026Aug 28, 2026$1,890.00Paid
  • #2026-0412
    Issued
    Sep 24, 2026
    Due
    Oct 24, 2026
    Total
    $7,271.42
    Status
    Issued
  • #2026-0398
    Issued
    Sep 2, 2026
    Due
    Oct 2, 2026
    Total
    $2,150.50
    Status
    Unpaid
  • #2026-0371
    Issued
    Aug 14, 2026
    Due
    Sep 13, 2026
    Total
    $6,049.50
    Status
    Paid
  • #2026-0344
    Issued
    Jul 29, 2026
    Due
    Aug 28, 2026
    Total
    $1,890.00
    Status
    Paid
View all (12)

Details

Tax ID
12-3456789
Registration
DE-5512345
Address
233 S Wacker Dr, Chicago, IL 60606
IBAN
GB29 NWBK 6016 1331 9268 19
Payment terms
30 days

Contacts3

DB

Daniel Brooks

Head of procurement

LP

Laura Price

Chief accountant

SP

Sam Porter

IT manager

3

Contract 2026.pdf471 KB·09/02/2026, 12:30 PM·Ryan Brooks
Quote Q-2026-118.pdf248 KB·09/23/2026, 3:05 PM·Emma Carter
Logo.png84 KB·03/12/2026, 9:10 AM·Emma Carter