diff --git a/docs/rules/index.md b/docs/rules/index.md
index 4e2c26dee..71a05d42e 100644
--- a/docs/rules/index.md
+++ b/docs/rules/index.md
@@ -277,6 +277,7 @@ For example:
 | [vue/sort-keys](./sort-keys.md) | enforce sort-keys in a manner that is compatible with order-in-components |  | :hammer: |
 | [vue/static-class-names-order](./static-class-names-order.md) | enforce static class names order | :wrench: | :hammer: |
 | [vue/v-for-delimiter-style](./v-for-delimiter-style.md) | enforce `v-for` directive's delimiter style | :wrench: | :lipstick: |
+| [vue/v-if-else-key](./v-if-else-key.md) | require key attribute for conditionally rendered repeated components | :wrench: | :warning: |
 | [vue/v-on-handler-style](./v-on-handler-style.md) | enforce writing style for handlers in `v-on` directives | :wrench: | :hammer: |
 | [vue/valid-define-options](./valid-define-options.md) | enforce valid `defineOptions` compiler macro |  | :warning: |
 
diff --git a/docs/rules/require-v-for-key.md b/docs/rules/require-v-for-key.md
index c8f894ff5..7699e1b30 100644
--- a/docs/rules/require-v-for-key.md
+++ b/docs/rules/require-v-for-key.md
@@ -5,6 +5,7 @@ title: vue/require-v-for-key
 description: require `v-bind:key` with `v-for` directives
 since: v3.0.0
 ---
+
 # vue/require-v-for-key
 
 > require `v-bind:key` with `v-for` directives
@@ -20,12 +21,9 @@ This rule reports the elements which have `v-for` and do not have `v-bind:key` w
 ```vue
 <template>
   <!-- ✓ GOOD -->
-  <div
-    v-for="todo in todos"
-    :key="todo.id"
-  />
+  <div v-for="todo in todos" :key="todo.id" />
   <!-- ✗ BAD -->
-  <div v-for="todo in todos"/>
+  <div v-for="todo in todos" />
 </template>
 ```
 
@@ -43,8 +41,10 @@ Nothing.
 ## :couple: Related Rules
 
 - [vue/valid-v-for]
+- [vue/v-if-else-key]
 
 [vue/valid-v-for]: ./valid-v-for.md
+[vue/v-if-else-key]: ./v-if-else-key.md
 
 ## :books: Further Reading
 
diff --git a/docs/rules/v-if-else-key.md b/docs/rules/v-if-else-key.md
new file mode 100644
index 000000000..11c8c642a
--- /dev/null
+++ b/docs/rules/v-if-else-key.md
@@ -0,0 +1,56 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/v-if-else-key
+description: require key attribute for conditionally rendered repeated components
+---
+
+# vue/v-if-else-key
+
+> require key attribute for conditionally rendered repeated components
+
+- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+
+## :book: Rule Details
+
+This rule checks for components that are both repeated and conditionally rendered within the same scope. If such a component is found, the rule then checks for the presence of a 'key' directive. If the 'key' directive is missing, the rule issues a warning and offers a fix.
+
+This rule is not required in Vue 3, as the key is automatically assigned to the elements.
+
+<eslint-code-block fix :rules="{'vue/v-if-else-key': ['error']}">
+
+```vue
+<template>
+  <!-- ✓ GOOD -->
+  <my-component v-if="condition1" :key="one" />
+  <my-component v-else-if="condition2" :key="two" />
+  <my-component v-else :key="three" />
+
+  <!-- ✗ BAD -->
+  <my-component v-if="condition1" />
+  <my-component v-else-if="condition2" />
+  <my-component v-else />
+</template>
+```
+
+</eslint-code-block>
+
+## :wrench: Options
+
+Nothing.
+
+## :couple: Related Rules
+
+- [vue/require-v-for-key]
+
+[vue/require-v-for-key]: ./require-v-for-key.md
+
+## :books: Further Reading
+
+- [Guide (for v2) - v-if without key](https://v2.vuejs.org/v2/style-guide/#v-if-v-else-if-v-else-without-key-use-with-caution)
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/v-if-else-key.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/v-if-else-key.js)
diff --git a/lib/index.js b/lib/index.js
index 0f3dcba27..3497a7b4f 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -216,6 +216,7 @@ module.exports = {
     'use-v-on-exact': require('./rules/use-v-on-exact'),
     'v-bind-style': require('./rules/v-bind-style'),
     'v-for-delimiter-style': require('./rules/v-for-delimiter-style'),
+    'v-if-else-key': require('./rules/v-if-else-key'),
     'v-on-event-hyphenation': require('./rules/v-on-event-hyphenation'),
     'v-on-function-call': require('./rules/v-on-function-call'),
     'v-on-handler-style': require('./rules/v-on-handler-style'),
diff --git a/lib/rules/v-if-else-key.js b/lib/rules/v-if-else-key.js
new file mode 100644
index 000000000..0a6ec9cfe
--- /dev/null
+++ b/lib/rules/v-if-else-key.js
@@ -0,0 +1,285 @@
+/**
+ * @author Felipe Melendez
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+// =============================================================================
+// Requirements
+// =============================================================================
+
+const utils = require('../utils')
+const casing = require('../utils/casing')
+
+// =============================================================================
+// Rule Helpers
+// =============================================================================
+
+/**
+ * A conditional family is made up of a group of repeated components that are conditionally rendered
+ * using v-if, v-else-if, and v-else.
+ *
+ * @typedef {Object} ConditionalFamily
+ * @property {VElement} if - The node associated with the 'v-if' directive.
+ * @property {VElement[]} elseIf - An array of nodes associated with 'v-else-if' directives.
+ * @property {VElement | null} else - The node associated with the 'v-else' directive, or null if there isn't one.
+ */
+
+/**
+ * Checks for the presence of a 'key' attribute in the given node. If the 'key' attribute is missing
+ * and the node is part of a conditional family a report is generated.
+ * The fix proposed adds a unique key based on the component's name and count,
+ * following the format '${kebabCase(componentName)}-${componentCount}', e.g., 'some-component-2'.
+ *
+ * @param {VElement} node - The Vue component node to check for a 'key' attribute.
+ * @param {RuleContext} context - The rule's context object, used for reporting.
+ * @param {string} componentName - Name of the component.
+ * @param {string} uniqueKey - A unique key for the repeated component, used for the fix.
+ * @param {Map<VElement, ConditionalFamily>} conditionalFamilies - Map of conditionally rendered components and their respective conditional directives.
+ */
+const checkForKey = (
+  node,
+  context,
+  componentName,
+  uniqueKey,
+  conditionalFamilies
+) => {
+  if (node.parent && node.parent.type === 'VElement') {
+    const conditionalFamily = conditionalFamilies.get(node.parent)
+
+    if (
+      conditionalFamily &&
+      (utils.hasDirective(node, 'bind', 'key') ||
+        utils.hasAttribute(node, 'key') ||
+        !hasConditionalDirective(node) ||
+        !(conditionalFamily.else || conditionalFamily.elseIf.length > 0))
+    ) {
+      return
+    }
+
+    context.report({
+      node: node.startTag,
+      loc: node.startTag.loc,
+      messageId: 'requireKey',
+      data: {
+        componentName
+      },
+      fix(fixer) {
+        const afterComponentNamePosition =
+          node.startTag.range[0] + componentName.length + 1
+        return fixer.insertTextBeforeRange(
+          [afterComponentNamePosition, afterComponentNamePosition],
+          ` key="${uniqueKey}"`
+        )
+      }
+    })
+  }
+}
+
+/**
+ * Checks for the presence of conditional directives in the given node.
+ *
+ * @param {VElement} node - The node to check for conditional directives.
+ * @returns {boolean} Returns true if a conditional directive is found in the node or its parents,
+ *   false otherwise.
+ */
+const hasConditionalDirective = (node) =>
+  utils.hasDirective(node, 'if') ||
+  utils.hasDirective(node, 'else-if') ||
+  utils.hasDirective(node, 'else')
+
+// =============================================================================
+// Rule Definition
+// =============================================================================
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    type: 'problem',
+    docs: {
+      description:
+        'require key attribute for conditionally rendered repeated components',
+      categories: null,
+      recommended: false,
+      url: 'https://eslint.vuejs.org/rules/v-if-else-key.html'
+    },
+    fixable: 'code',
+    schema: [],
+    messages: {
+      requireKey:
+        "Conditionally rendered repeated component '{{componentName}}' expected to have a 'key' attribute."
+    }
+  },
+  /**
+   * Creates and returns a rule object which checks usage of repeated components. If a component
+   * is used more than once, it checks for the presence of a key.
+   *
+   * @param {RuleContext} context - The context object.
+   * @returns {Object} A dictionary of functions to be called on traversal of the template body by
+   *   the eslint parser.
+   */
+  create(context) {
+    /**
+     * Map to store conditionally rendered components and their respective conditional directives.
+     *
+     * @type {Map<VElement, ConditionalFamily>}
+     */
+    const conditionalFamilies = new Map()
+
+    /**
+     * Array of Maps to keep track of components and their usage counts along with the first
+     * node instance. Each Map represents a different scope level, and maps a component name to
+     * an object containing the count and a reference to the first node.
+     */
+    /** @type {Map<string, { count: number; firstNode: any }>[]} */
+    const componentUsageStack = [new Map()]
+
+    /**
+     * Checks if a given node represents a custom component without any conditional directives.
+     *
+     * @param {VElement} node - The AST node to check.
+     * @returns {boolean} True if the node represents a custom component without any conditional directives, false otherwise.
+     */
+    const isCustomComponentWithoutCondition = (node) =>
+      node.type === 'VElement' &&
+      utils.isCustomComponent(node) &&
+      !hasConditionalDirective(node)
+
+    /** Set of built-in Vue components that are exempt from the rule. */
+    /** @type {Set<string>} */
+    const exemptTags = new Set(['component', 'slot', 'template'])
+
+    /** Set to keep track of nodes we've pushed to the stack. */
+    /** @type {Set<any>} */
+    const pushedNodes = new Set()
+
+    /**
+     * Creates and returns an object representing a conditional family.
+     *
+     * @param {VElement} ifNode - The VElement associated with the 'v-if' directive.
+     * @returns {ConditionalFamily}
+     */
+    const createConditionalFamily = (ifNode) => ({
+      if: ifNode,
+      elseIf: [],
+      else: null
+    })
+
+    return utils.defineTemplateBodyVisitor(context, {
+      /**
+       * Callback to be executed when a Vue element is traversed. This function checks if the
+       * element is a component, increments the usage count of the component in the
+       * current scope, and checks for the key directive if the component is repeated.
+       *
+       * @param {VElement} node - The traversed Vue element.
+       */
+      VElement(node) {
+        if (exemptTags.has(node.rawName)) {
+          return
+        }
+
+        const condition =
+          utils.getDirective(node, 'if') ||
+          utils.getDirective(node, 'else-if') ||
+          utils.getDirective(node, 'else')
+
+        if (condition) {
+          const conditionType = condition.key.name.name
+
+          if (node.parent && node.parent.type === 'VElement') {
+            let conditionalFamily = conditionalFamilies.get(node.parent)
+
+            if (conditionType === 'if' && !conditionalFamily) {
+              conditionalFamily = createConditionalFamily(node)
+              conditionalFamilies.set(node.parent, conditionalFamily)
+            }
+
+            if (conditionalFamily) {
+              switch (conditionType) {
+                case 'else-if': {
+                  conditionalFamily.elseIf.push(node)
+                  break
+                }
+                case 'else': {
+                  conditionalFamily.else = node
+                  break
+                }
+              }
+            }
+          }
+        }
+
+        if (isCustomComponentWithoutCondition(node)) {
+          componentUsageStack.push(new Map())
+          return
+        }
+
+        if (!utils.isCustomComponent(node)) {
+          return
+        }
+
+        const componentName = node.rawName
+        const currentScope = componentUsageStack[componentUsageStack.length - 1]
+        const usageInfo = currentScope.get(componentName) || {
+          count: 0,
+          firstNode: null
+        }
+
+        if (hasConditionalDirective(node)) {
+          // Store the first node if this is the first occurrence
+          if (usageInfo.count === 0) {
+            usageInfo.firstNode = node
+          }
+
+          if (usageInfo.count > 0) {
+            const uniqueKey = `${casing.kebabCase(componentName)}-${
+              usageInfo.count + 1
+            }`
+            checkForKey(
+              node,
+              context,
+              componentName,
+              uniqueKey,
+              conditionalFamilies
+            )
+
+            // If this is the second occurrence, also apply a fix to the first occurrence
+            if (usageInfo.count === 1) {
+              const uniqueKeyForFirstInstance = `${casing.kebabCase(
+                componentName
+              )}-1`
+              checkForKey(
+                usageInfo.firstNode,
+                context,
+                componentName,
+                uniqueKeyForFirstInstance,
+                conditionalFamilies
+              )
+            }
+          }
+          usageInfo.count += 1
+          currentScope.set(componentName, usageInfo)
+        }
+        componentUsageStack.push(new Map())
+        pushedNodes.add(node)
+      },
+
+      'VElement:exit'(node) {
+        if (exemptTags.has(node.rawName)) {
+          return
+        }
+        if (isCustomComponentWithoutCondition(node)) {
+          componentUsageStack.pop()
+          return
+        }
+        if (!utils.isCustomComponent(node)) {
+          return
+        }
+        if (pushedNodes.has(node)) {
+          componentUsageStack.pop()
+          pushedNodes.delete(node)
+        }
+      }
+    })
+  }
+}
diff --git a/tests/lib/rules/v-if-else-key.js b/tests/lib/rules/v-if-else-key.js
new file mode 100644
index 000000000..4aab257d0
--- /dev/null
+++ b/tests/lib/rules/v-if-else-key.js
@@ -0,0 +1,429 @@
+/**
+ * @author Felipe Melendez
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+const RuleTester = require('eslint').RuleTester
+const rule = require('../../../lib/rules/v-if-else-key')
+
+const tester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: {
+    ecmaVersion: 2020,
+    sourceType: 'module'
+  }
+})
+
+tester.run('v-if-else-key', rule, {
+  valid: [
+    {
+      filename: 'test.vue',
+      code: `
+        <template>
+          <div>
+            <CustomComponent v-if="some-condition" key="key1" />
+            <CustomComponent v-else key="key2" />
+          </div>
+        </template>
+        <script>
+        export default {
+            components: {
+                CustomComponent
+            }
+        }
+        </script>
+        `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <template>
+    <div>
+      <CustomComponent v-if="some-condition" />
+      <CustomComponent />
+    </div>
+        </template>
+        <script>
+        export default {
+            components: {
+                CustomComponent
+            }
+        }
+        </script>
+        `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <template>
+          <div>
+            <div v-if="some-condition" >
+              <CustomComponent />
+            </div>
+            <CustomComponent v-else />
+          </div>
+        </template>
+        <script>
+        export default {
+            components: {
+                CustomComponent
+            }
+        }
+        </script>
+        `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <template>
+          <div>
+            <slot v-if="some-condition" />
+            <slot v-else />
+          </div>
+        </template>
+        <script>
+        export default {
+            components: {
+                CustomComponent
+            }
+        }
+        </script>
+        `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <template>
+          <div>
+            <div v-if="someCondition" />
+            <div v-else-if="otherCondition" />
+            <div v-else />
+          </div>
+        </template>
+        <script>
+        export default {
+            components: {
+                CustomComponent
+            }
+        }
+        </script>
+        `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <template>
+          <div>
+            <div v-if="foo" />
+            <span v-else />
+          </div>
+        </template>
+        <script>
+        export default {
+            components: {
+                CustomComponent
+            }
+        }
+        </script>
+        `
+    }
+  ],
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: `
+        <template>
+          <div>
+            <OuterComponent v-if="showOuterComponent">
+              <InnerComponent v-if="showInnerComponent1" />
+              <InnerComponent v-else />
+            </OuterComponent>
+            <OuterComponent v-else>
+              <InnerComponent :key="key1" />
+              <InnerComponent :key="key2" />
+            </OuterComponent>
+          </div>
+        </template>
+        <script>
+        export default {
+          components: {
+            OuterComponent,
+            InnerComponent
+          }
+        }
+        </script>
+        `,
+      output: `
+        <template>
+          <div>
+            <OuterComponent key="outer-component-1" v-if="showOuterComponent">
+              <InnerComponent key="inner-component-1" v-if="showInnerComponent1" />
+              <InnerComponent key="inner-component-2" v-else />
+            </OuterComponent>
+            <OuterComponent key="outer-component-2" v-else>
+              <InnerComponent :key="key1" />
+              <InnerComponent :key="key2" />
+            </OuterComponent>
+          </div>
+        </template>
+        <script>
+        export default {
+          components: {
+            OuterComponent,
+            InnerComponent
+          }
+        }
+        </script>
+        `,
+      errors: [
+        {
+          message:
+            "Conditionally rendered repeated component 'OuterComponent' expected to have a 'key' attribute.",
+          line: 4
+        },
+        {
+          message:
+            "Conditionally rendered repeated component 'InnerComponent' expected to have a 'key' attribute.",
+          line: 5
+        },
+        {
+          message:
+            "Conditionally rendered repeated component 'InnerComponent' expected to have a 'key' attribute.",
+          line: 6
+        },
+        {
+          message:
+            "Conditionally rendered repeated component 'OuterComponent' expected to have a 'key' attribute.",
+          line: 8
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <template>
+          <div>
+            <OuterComponent v-if="showOuterComponent">
+              <InnerComponent v-if="showInnerComponent1">
+                <DifferentScopeComponent />
+              </InnerComponent>
+              <InnerComponent v-else />
+            </OuterComponent>
+            <OuterComponent v-else>
+              <InnerComponent />
+              <InnerComponent />
+            </OuterComponent>
+            <DifferentScopeComponent />
+          </div>
+        </template>
+        <script>
+        export default {
+          components: {
+            OuterComponent,
+            InnerComponent
+          }
+        }
+        </script>
+        `,
+      output: `
+        <template>
+          <div>
+            <OuterComponent key="outer-component-1" v-if="showOuterComponent">
+              <InnerComponent key="inner-component-1" v-if="showInnerComponent1">
+                <DifferentScopeComponent />
+              </InnerComponent>
+              <InnerComponent key="inner-component-2" v-else />
+            </OuterComponent>
+            <OuterComponent key="outer-component-2" v-else>
+              <InnerComponent />
+              <InnerComponent />
+            </OuterComponent>
+            <DifferentScopeComponent />
+          </div>
+        </template>
+        <script>
+        export default {
+          components: {
+            OuterComponent,
+            InnerComponent
+          }
+        }
+        </script>
+        `,
+      errors: [
+        {
+          message:
+            "Conditionally rendered repeated component 'OuterComponent' expected to have a 'key' attribute.",
+          line: 4
+        },
+        {
+          message:
+            "Conditionally rendered repeated component 'InnerComponent' expected to have a 'key' attribute.",
+          line: 5
+        },
+        {
+          message:
+            "Conditionally rendered repeated component 'InnerComponent' expected to have a 'key' attribute.",
+          line: 8
+        },
+        {
+          message:
+            "Conditionally rendered repeated component 'OuterComponent' expected to have a 'key' attribute.",
+          line: 10
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <template>
+          <div>
+            <OuterComponent v-if="showOuterComponent">
+              <InnerComponent v-if="showInnerComponent1" />
+              <InnerComponent v-else />
+            </OuterComponent>
+            <InnerComponent v-else />
+          </div>
+        </template>
+        <script>
+        export default {
+          components: {
+            OuterComponent,
+            InnerComponent
+          }
+        }
+        </script>
+        `,
+      output: `
+        <template>
+          <div>
+            <OuterComponent v-if="showOuterComponent">
+              <InnerComponent key="inner-component-1" v-if="showInnerComponent1" />
+              <InnerComponent key="inner-component-2" v-else />
+            </OuterComponent>
+            <InnerComponent v-else />
+          </div>
+        </template>
+        <script>
+        export default {
+          components: {
+            OuterComponent,
+            InnerComponent
+          }
+        }
+        </script>
+        `,
+      errors: [
+        {
+          message:
+            "Conditionally rendered repeated component 'InnerComponent' expected to have a 'key' attribute.",
+          line: 5
+        },
+        {
+          message:
+            "Conditionally rendered repeated component 'InnerComponent' expected to have a 'key' attribute.",
+          line: 6
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <template>
+          <div>
+            <ComponentA v-if="foo" />
+            <ComponentA v-else-if="bar" />
+            <ComponentB v-else />
+          </div>
+        </template>
+        <script>
+        export default {
+          components: {
+            ComponentA,
+            ComponentB
+          }
+        }
+        </script>
+        `,
+      output: `
+        <template>
+          <div>
+            <ComponentA key="component-a-1" v-if="foo" />
+            <ComponentA key="component-a-2" v-else-if="bar" />
+            <ComponentB v-else />
+          </div>
+        </template>
+        <script>
+        export default {
+          components: {
+            ComponentA,
+            ComponentB
+          }
+        }
+        </script>
+        `,
+      errors: [
+        {
+          message:
+            "Conditionally rendered repeated component 'ComponentA' expected to have a 'key' attribute.",
+          line: 4
+        },
+        {
+          message:
+            "Conditionally rendered repeated component 'ComponentA' expected to have a 'key' attribute.",
+          line: 5
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <template>
+          <div>
+            <ComponentA v-if="foo" />
+            <ComponentB v-else-if="bar" />
+            <ComponentA v-else />
+          </div>
+        </template>
+        <script>
+        export default {
+          components: {
+            ComponentA,
+            ComponentB
+          }
+        }
+        </script>
+        `,
+      output: `
+        <template>
+          <div>
+            <ComponentA key="component-a-1" v-if="foo" />
+            <ComponentB v-else-if="bar" />
+            <ComponentA key="component-a-2" v-else />
+          </div>
+        </template>
+        <script>
+        export default {
+          components: {
+            ComponentA,
+            ComponentB
+          }
+        }
+        </script>
+        `,
+      errors: [
+        {
+          message:
+            "Conditionally rendered repeated component 'ComponentA' expected to have a 'key' attribute.",
+          line: 4
+        },
+        {
+          message:
+            "Conditionally rendered repeated component 'ComponentA' expected to have a 'key' attribute.",
+          line: 6
+        }
+      ]
+    }
+  ]
+})