diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md
index 042dc89b930e5358d8d155db8cad79becbcb33bf..987fdfa0293f1fc5ea64cb401eab4338818f9750 100644
--- a/docs/usage/configuration-options.md
+++ b/docs/usage/configuration-options.md
@@ -3849,6 +3849,16 @@ The field supports multiple URLs but it is datasource-dependent on whether only
 
 Add to this object if you wish to define rules that apply only to PRs that replace dependencies.
 
+## replacementApproach
+
+For `npm` manager when `replacementApproach=alias` then instead of replacing `"foo": "1.2.3"` with `"@my/foo": "1.2.4"` we would instead replace it with `"foo": "npm:@my/foo@1.2.4"`.
+
+```json
+{
+  "replacementApproach": "alias"
+}
+```
+
 ## respectLatest
 
 Similar to `ignoreUnstable`, this option controls whether to update to versions that are greater than the version tagged as `latest` in the repository.
diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts
index c5ad124c28110cd7f76ffe3f609b444b9a0ace06..6f20b33e45becb611e46bb82b844e9e609aa7522 100644
--- a/lib/config/options/index.ts
+++ b/lib/config/options/index.ts
@@ -1465,6 +1465,16 @@ const options: RenovateOptions[] = [
     cli: false,
     env: false,
   },
+  {
+    name: 'replacementApproach',
+    description:
+      'Select whether to perform a direct replacement or alias replacement.',
+    type: 'string',
+    stage: 'branch',
+    allowedValues: ['replace', 'alias'],
+    supportedManagers: ['npm'],
+    default: 'replace',
+  },
   {
     name: 'matchConfidence',
     description:
diff --git a/lib/modules/manager/npm/update/dependency/index.spec.ts b/lib/modules/manager/npm/update/dependency/index.spec.ts
index e02760aadc42e49db2a8803dab1a02ee46f1677f..18d871756e6d89e8530b8f00cb3b88cdbac6cc89 100644
--- a/lib/modules/manager/npm/update/dependency/index.spec.ts
+++ b/lib/modules/manager/npm/update/dependency/index.spec.ts
@@ -1,5 +1,6 @@
 import * as npmUpdater from '../..';
 import { Fixtures } from '../../../../../../test/fixtures';
+import { type Upgrade } from '../../../types';
 
 const readFixture = (x: string): string => Fixtures.get(x, '../..');
 
@@ -254,6 +255,23 @@ describe('modules/manager/npm/update/dependency/index', () => {
       expect(JSON.parse(testContent!).dependencies.abc).toBe('2.0.0');
     });
 
+    it('supports alias-based replacement', () => {
+      const upgrade: Upgrade = {
+        depType: 'dependencies',
+        depName: 'config',
+        newName: 'abc',
+        replacementApproach: 'alias',
+        newValue: '2.0.0',
+      };
+      const testContent = npmUpdater.updateDependency({
+        fileContent: input01Content,
+        upgrade,
+      });
+      expect(JSON.parse(testContent!).dependencies.config).toBe(
+        'npm:abc@2.0.0',
+      );
+    });
+
     it('replaces glob package resolutions', () => {
       const upgrade = {
         depType: 'dependencies',
diff --git a/lib/modules/manager/npm/update/dependency/index.ts b/lib/modules/manager/npm/update/dependency/index.ts
index 0d4bb218f0205e6d8be3a6e6dc3b8db16c11a2c8..c86ed856b207616680951597ef61790d954cbee4 100644
--- a/lib/modules/manager/npm/update/dependency/index.ts
+++ b/lib/modules/manager/npm/update/dependency/index.ts
@@ -161,25 +161,38 @@ export function updateDependency({
     }
 
     // TODO #22198
-    let newFileContent = replaceAsString(
-      parsedContents,
-      fileContent,
-      depType as NpmDepType,
-      depName,
-      oldVersion!,
-      newValue!,
-      overrideDepParents,
-    );
-    if (upgrade.newName) {
+    let newFileContent: string;
+    if (upgrade.newName && upgrade.replacementApproach === 'alias') {
       newFileContent = replaceAsString(
         parsedContents,
-        newFileContent,
+        fileContent,
         depType as NpmDepType,
         depName,
+        oldVersion!,
+        `npm:${upgrade.newName}@${newValue}`,
+        overrideDepParents,
+      );
+    } else {
+      newFileContent = replaceAsString(
+        parsedContents,
+        fileContent,
+        depType as NpmDepType,
         depName,
-        upgrade.newName,
+        oldVersion!,
+        newValue!,
         overrideDepParents,
       );
+      if (upgrade.newName) {
+        newFileContent = replaceAsString(
+          parsedContents,
+          newFileContent,
+          depType as NpmDepType,
+          depName,
+          depName,
+          upgrade.newName,
+          overrideDepParents,
+        );
+      }
     }
     // istanbul ignore if
     if (!newFileContent) {
diff --git a/lib/modules/manager/types.ts b/lib/modules/manager/types.ts
index 28ede22bda95183b199bf07cf8246af0ebf73a17..3ce881369af1e5fd745f2c436b090e668091913c 100644
--- a/lib/modules/manager/types.ts
+++ b/lib/modules/manager/types.ts
@@ -192,6 +192,7 @@ export interface Upgrade<T = Record<string, any>> extends PackageDependency<T> {
   registryUrls?: string[] | null;
   currentVersion?: string;
   replaceString?: string;
+  replacementApproach?: 'replace' | 'alias';
 }
 
 export interface ArtifactNotice {