DEV Community

Michael Synan
Michael Synan

Posted on

Nuxt3 Form with Feedback

In my previous post I demonstrated how to submit form content to Supabase so you can start building your email list. Below, I will show how to add some basic validation and user feedback to help make sure the user is submitting useful information.


If you're using a chrome-based browser, you can check out the Stackblitz embed, otherwise visit here:

The final version will look like this:

Nuxt3 form with validation


This form will use two components, the signup form itself, as well as a feedback component which receives a prop from the form indicating what type of feedback to provide upon hitting the "submit" button and performing basic validation.

 

Let's go. 🏃‍♀️💨

 

Nuxt3 Template

<template>
  <div>
    <div class="flex items-center justify-center h-screen bg-black">
      <div
        class="text-center text-white p-6 border-2 border-white w-full max-w-md"
      >
        <h1 class="text-2xl mb-3">Join Our Newsletter</h1>
        <p class="mb-6 text-lg">Get amazing updates right to your inbox.</p>
        <form @submit.prevent="submitEmail" class="flex flex-col">
          <label class="mb-4 text-left text-stone-400">
            Name
            <input
              v-model="name"
              type="text"
              class="bg-gray-800 text-white w-full px-3 py-2 mt-2"
            />
          </label>
          <label class="mb-4 text-left text-stone-400">
            Email
            <input
              v-model="email"
              type="email"
              class="bg-gray-800 text-white w-full px-3 py-2 mt-2"
            />
          </label>
          <label class="flex items-center text-white mb-4">
            <input
              type="checkbox"
              v-model="consent"
              class="mr-3"
              id="updates"
            />
            I agree to terms and conditions
          </label>
          <button
            type="submit"
            class="bg-white text-black px-6 py-2 mt-4"
            @click.prevent="submitForm"
          >
            {{ isLoading ? 'Loading...' : 'Submit' }}
          </button>
          <feedback :formFeedback="formFeedback" />
        </form>
      </div>
    </div>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

Note: We will be using the Nuxt Tailwind module for styling.

The form has 3 input fields and a submit button. Each input is modeled to a ref value and the form submit button is linked to a submitForm function.

Additionally, the button text is responsive to the isLoading value, which we will handle in the script. Finally, you can see there is the 'feedback' component which accepts the formFeedback ref value as a prop.
 

Nuxt3 Script Section

<script lang="ts" setup>
import { ref } from 'vue';

type FormFeedbackType = 'incomplete' | 'consent' | 'invalid' | 'success' | null;

const name = ref('');
const email = ref('');
const consent = ref(false);
const isLoading = ref(false);
const formFeedback: Ref<FormFeedbackType> = ref(null);
const success = ref(true);

const submitForm = async () => {
  isLoading.value = true;
  formFeedback.value = null;

  if (!name.value.trim() || !email.value.trim()) {
    formFeedback.value = 'incomplete';
    isLoading.value = false;
    return;
  }

  const regex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i;
  if (email.value && !regex.test(email.value)) {
    formFeedback.value = 'invalid';
    success.value = false;
    isLoading.value = false;
    return;
  }

  if (!consent.value) {
    formFeedback.value = 'consent';
    success.value = false;
    isLoading.value = false;
    return;
  }

  setTimeout(() => {
    // If the execution reaches here, it means that all checks have passed.
    success.value = true;
    formFeedback.value = 'success';
    isLoading.value = false;
  }, 1000);
};
</script>
Enter fullscreen mode Exit fullscreen mode

The contact form component script includes ref values for each form field and state that is required for the form to function. Underneath the refs the async submitForm function which handles the validation and submission logic. In our case we are not submitting form information to an actual database so the submission status is simulated by using a setTimeout function.

As an bonus I created a new type for the formFeedback ref which only allows certain strings to get passed to the feedback component. Anything other than the options defined in the type will result in a TypeScript compile-time error.
 

Form Feedback Component

The feedback component accepts a single prop, named 'feedbackType', which determines the type of feedback to be displayed. Based on the value of 'feedbackType', the component renders the corresponding div. The 'feedbackType' prop can only accept specific strings, as defined by the TypeScript type associated with the formFeedback ref.

<script lang="ts" setup>
const props = defineProps({
  formFeedback: String,
});
</script>

<template>
  <div
    v-if="formFeedback === 'error'"
    class="text-white w-full block p-3 mt-4 bg-red-500"
  >
    There was an error processing your request.
  </div>

  <div
    v-else-if="formFeedback === 'success'"
    class="text-white w-full block p-3 mt-4 bg-green-500"
  >
    Thanks for signing up!
  </div>

  <div
    v-else-if="formFeedback === 'incomplete'"
    class="text-white w-full block p-3 mt-4 bg-red-500"
  >
    Please complete all required fields.
  </div>

  <div
    v-else-if="formFeedback === 'invalid'"
    class="text-white w-full block p-3 mt-4 bg-red-500"
  >
    Please enter a valid email address.
  </div>

  <div
    v-else-if="formFeedback === 'consent'"
    class="text-white w-full block p-3 mt-4 bg-red-500"
  >
    Please agree to terms and conditions.
  </div>
</template>

<style scoped></style>
Enter fullscreen mode Exit fullscreen mode

This feedback component can be reused throughout your website for different forms - just make sure you update it to include different conditions.
 

App.vue

Maybe obvious but to be thorough, simply add the contact component to your app.vue or desired page:

<template>
  <div>
    <contact />
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

Conclusion

 

That's it! You can now build a contact form with validation and feedback. 🎉

 

You can get the full code here - simply fork the project and start working on your own as needed.

Top comments (2)

Collapse
 
cmcnicholas profile image
Craig McNicholas

This looks broken <form @submit.prevent="submitEmail" class="flex flex-col">

submitEmail isn't defined

Collapse
 
michaelsynan profile image
Michael Synan

Apologies, this form doesn't actually submit emails—you need to connect it to Supabase for that. It is, however, set up for submission and error handling.