Why is this an issue?

Vue merges the properties declared in a component’s props, data, computed, methods, and setup blocks onto a single instance namespace, so the component and its template can access any of them the same way (for example this.name or {{ name }}). If the same name is declared in more than one of these blocks, only one of them survives, and which one wins depends on Vue’s internal merge order rather than anything visible in the source. In development, Vue detects most of these collisions and logs a console warning naming the conflicting option groups, but that check is stripped out of production builds, so the overwrite still happens silently there.

This rule also checks <script setup> components: it flags a prop declared with defineProps() (including through withDefaults()) that shares its name with another top-level binding declared in the same <script setup> block. Vue does not emit a runtime warning for this case; the local binding silently takes precedence over the prop when the template is compiled.

To fix this, rename one of the colliding declarations so that each name appears in only one place.

Code examples

Noncompliant code example

<script>
export default {
  props: {
    name: String
  },
  data() {
    return {
      name: null // Noncompliant: "name" is already declared as a prop
    };
  },
  methods: {
    name() {} // Noncompliant: "name" is already declared as a prop
  }
}
</script>

Compliant solution

<script>
export default {
  props: {
    name: String
  },
  data() {
    return {
      fullName: null
    };
  },
  methods: {
    formatName() {}
  }
}
</script>

Noncompliant code example

<script setup>
const props = defineProps(['name']);

const name = ref(''); // Noncompliant: "name" collides with the "name" prop
</script>

Compliant solution

<script setup>
const props = defineProps(['name']);

const displayName = ref('');
</script>

Resources

Documentation