diff --git a/frontend/src/lib/components/TagChipList.svelte b/frontend/src/lib/components/TagChipList.svelte
new file mode 100644
index 00000000..05851250
--- /dev/null
+++ b/frontend/src/lib/components/TagChipList.svelte
@@ -0,0 +1,23 @@
+
+
+{#if tags.length > 0}
+
+ {#each displayedTags as tag (tag.id)}
+ {tag.name}
+ {/each}
+ {#if hiddenTagCount > 0}
+ +{hiddenTagCount}
+ {/if}
+
+{/if}
diff --git a/frontend/src/lib/components/TagChipList.svelte.spec.ts b/frontend/src/lib/components/TagChipList.svelte.spec.ts
new file mode 100644
index 00000000..14eb2a14
--- /dev/null
+++ b/frontend/src/lib/components/TagChipList.svelte.spec.ts
@@ -0,0 +1,34 @@
+import { describe, it, expect, afterEach } from 'vitest';
+import { cleanup, render } from 'vitest-browser-svelte';
+
+import TagChipList from './TagChipList.svelte';
+
+afterEach(() => {
+ cleanup();
+});
+
+const makeTags = (n: number) =>
+ Array.from({ length: n }, (_, i) => ({ id: `t${i}`, name: `Tag${i}` }));
+
+describe('TagChipList', () => {
+ it('renders all tags as chips when under the cap', () => {
+ render(TagChipList, { tags: makeTags(2), max: 3 });
+ const chips = document.querySelectorAll('[data-testid="thumb-row-tag"]');
+ expect(chips).toHaveLength(2);
+ expect(document.body.textContent).not.toMatch(/\+/);
+ });
+
+ it('caps visible chips at max and renders +N for the remainder', () => {
+ render(TagChipList, { tags: makeTags(5), max: 3 });
+ const chips = document.querySelectorAll('[data-testid="thumb-row-tag"]');
+ expect(chips).toHaveLength(3);
+ expect(document.body.textContent).toMatch(/\+2/);
+ });
+
+ it('renders nothing when tags is empty', () => {
+ render(TagChipList, { tags: [], max: 3 });
+ const chips = document.querySelectorAll('[data-testid="thumb-row-tag"]');
+ expect(chips).toHaveLength(0);
+ expect(document.body.textContent).not.toMatch(/\+/);
+ });
+});
diff --git a/frontend/src/lib/components/ThumbnailRow.svelte b/frontend/src/lib/components/ThumbnailRow.svelte
index 700550a7..f605b0c4 100644
--- a/frontend/src/lib/components/ThumbnailRow.svelte
+++ b/frontend/src/lib/components/ThumbnailRow.svelte
@@ -1,5 +1,6 @@