diff --git a/e2e/src/specs/web/duplicates.e2e-spec.ts b/e2e/src/specs/web/duplicates.e2e-spec.ts
new file mode 100644
index 0000000000..34f11cdf78
--- /dev/null
+++ b/e2e/src/specs/web/duplicates.e2e-spec.ts
@@ -0,0 +1,51 @@
+import { AssetMediaResponseDto, LoginResponseDto, updateAssets } from '@immich/sdk';
+import { expect, test } from '@playwright/test';
+import crypto from 'node:crypto';
+import { asBearerAuth, utils } from 'src/utils';
+
+test.describe('Duplicates Utility', () => {
+ let admin: LoginResponseDto;
+ let firstAsset: AssetMediaResponseDto;
+ let secondAsset: AssetMediaResponseDto;
+
+ test.beforeAll(async () => {
+ utils.initSdk();
+ await utils.resetDatabase();
+ admin = await utils.adminSetup();
+ });
+
+ test.beforeEach(async ({ context }) => {
+ [firstAsset, secondAsset] = await Promise.all([
+ utils.createAsset(admin.accessToken, { deviceAssetId: 'duplicate-a' }),
+ utils.createAsset(admin.accessToken, { deviceAssetId: 'duplicate-b' }),
+ ]);
+
+ await updateAssets(
+ {
+ assetBulkUpdateDto: {
+ ids: [firstAsset.id, secondAsset.id],
+ duplicateId: crypto.randomUUID(),
+ },
+ },
+ { headers: asBearerAuth(admin.accessToken) },
+ );
+
+ await utils.setAuthCookies(context, admin.accessToken);
+ });
+
+ test('navigates with arrow keys between duplicate preview assets', async ({ page }) => {
+ await page.goto('/utilities/duplicates');
+ await page.getByRole('button', { name: 'View' }).first().click();
+ await page.waitForSelector('#immich-asset-viewer');
+
+ const getViewedAssetId = () => new URL(page.url()).pathname.split('/').at(-1) ?? '';
+ const initialAssetId = getViewedAssetId();
+ expect([firstAsset.id, secondAsset.id]).toContain(initialAssetId);
+
+ await page.keyboard.press('ArrowRight');
+ await expect.poll(getViewedAssetId).not.toBe(initialAssetId);
+
+ await page.keyboard.press('ArrowLeft');
+ await expect.poll(getViewedAssetId).toBe(initialAssetId);
+ });
+});
diff --git a/web/src/routes/(user)/utilities/duplicates/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/utilities/duplicates/[[photos=photos]]/[[assetId=id]]/+page.svelte
index f1b9ca7614..c7c0c146ad 100644
--- a/web/src/routes/(user)/utilities/duplicates/[[photos=photos]]/[[assetId=id]]/+page.svelte
+++ b/web/src/routes/(user)/utilities/duplicates/[[photos=photos]]/[[assetId=id]]/+page.svelte
@@ -178,19 +178,7 @@
const handleFirst = () => navigateToIndex(0);
const handlePrevious = () => navigateToIndex(Math.max(duplicatesIndex - 1, 0));
- const handlePreviousShortcut = async () => {
- if ($showAssetViewer) {
- return;
- }
- await handlePrevious();
- };
const handleNext = async () => navigateToIndex(Math.min(duplicatesIndex + 1, duplicates.length - 1));
- const handleNextShortcut = async () => {
- if ($showAssetViewer) {
- return;
- }
- await handleNext();
- };
const handleLast = () => navigateToIndex(duplicates.length - 1);
const navigateToIndex = async (index: number) =>
@@ -198,10 +186,12 @@