← Back to all snippets
JAVASCRIPT

Vue 3 Custom Composable for Reusable Form Input Logic

Learn to create a custom composable in Vue 3 to encapsulate and reuse common logic, such as form input handling with validation, across multiple components, promoting cleaner code.

// composables/useInputField.js
import { ref, computed } from 'vue';

export function useInputField(initialValue = '', validationFn = (val) => true) {
  const value = ref(initialValue);
  const touched = ref(false);

  const isValid = computed(() => validationFn(value.value));
  const hasError = computed(() => touched.value && !isValid.value);

  const onInput = (event) => {
    value.value = event.target.value;
  };

  const onBlur = () => {
    touched.value = true;
  };

  return {
    value,
    isValid,
    hasError,
    onInput,
    onBlur,
    touched
  };
}

// components/MyForm.vue
<template>
  <form @submit.prevent="submitForm">
    <div>
      <label for="name">Name:</label>
      <input
        type="text"
        id="name"
        :value="nameField.value"
        @input="nameField.onInput"
        @blur="nameField.onBlur"
      />
      <p v-if="nameField.hasError" style="color: red;">Name cannot be empty.</p>
    </div>

    <div>
      <label for="age">Age:</label>
      <input
        type="number"
        id="age"
        :value="ageField.value"
        @input="ageField.onInput"
        @blur="ageField.onBlur"
      />
      <p v-if="ageField.hasError" style="color: red;">Age must be at least 18.</p>
    </div>

    <button type="submit" :disabled="!isFormValid">Submit</button>
  </form>
</template>

<script setup>
import { computed } from 'vue';
import { useInputField } from '../composables/useInputField';

// Validation functions
const isNotEmpty = (val) => val.trim() !== '';
const isAdult = (val) => parseInt(val) >= 18;

// Use the custom composable for each input
const nameField = useInputField('', isNotEmpty);
const ageField = useInputField(0, isAdult);

const isFormValid = computed(() => nameField.isValid.value && ageField.isValid.value);

const submitForm = () => {
  // Manually mark all fields as touched on submit attempt
  nameField.touched.value = true;
  ageField.touched.value = true;

  if (isFormValid.value) {
    alert(`Form submitted! Name: ${nameField.value.value}, Age: ${ageField.value.value}`);
    // Reset form or other logic
  } else {
    alert('Please correct the form errors.');
  }
};
</script>
How it works: This snippet demonstrates creating and using a custom composable, `useInputField`, in Vue 3. This composable encapsulates the logic for managing an input's value, `touched` state, and validation status. It can be reused across different form fields, making form handling more modular and reducing code duplication. Components use this composable, passing in specific validation functions, and reactively display error messages based on its returned properties.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs