How to prevent Chrome form auto fill on Vue?
Tags: vue, javascript, chrome, autofill
Published: 2020-12-05 12:21:00

When you logged in website, Chrome ask that do you want to save credentials for next visiting. If you accept that, Chrome saves your password then fill every password field(fields which types set as password) with your password on the web site. 

This is expected behavior of this feature but sometimes comes annoying to users. For example in web site you have a field for API secret key and you decided to make that field type as password for security then Chrome thinks, this is login form and filling with your credential to form automatically. Today I will show how to prevent this behavior.

I made researches about how to prevent this behavior but answers or libraries I found for this behavior won't worked. So I decided to make my implementation and share with it.

## Reason of That Behavior

As I mentioned before when form has password field and you saved your credentials to Chrome for that web site, Chrome things that, you are attempting to login this web site and fills every password field automatically.

## Solution

Solution I found is very simple. I will implement with Vue on article but I think it could be easily implemented with React as well. My solution has 3 steps;

- Set`type='text'` and add `autocomplete="off"` to all inputs in form for initial render.
- When user focused password field, change field type as `password`
- If user blur the input with empty value change input type as text.

## Implementation in Vue

I will pass through application setup steps. I created to vue 2 app with default settings. Then added to simple login form on `src/App.vue` file shown below. 

```vue
<template>
  <div id="app">
    <div v-if="isloggedin" class="welcome">
      Welcome {{username}}
    </div>
    <div v-else id="form-wrapper">
      <label for="username">Username: </label>
      <input
        v-model="username" 
        class="form-input" 
        type="text" 
        name="username" 
        value=""
      />
      <label for="password">Password: </label>
      <input 
        v-model="password"
        class="form-input"
        type="password"
        name="password"
        value=""
      />
      <button class="block" type="button" @click="saveCredentials">
        Submit Form
      </button>
    </div>
  </div>
</template>

<script>

export default {
  name: 'App',
  data() {
    return {
      username: '',
      password: '',
      isloggedin: false
    }
  },
  methods: {
    saveCredentials() {
      this.isloggedin = true;
    }
  }
}
</script>

<style>
#app {
  display: flex;
  flex: 1;
  height: 100vh;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}

#form-wrapper {
  flex-shrink: 1;
  display:flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
}

.form-input {
  margin: 4px 0px;
}

.block {
  width: 100%;
}
</style>
```
Assume that we visited and login this web site before and saved credentials to Chrome. On next visit to site the login form will looks like shown below.  

![before-solution](https://dev-to-uploads.s3.amazonaws.com/i/dvc470av5doz2gaketh3.png)

To prevent this behavior, firstly we need to set input types as reactive and hold it in state. For initial render we need to set as `text` to input type.

```html
<input 
  v-model="password"
  class="form-input"
  :type="fieldTypes.password" // change this as reactive
  name="password"
  value=""
/>
```

```js
data() {
  return {
    username: '',
    password: '',
    isloggedin: false,
    fieldTypes: { // add this for change input type
      password: 'text',
    }
  }
},
```

Then need to add @focus and @blur events to password input and trigger callback function for that events.

```html
<input 
  v-model="password"
  class="form-input"
  :type="fieldTypes.password"
  name="password"
  value=""
  @focus="handleType"
  @blur="handleType"
/>
```

```js
handleType(event) {
  const { srcElement, type } = event;
  const { name, value } = srcElement;

  if(type === 'blur' && !value) {
    this.fieldTypes[name] = 'text'
  } else {
    this.fieldTypes[name] = 'password'
  }
}
```

Lastly to prevent chrome suggestions we need to add `autocomplete="off"` to all inputs. You could add `autocomplete="new-password"` to password field for strong password suggestions from browser. The final password field code looks like shown above.

```html
<input 
  v-model="password"
  class="form-input"
  :type="fieldTypes.password"
  name="password"
  value=""
  @focus="handleType"
  @blur="handleType"
  autocomplete="off"
/>
```

After all the final version of `App.vue` looks like this.
```vue
<template>
  <div id="app">
    <div v-if="isloggedin" class="welcome">
      Welcome {{username}}
    </div>
    <div v-else id="form-wrapper">
      <label for="username">Username: </label>
      <input
        v-model="username" 
        class="form-input" 
        type="text" 
        name="username" 
        value=""
        autocomplete="off"
      />
      <label for="password">Password: </label>
      <input 
        v-model="password"
        class="form-input"
        :type="fieldTypes.password"
        name="password"
        value=""
        @focus="handleType"
        @blur="handleType"
        autocomplete="off"
      />
      <button class="block" type="button" @click="saveCredentials">
        Submit Form
      </button>
    </div>
  </div>
</template>

<script>

export default {
  name: 'App',
  data() {
    return {
      username: '',
      password: '',
      isloggedin: false,
      fieldTypes: {
        password: 'text',
      }
    }
  },
  methods: {
    saveCredentials() {
      this.isloggedin = true;
    },
    handleType(event) {
      const { srcElement, type } = event;
      const { name, value } = srcElement;

      if(type === 'blur' && !value) {
        this.fieldTypes[name] = 'text'
      } else {
        this.fieldTypes[name] = 'password'
      }
    }
  }
}
</script>

<style>
#app {
  display: flex;
  flex: 1;
  height: 100vh;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}

#form-wrapper {
  flex-shrink: 1;
  display:flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
}

.form-input {
  margin: 4px 0px;
}

.block {
  width: 100%;
}
</style>
```