diff --git a/web/apps/photos/src/components/Collections/CollectionCard.tsx b/web/apps/photos/src/components/Collections/CollectionCard.tsx
index 7d757561ba..b6d4b36c49 100644
--- a/web/apps/photos/src/components/Collections/CollectionCard.tsx
+++ b/web/apps/photos/src/components/Collections/CollectionCard.tsx
@@ -3,7 +3,7 @@ import { EnteFile } from "@/new/photos/types/file";
import {
LoadingThumbnail,
StaticThumbnail,
-} from "components/PlaceholderThumbnails";
+} from "@/new/photos/components/PlaceholderThumbnails";
import { useEffect, useState } from "react";
export default function CollectionCard(props: {
diff --git a/web/apps/photos/src/components/SearchBar.tsx b/web/apps/photos/src/components/SearchBar.tsx
index 6c4d1928bd..541afb524e 100644
--- a/web/apps/photos/src/components/SearchBar.tsx
+++ b/web/apps/photos/src/components/SearchBar.tsx
@@ -1,6 +1,7 @@
import { assertionFailed } from "@/base/assert";
import { useIsMobileWidth } from "@/base/hooks";
import { FileType } from "@/media/file-type";
+import { ItemCard } from "@/new/photos/components/ItemCards";
import {
isMLSupported,
mlStatusSnapshot,
@@ -37,7 +38,6 @@ import {
useTheme,
type Theme,
} from "@mui/material";
-import CollectionCard from "components/Collections/CollectionCard";
import { ResultPreviewTile } from "components/Collections/styledComponents";
import { t } from "i18next";
import pDebounce from "p-debounce";
@@ -580,11 +580,10 @@ const OptionContents = ({ data }: { data: SearchOption }) => (
{data.previewFiles.map((file) => (
- null}
- collectionTile={ResultPreviewTile}
+ TileComponent={ResultPreviewTile}
/>
))}
diff --git a/web/apps/photos/src/components/pages/gallery/PreviewCard.tsx b/web/apps/photos/src/components/pages/gallery/PreviewCard.tsx
index deb212ef77..61ec39e21d 100644
--- a/web/apps/photos/src/components/pages/gallery/PreviewCard.tsx
+++ b/web/apps/photos/src/components/pages/gallery/PreviewCard.tsx
@@ -15,7 +15,7 @@ import {
import {
LoadingThumbnail,
StaticThumbnail,
-} from "components/PlaceholderThumbnails";
+} from "@/new/photos/components/PlaceholderThumbnails";
import i18n from "i18next";
import { DeduplicateContext } from "pages/deduplicate";
import { GalleryContext } from "pages/gallery";
diff --git a/web/packages/new/photos/components/ItemCards.tsx b/web/packages/new/photos/components/ItemCards.tsx
new file mode 100644
index 0000000000..9bfa8afb89
--- /dev/null
+++ b/web/packages/new/photos/components/ItemCards.tsx
@@ -0,0 +1,43 @@
+import {
+ LoadingThumbnail,
+ StaticThumbnail,
+} from "@/new/photos/components/PlaceholderThumbnails";
+import downloadManager from "@/new/photos/services/download";
+import { type EnteFile } from "@/new/photos/types/file";
+import React, { useEffect, useState } from "react";
+
+interface ItemCardProps {
+ coverFile: EnteFile;
+ TileComponent: React.FC;
+}
+
+/**
+ * A simplified variant of {@link CollectionCard}, meant to be used for
+ * representing either collections and files.
+ */
+export const ItemCard: React.FC = ({
+ coverFile,
+ TileComponent,
+}) => {
+ const [coverImageURL, setCoverImageURL] = useState("");
+
+ useEffect(() => {
+ const main = async () => {
+ const url = await downloadManager.getThumbnailForPreview(coverFile);
+ if (url) setCoverImageURL(url);
+ };
+ void main();
+ }, [coverFile]);
+
+ return (
+
+ {coverFile.metadata.hasStaticThumbnail ? (
+
+ ) : coverImageURL ? (
+
+ ) : (
+
+ )}
+
+ );
+};
diff --git a/web/apps/photos/src/components/PlaceholderThumbnails.tsx b/web/packages/new/photos/components/PlaceholderThumbnails.tsx
similarity index 100%
rename from web/apps/photos/src/components/PlaceholderThumbnails.tsx
rename to web/packages/new/photos/components/PlaceholderThumbnails.tsx