How to easily make reusable forms in Vue

Reusable form code

Web applications contain a lot of forms nowadays. Many times, we have the same form layout when creating or editing something (it can be anything: user, project, todo item, product etc.). Usually, creating and editing a resource is implemented on 2 separate pages. To keep the code DRY and avoid code repetition we should try to reuse the same form component for both scenarios. Luckily, if you use Vue you can implement reusable form components easily. Let’s get started then.

Let’s create a reusable form component

We will create a simple form for creating or editing a user. For simplicity, we will only have 2 fields: email and name. Without further ado, here’s how the reusable form will look like in the end.

<template>
  <form @submit.prevent="$emit('on-submit', form)">
    <div class="form-group">
      <label for="email">Email</label>
      <input type="email" name="email" v-model="form.email" />
    </div>

    <div class="form-group">
      <label for="name">Name</label>
      <input type="text" name="name" v-model="form.name" />
    </div>

    <button type="submit">Submit</button>
  </form>
</template>

<script>
export default {
  props: {
    user: {
      type: Object,
    },
  },
  data() {
    return {
      form: {
        email: this.user?.email || "",
        name: this.user?.name || "",
      },
    };
  },
};
</script>

UserForm component has an optional user prop and contains 2 inputs. Each input has a binding to a data entry – form.email and form.name. On form submission, we will emit the custom on-submit event with the form object as an event payload. The user prop is an object and if passed it will be used to get values for the email and name inputs. If the prop is not passed, inputs will default to empty strings.

How to use reusable forms

Let’s create a root Vue App component that will be mounted in the app and also use the UserForm component. This component will import the UserForm and render it 2 times: first to show the create user scenario, and the second time to show the update user scenario.

<template>
  <div id="app">
    <h3>Create user form</h3>
    <UserForm @on-submit="createUser" />

    <h3>Edit User form</h3>
    <UserForm :user="user" @on-submit="updateUser" />
  </div>
</template>

<script>
import UserForm from "./components/UserForm";

export default {
  name: "App",
  components: { UserForm },
  data() {
    return {
      user: {
        email: "john@example.com",
        name: "John",
      },
    };
  },
  methods: {
    createUser(userForm) {
      console.log("creating", userForm);

      // call an API to create a new user
    },
    updateUser(userForm) {
      console.log("updating", userForm);

      // call an API to update the existing user
    },
  },
};
</script>

The App component has a user object (containing email and name) as part of its data. We will use this user to showcase the update user scenario. App also has 2 methods which are handlers for the custom on-submit event for the create and update form. The handler has 1 parameter and that is the userForm object which contains email and name. The first time we use UserForm component we don’t pass the user prop, only the handler for on-submit event. This is the create mode of the form. In the second example, we pass the user object as a prop which means we will use the form component in edit (or update) mode. This time, the form will be pre-filled with the values for the email and name inputs. You can check out the working example in CodeSandbox.

Reusable forms improve maintainability and code reuse

The benefits of reusing the same component for forms are better code maintainability and code reuse. You simply have to write less code when implementing or refactoring forms. In bigger forms, it will probably slightly increase the complexity, but the benefits are even greater then.

Ivan Bernatović

I am a full-stack engineer who likes to program, architect and read.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.