- Overview
- Anatomy
- Header
- Navigation
- Renderers
FileViewerSidebar is the optional rail inside FileViewerContent. It belongs to
the File Viewer frame: FileViewer, FileViewerHeader, FileViewerContent,
FileViewerSidebar, and FileViewerInset.
Use it for document-adjacent panels: page sections, attachments, source fields,
thumbnails, and other workflow rails that sit beside a viewer inset. Do not
use the app-shell SidebarProvider pattern for these rails.
Viewer Sidebar Example
This is the shape used across file-backed viewers: FileViewerProvider owns
sidebar open state, FileViewer owns mode resolution, and
FileViewerSidebar owns the rail shell and accessible rail label. The
navigation component inside the rail owns the row model and interaction. Here,
SegmentSidebar owns section rows while FileViewerDocument owns the document
renderer.
"use client";
import * as React from "react";
import { meanConfidence, toSegments } from "@/lib/segments";
import { FileViewerPreview } from "@/components/ui/file-viewer";
import { SegmentSidebar } from "@/components/ui/segment-sidebar";
import { useSegmentInteraction } from "@/components/ui/use-segment-interaction";
import {
ViewerBody,Composition
<FileViewerProvider source={source} defaultSidebarOpen>
<FileViewer sidebarMode="auto">
<FileViewerHeader>
<FileViewerSidebarTrigger />
<FileViewerTitle className="flex-1" />
<FileViewerControls className="ms-auto" />
</FileViewerHeader>
<FileViewerContent>
<FileViewerSidebar aria-label="Document sections" width="18rem">
<FileViewerSidebarHeader>Sections</FileViewerSidebarHeader>
<FileViewerSidebarContent>
<SegmentSidebar
segments={segments}
interaction={interaction}
currentPage={currentPage}
onSelect={(segment) => scrollToPage(segment.pages[0])}
/>
</FileViewerSidebarContent>
</FileViewerSidebar>
<FileViewerInset>
<FileViewerViewport>
<FileViewerDocument />
</FileViewerViewport>
</FileViewerInset>
</FileViewerContent>
</FileViewer>
</FileViewerProvider>Ownership Boundary
FileViewerProviderowns sidebar open state and controlled/uncontrolled state props.FileViewerowns sidebar mode resolution, trigger wiring, the single-primary-sidebar contract, and the root motion store for inline geometry.FileViewerSidebarowns spatial placement, declared width, side override, collapse behavior, and the rail's accessible label.FileViewerInsetowns the main file inset and document frame beside the rail.SidebarList*owns providerless grouped-row grammar inside a rail.SegmentSidebarowns segment row state, preview interaction, current-page highlighting, and select callbacks.AttachmentSidebarowns file attachment rows, thumbnails, selected state, disabled rows, and empty state.
Do not mount SidebarProvider inside File Viewer. That provider is for
app-shell navigation: persistence, global shortcuts, mobile sheet projection,
and workspace menus. File Viewer rails use FileViewerSidebar for placement
and SidebarList* for row grammar.
Domain Rails
Use a named domain sidebar when the rows have semantics:
SegmentSidebarfor split and partition segments. It coordinates with legends and page timelines throughuseSegmentInteraction.AttachmentSidebarfor files attached to an email, message, or workflow. It rendersFileThumbnailrows and lets the caller own selected attachment id.- source-field sidebars in extraction workflows should use the same
FileViewerSidebarplacement contract and a domain row model, not an app navigation menu.
Use raw SidebarList* primitives only when you are creating a new domain rail
and need grouped rows without app-sidebar behavior.
Use FileViewerSidebarContent and the sidebar section primitives when a custom
rail needs File Viewer spacing but not menu semantics.
Sidebar Modes
FileViewer sidebarMode="auto" starts in overlay mode until the viewer has a
measured width, then resolves to inline or overlay. Use fixed modes only when a
workflow requires it:
<FileViewerProvider source={source} defaultSidebarOpen>
<FileViewer>
<FileViewerContent>
<FileViewerInset>
<FileViewerViewport>
<FileViewerDocument />
</FileViewerViewport>
</FileViewerInset>
<FileViewerSidebar aria-label="Attachments" side="right" width="20rem">
<AttachmentSidebar items={attachments} selectedId={selectedId} />
</FileViewerSidebar>
</FileViewerContent>
</FileViewer>
</FileViewerProvider>FileViewerSidebarTrigger is disabled until a FileViewerSidebar registers
with the nearest FileViewer frame. In overlay mode, Escape closes the sidebar
and focus returns to the trigger.
For inline rails, the document and sidebar resize from one FileViewer motion clock. The motion store writes the sidebar gap and document surface transform from the same geometry frame so inherited root styles do not invalidate the full viewer tree. Renderers use the document-frame contract for fit-width scale, anchoring, and virtualization.
Structural Attributes
File Viewer sidebar primitives expose stable attributes for local styling and tests:
data-slot="file-viewer-header",file-viewer-content,file-viewer-sidebar,file-viewer-sidebar-header,file-viewer-sidebar-content,file-viewer-sidebar-footer,file-viewer-inset, andfile-viewer-sidebar-trigger;data-file-viewer-sidebar-mode="inline" | "overlay";data-file-viewer-sidebar-open="true" | "false";data-file-viewer-sidebar-state="expanded" | "collapsed";data-file-viewer-sidebar-side="left" | "right";data-collapsibleanddata-side.
Put domain meaning in the named rail component and accessible label:
aria-label="Document sections", aria-label="Email attachments", or
aria-label="Source fields".
The lower-level shared viewer primitives also emit data-viewer-* attributes,
but file-viewer examples and app-level styling should prefer the namespaced
data-file-viewer-* contract.
Source
"use client";
import * as React from "react";
import { Slot } from "radix-ui";
import { cn } from "@/lib/utils";
import {
DEFAULT_FILE_VIEWER_SIDEBAR_WIDTH,
type FileViewerSidebarCollapsible,
type FileViewerSidebarSide,
useFileViewerShell,