This page covers the web client components and utilities responsible for displaying, navigating, and acting on assets (photos and videos). It includes the full-screen asset viewer, the timeline grid display, asset actions (archive, trash, stack, download), the file upload system, and the duplicate detection interface.
For information about the underlying background processing that produces thumbnails and transcodes media, see Media Processing. For album creation and sharing, see Albums and Sharing. For mobile-specific asset management, see Mobile-Specific Features.
The primary way users browse their library is the Timeline component, which renders assets grouped by month and date in a date-descending list. It is reused across all major views in the web client.
Routes using Timeline:
Separate from Timeline, a flat grid layout is provided by GalleryViewer (used on search results, memory pages, folders, and shared link views). The GalleryViewer uses a justified layout algorithm (getJustifiedLayoutFromAssets) to fit assets into rows of equal height.
Timeline vs. GalleryViewer:
| Feature | Timeline | GalleryViewer |
|---|---|---|
| Grouping | By month and date | None |
| Layout | Scrolling date-bucketed list | Justified row layout |
| Asset source | TimelineManager (paginated, SDK-backed) | Static AssetResponseDto[] array |
| Routing | Encodes assetId in URL path | Encodes assetId in URL path |
| Used by | Main views, albums, people | Search, memories, folders, shared links |
Sources: web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte
AssetViewer (web/src/lib/components/asset-viewer/asset-viewer.svelte) is a full-screen overlay that takes an AssetCursor prop (current, previous, next asset) and renders the appropriate sub-viewer based on asset type and state.
AssetCursor {
current: AssetResponseDto
nextAsset?: AssetResponseDto
previousAsset?: AssetResponseDto
}
Defined at web/src/lib/components/asset-viewer/asset-viewer.svelte57-61
The viewerKind derived value (computed at web/src/lib/components/asset-viewer/asset-viewer.svelte398-418) selects among seven sub-viewer modes:
Viewer Kind Selection Diagram:
Sources: web/src/lib/components/asset-viewer/asset-viewer.svelte398-418 web/src/lib/constants.ts21-30
The viewer occupies a full-screen section#immich-asset-viewer using CSS grid (grid-cols-4 grid-rows-[64px_1fr]):
AssetViewerNavBar (top navigation, hidden during slideshow)PreviousAssetAction navigation buttonNextAssetAction navigation buttonDetailPanel or EditorPanelwithStacked=true)ActivityViewer (when in a shared album)Sources: web/src/lib/components/asset-viewer/asset-viewer.svelte448-652
Slideshow playback is controlled by the slideshowStore. The AssetViewer subscribes to slideshowState and slideshowNavigation to drive auto-advance behavior.
Slideshow states (SlideshowState enum): None, PlaySlideshow, StopSlideshow
Navigation modes (SlideshowNavigation enum): AscendingOrder, Shuffle
When shuffle mode is active, SlideshowHistory (web/src/lib/utils/slideshow-history.ts) tracks visited assets for back/forward navigation. When repeat is enabled and all assets are exhausted, the viewer loops back to slideshowStartAssetId.
Sources: web/src/lib/components/asset-viewer/asset-viewer.svelte256-294
When an asset belongs to a stack, AssetViewer fetches the full StackResponseDto via getStack() and renders a thumbnail filmstrip at the bottom. Hovering a stacked thumbnail previews that asset using the appropriate sub-viewer without navigating away.
Stack-related AssetAction values: STACK, UNSTACK, SET_STACK_PRIMARY_ASSET, REMOVE_ASSET_FROM_STACK
Sources: web/src/lib/components/asset-viewer/asset-viewer.svelte599-633 web/src/lib/constants.ts3-17
PhotoViewer (web/src/lib/components/asset-viewer/photo-viewer.svelte) handles still image display with:
targetImageSize derived from assetViewerManager.zoom)zoomImageAction and assetViewerManager.zoom; double-click or z key toggles zoomsvelte-gestures for left/right swipe on mobileBlurredBackground mode which renders a blurred copy behind the main imageboundingBoxesArray as <div> rectangles using getBoundingBox()OcrBoundingBox components when ocrManager.showOverlay is trueisFaceEditMode.value, renders FaceEditor over the imageCtrl+C / Cmd+C copies the image via copyImageToClipboard()The image URL is computed by getAssetUrl() with forceOriginal set when the user has zoomed in or originalImageLoaded is already true.
Sources: web/src/lib/components/asset-viewer/photo-viewer.svelte
AssetViewerNavBar (web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte) renders the top bar of the viewer. Its action set is generated by getAssetActions() from web/src/lib/services/asset.service.ts
Actions available in the nav bar:
| Action | Condition |
|---|---|
Share | Always |
Offline | Always |
PlayMotionPhoto / StopMotionPhoto | Live photos |
ZoomIn / ZoomOut | Image assets |
Copy | Image assets |
Info | Has metadata |
Favorite / Unfavorite | Always |
RatingAction | Owner only |
Edit | Always |
DeleteAction | Owner only |
Download / DownloadOriginal | In overflow menu |
RestoreAction | Trashed assets |
AddToAlbum | In overflow menu |
AddToStackAction | Owner only |
UnstackAction / KeepThisDeleteOthersAction | Has stack |
SetStackPrimaryAsset / RemoveAssetFromStack | In a stack |
SetAlbumCoverAction | In an album |
SetFeaturedPhotoAction | In person view |
SetProfilePictureAction | Image, owner |
ArchiveAction | Owner, not locked |
SetVisibilityAction | Owner, not trashed |
Slideshow | Not locked |
View in timeline | Owner, not archived/trashed |
View similar photos | Smart search enabled |
Play original/transcoded video | Video assets |
RefreshFacesJob, RefreshMetadataJob, RegenerateThumbnailJob, TranscodeVideoJob | Owner only |
Sources: web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte web/src/lib/constants.ts3-17
Defined in web/src/lib/constants.ts3-17:
AssetAction {
ARCHIVE, UNARCHIVE
TRASH, DELETE, RESTORE
STACK, UNSTACK
SET_STACK_PRIMARY_ASSET, REMOVE_ASSET_FROM_STACK
SET_VISIBILITY_LOCKED, SET_VISIBILITY_TIMELINE
SET_PERSON_FEATURED_PHOTO
RATING
}
handleAction() in AssetViewer dispatches on these action types to update local state (stack reference, cursor, etc.) and then calls the parent onAction callback. Events that affect the timeline (delete, trash) are emitted via eventManager.emit('AssetsDelete', ...).
Sources: web/src/lib/components/asset-viewer/asset-viewer.svelte302-343
DetailPanel (web/src/lib/components/asset-viewer/detail-panel.svelte) slides in from the right to show asset metadata. It is shown when assetViewerManager.isShowDetailPanel is true and the asset has metadata.
Sections in the detail panel:
| Section | Content |
|---|---|
| Offline warning | Shown when asset.isOffline |
| Description | DetailPanelDescription – editable for owners |
| Rating | DetailPanelRating – star rating for owners |
| People | Face thumbnails linking to person pages; edit/tag faces |
| EXIF details | Date/time (editable), filename, dimensions, megapixels, file size |
| Camera info | Make, model (search-linked), exposure, ISO |
| Lens info | Lens model (search-linked), f-number, focal length |
| Location | DetailPanelLocation – reverse geocoded city/country |
| Map | Embedded map if coordinates present (lazy-loaded) |
| Albums | List of albums the asset appears in |
| Shared by | Asset owner avatar (in shared albums) |
| Tags | DetailPanelTags – if tags feature enabled |
The date field is clickable for owners and opens AssetChangeDateModal. The filename shows the original path on toggle. Camera make/model and lens model are hyperlinked to search results.
Sources: web/src/lib/components/asset-viewer/detail-panel.svelte
When assets are multi-selected in Timeline or GalleryViewer, a AssetSelectControlBar is shown with bulk operations. The available actions are generated by getAssetBulkActions().
Multi-select Action Flow:
Available bulk actions (varies by context):
updateAssets with isFavoriteupdateAssets with visibility: ArchivedownloadArchive() via getDownloadInfo + chunked POSTTimelineManager months, loading lazilyThe AssetInteraction class manages the selection set, shift-range selection candidates, and selectAll flag.
Sources: web/src/lib/utils/asset-utils.ts web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte:451-499
Archive changes an asset's visibility to AssetVisibility.Archive, hiding it from the main timeline while keeping it accessible from /archive. The single-asset utility is toggleArchive() and the bulk version is archiveAssets() in web/src/lib/utils/asset-utils.ts395-438
Trash moves assets to a soft-deleted state. The trash page (/trash) shows all trashed assets. Permanently deleting calls deleteAssets from the SDK with force: true. If the trash feature flag is disabled, delete goes directly to permanent deletion.
Both operations update the Timeline in-memory by calling timelineManager.removeAssets() or timelineManager.update().
Sources: web/src/lib/utils/asset-utils.ts395-438 web/src/routes/(user)/trash/[[photos=photos]]/[[assetId=id]]/+page.svelte
Stacking groups related assets (e.g., burst photos, RAW+JPEG pairs) so only the primary is shown in the timeline. The utility function stackAssets() in web/src/lib/utils/asset-utils.ts282-313 calls createStack() from the SDK.
stackAssets(assets) → createStack({ assetIds }) → StackResponseDto
deleteStack() (web/src/lib/utils/asset-utils.ts316-341) fetches each stack, deletes via deleteStacks(), and clears the stack field on the returned assets. keepThisDeleteOthers() (web/src/lib/utils/asset-utils.ts343-358) deletes all assets in a stack except the chosen one.
Sources: web/src/lib/utils/asset-utils.ts277-358
Downloads are handled by downloadArchive() in web/src/lib/utils/asset-utils.ts105-154 which:
getDownloadInfo() to get a list of archive chunks (DownloadInfoDto.archives[])./download/archive with progress tracking via downloadManager.downloadBlob() to trigger a browser download using a temporary anchor element.The downloadManager (web/src/lib/managers/download-manager.svelte.ts) is a Svelte store that tracks in-progress downloads, visible in DownloadPanel.
Preferences (download.archiveSize) control the maximum archive size per chunk, allowing large collections to be split into multiple ZIP files.
Sources: web/src/lib/utils/asset-utils.ts105-154 web/src/lib/components/asset-viewer/download-panel.svelte
The upload path on the web client:
Key components:
openFileUploadDialog() (web/src/lib/utils/file-uploader.ts86-95) – Opens native file picker, filtered by uploadManager.getExtensions()fileUploadHandler() (web/src/lib/utils/file-uploader.ts101-125) – Filters by extension, enqueues each filefileUploader() (web/src/lib/utils/file-uploader.ts140-250) – Computes SHA-1, checks deduplication via checkBulkUpload, uploads via POST /assetsuploadExecutionQueue – An ExecutorQueue with concurrency 2uploadAssetsStore – Tracks state per file: PENDING, STARTED, ERROR, DUPLICATED, DONEThe UploadPanel component (web/src/lib/components/shared-components/upload-panel.svelte) displays upload progress in a fixed bottom-right overlay. It shows counts of successes, errors, and duplicates, and supports minimizing the panel while uploads proceed.
Sources: web/src/lib/utils/file-uploader.ts web/src/lib/components/shared-components/upload-panel.svelte
The utilities page at /utilities/duplicates allows users to resolve groups of duplicate assets detected by the server.
Duplicate resolution flow:
DuplicatesCompareControl (web/src/lib/components/utilities-page/duplicates/duplicates-compare-control.svelte) manages a SvelteSet<string> of selected (keep) asset IDs.suggestDuplicate() heuristically picks the best candidate to keep.DuplicateAsset displays per-asset metadata for comparison: resolution, file size, date, location, album count, etc.a = keep all, d = keep none, Shift+C = confirm, Shift+S = stack all.deleteAssets on the discarded IDs and deleteDuplicates to clear the duplicate group record.Sources: web/src/lib/components/utilities-page/duplicates/duplicates-compare-control.svelte web/src/routes/(user)/utilities/duplicates/[[photos=photos]]/[[assetId=id]]/+page.svelte, web/src/lib/components/utilities-page/duplicates/duplicate-asset.svelte
Sources: web/src/lib/components/asset-viewer/asset-viewer.svelte web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte
Refresh this wiki
This wiki was recently refreshed. Please wait 2 days to refresh again.