notacoder I write about coding stuff

How to use v-model with custom components in Vue 3 (v3.3)

Two-way binding with the defineModel macro

The advantage of components lies in their ability to generate reusable segments of code with a uniform design, making maintenance notably straightforward, particularly in larger projects.

For instance, let's consider the TextInput component offered by Laravel Jetstream in conjunction with Inertia, and customize it to leverage the latest defineModel macro.

Here's the current version of the component:

<script setup>
import { onMounted, ref } from 'vue';

defineProps({
    modelValue: String,
});

defineEmits(['update:modelValue']);

const input = ref(null);

onMounted(() => {
    if (input.value.hasAttribute('autofocus')) {
        input.value.focus();
    }
});

defineExpose({ focus: () => input.value.focus() });
</script>

<template>
    <input
        ref="input"
        class="border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm"
        :value="modelValue"
        @input="$emit('update:modelValue', $event.target.value)">
</template>

Time to refactor and simplify this code!

Let's see now how we can use this new macro and clean this component a little bit:

<script setup>
import { onMounted, ref } from 'vue';

const model = defineModel();

const input = ref(null);

onMounted(() => {
    if (input.value.hasAttribute('autofocus')) {
        input.value.focus();
    }
});

defineExpose({ focus: () => input.value.focus() });
</script>

<template>
    <input
        ref="input"
        class="border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm"
        :value="model">
</template>

Line 4 is where the magic happens:

const model = defineModel();

As stated in the Vue documentation:

The macro automatically registers a prop, and returns a ref that can be directly mutated

We can then simply add v-model="model" to our input element without having to worry about defining emitters or events.

That's it! For more details, head to the official documentation: LINK.

Tags: Vue JavaScript