Vue.js
Email checker for Vue.js. Real-time email verification in Vue components and forms.
Integrate EmailVerify into your Vue 3 applications for real-time email validation. This guide covers both Composition API and Options API implementations, from basic setup to advanced patterns.
Installation
Install the EmailVerify package and configure your project.
npm install emailverifyyarn add emailverifypnpm add emailverifyQuick Start
Composition API
Use the Composition API for reactive email verification in your components.
<template>
<div class="email-verification">
<input
v-model="email"
type="email"
placeholder="Enter email address"
@blur="verifyEmail"
class="email-input"
/>
<div v-if="isLoading" class="status loading">
Verifying...
</div>
<div v-else-if="result" :class="['status', result.status]">
<span v-if="result.status === 'valid'">✓ Valid email</span>
<span v-else-if="result.status === 'invalid'">✗ Invalid email</span>
<span v-else>? Could not verify</span>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { emailverify } from 'emailverify';
const email = ref('');
const isLoading = ref(false);
const result = ref<any>(null);
const verifyEmail = async () => {
if (!email.value) return;
isLoading.value = true;
try {
result.value = await emailverify.verify({
email: email.value,
apiKey: import.meta.env.VITE_EMAILVERIFY_API_KEY
});
} catch (error) {
console.error('Verification failed:', error);
result.value = null;
} finally {
isLoading.value = false;
}
};
</script>
<style scoped>
.email-input {
padding: 8px 12px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 16px;
width: 100%;
}
.status {
margin-top: 8px;
padding: 8px 12px;
border-radius: 4px;
font-size: 14px;
}
.status.valid {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.status.invalid {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.status.loading {
background-color: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
</style>Options API
For projects using the Options API pattern:
<template>
<div class="email-verification">
<input
v-model="email"
type="email"
placeholder="Enter email address"
@blur="verifyEmail"
class="email-input"
/>
<div v-if="isLoading" class="status loading">
Verifying...
</div>
<div v-else-if="result" :class="['status', result.status]">
{{ statusMessage }}
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { emailverify } from 'emailverify';
export default defineComponent({
name: 'EmailVerification',
data() {
return {
email: '',
isLoading: false,
result: null
};
},
computed: {
statusMessage(): string {
if (this.result?.status === 'valid') return '✓ Valid email';
if (this.result?.status === 'invalid') return '✗ Invalid email';
return '? Could not verify';
}
},
methods: {
async verifyEmail() {
if (!this.email) return;
this.isLoading = true;
try {
this.result = await emailverify.verify({
email: this.email,
apiKey: import.meta.env.VITE_EMAILVERIFY_API_KEY
});
} catch (error) {
console.error('Verification failed:', error);
this.result = null;
} finally {
this.isLoading = false;
}
}
}
});
</script>Composables Reference
useEmailVerification
A custom composable for managing email verification state and logic.
import { ref, computed } from 'vue';
import { emailverify } from 'emailverify';
export function useEmailVerification(apiKey: string) {
const email = ref('');
const isLoading = ref(false);
const error = ref<string | null>(null);
const result = ref<any>(null);
const isValid = computed(() => result.value?.status === 'valid');
const isInvalid = computed(() => result.value?.status === 'invalid');
const isDisposable = computed(() => result.value?.result?.disposable || false);
const verify = async (emailToVerify?: string) => {
const emailAddress = emailToVerify || email.value;
if (!emailAddress) {
error.value = 'Email address is required';
return;
}
isLoading.value = true;
error.value = null;
try {
result.value = await emailverify.verify({
email: emailAddress,
apiKey
});
} catch (err) {
error.value = err instanceof Error ? err.message : 'Verification failed';
result.value = null;
} finally {
isLoading.value = false;
}
};
const reset = () => {
email.value = '';
result.value = null;
error.value = null;
isLoading.value = false;
};
return {
email,
isLoading,
error,
result,
isValid,
isInvalid,
isDisposable,
verify,
reset
};
}useEmailInput
A composable for handling email input with debouncing and caching.
import { ref, computed, watch } from 'vue';
import { useEmailVerification } from './useEmailVerification';
const CACHE_DURATION = 30 * 60 * 1000; // 30 minutes
export function useEmailInput(apiKey: string) {
const email = ref('');
const cache = new Map<string, { result: any; timestamp: number }>();
const { verify: verifyWithAPI, isLoading, error, result } = useEmailVerification(apiKey);
const verify = async () => {
if (!email.value) return;
// Check cache first
const cached = cache.get(email.value);
if (cached && Date.now() - cached.timestamp < CACHE_DURATION) {
result.value = cached.result;
return;
}
// Verify with API
await verifyWithAPI(email.value);
// Cache the result
if (result.value) {
cache.set(email.value, {
result: result.value,
timestamp: Date.now()
});
}
};
const clearCache = () => cache.clear();
return {
email,
isLoading,
error,
result,
verify,
clearCache
};
}Component Integration
VeeValidate Integration
Integrate email verification with VeeValidate for form validation.
<template>
<Form @submit="onSubmit" class="form">
<div class="form-group">
<label>Email Address</label>
<Field
name="email"
type="email"
placeholder="your@email.com"
v-slot="{ field, meta }"
:rules="validateEmail"
>
<input
v-bind="field"
:class="{ 'input-error': meta.touched && !meta.valid }"
@blur="verifyEmail(field.value)"
/>
</Field>
<div v-if="verificationResult" :class="['verification-status', verificationResult.status]">
{{ getStatusMessage(verificationResult) }}
</div>
<ErrorMessage name="email" class="error-message" />
</div>
<button type="submit" :disabled="isLoading">
{{ isLoading ? 'Verifying...' : 'Submit' }}
</button>
</Form>
</template>
<script setup lang="ts">
import { Form, Field, ErrorMessage } from 'vee-validate';
import * as yup from 'yup';
import { ref } from 'vue';
import { useEmailVerification } from './composables/useEmailVerification';
const schema = yup.object({
email: yup.string().email('Invalid email').required('Email is required')
});
const { verify, result: verificationResult, isLoading } = useEmailVerification(
import.meta.env.VITE_EMAILVERIFY_API_KEY
);
const validateEmail = async (value: string) => {
if (!value) return 'Email is required';
try {
await schema.validate({ email: value });
return true;
} catch {
return 'Invalid email format';
}
};
const verifyEmail = async (email: string) => {
if (email) {
await verify(email);
}
};
const getStatusMessage = (result: any) => {
switch (result.status) {
case 'valid':
return '✓ Valid email address';
case 'invalid':
return '✗ Invalid email address';
case 'unknown':
return '? Could not verify this email';
default:
return 'Unknown status';
}
};
const onSubmit = (values: any) => {
if (verificationResult.value?.status === 'valid') {
console.log('Form submitted:', values);
}
};
</script>
<style scoped>
.form-group {
margin-bottom: 16px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
input {
width: 100%;
padding: 8px 12px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 14px;
}
input.input-error {
border-color: #dc3545;
background-color: #fff5f5;
}
.error-message {
color: #dc3545;
font-size: 12px;
margin-top: 4px;
}
.verification-status {
margin-top: 8px;
padding: 8px 12px;
border-radius: 4px;
font-size: 13px;
}
.verification-status.valid {
background-color: #d4edda;
color: #155724;
}
.verification-status.invalid {
background-color: #f8d7da;
color: #721c24;
}
</style>Element Plus Integration
Use EmailVerify with Element Plus form components.
<template>
<el-form :model="form" @submit.prevent="handleSubmit">
<el-form-item label="Email" prop="email">
<el-input
v-model="form.email"
type="email"
placeholder="Enter email"
@blur="verifyEmail"
:loading="isLoading"
/>
<el-alert
v-if="verificationResult"
:title="getStatusTitle()"
:type="getAlertType()"
:closable="false"
style="margin-top: 8px"
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="handleSubmit"
:loading="isLoading"
>
Submit
</el-button>
</el-form-item>
</el-form>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
import { useEmailVerification } from './composables/useEmailVerification';
const form = reactive({
email: ''
});
const { verify, result: verificationResult, isLoading } = useEmailVerification(
import.meta.env.VITE_EMAILVERIFY_API_KEY
);
const verifyEmail = async () => {
if (form.email) {
await verify(form.email);
}
};
const getStatusTitle = () => {
switch (verificationResult.value?.status) {
case 'valid':
return 'Email is valid';
case 'invalid':
return 'Email is invalid';
default:
return 'Could not verify email';
}
};
const getAlertType = () => {
return verificationResult.value?.status === 'valid' ? 'success' : 'error';
};
const handleSubmit = async () => {
await verifyEmail();
if (verificationResult.value?.status === 'valid') {
console.log('Email verified, submitting form...');
}
};
</script>SSR with Nuxt 3
Server-Side Verification
Verify emails on the server for better security and performance.
// server/api/verify-email.ts
import { emailverify } from 'emailverify';
export default defineEventHandler(async (event) => {
const { email } = await readBody(event);
if (!email) {
throw createError({
statusCode: 400,
statusMessage: 'Email is required'
});
}
try {
const result = await emailverify.verify({
email,
apiKey: process.env.EMAILVERIFY_API_KEY!
});
return result;
} catch (error) {
throw createError({
statusCode: 500,
statusMessage: 'Verification failed'
});
}
});Client-Side Integration in Nuxt
Use the server endpoint from your Nuxt components.
<template>
<div class="email-verification">
<input
v-model="email"
type="email"
placeholder="your@email.com"
@blur="verifyEmail"
/>
<div v-if="pending" class="status loading">
Verifying...
</div>
<div v-else-if="result" :class="['status', result.status]">
{{ statusMessage }}
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
const email = ref('');
const { data: result, pending, execute: verifyEmail } = await useFetch(
'/api/verify-email',
{
method: 'POST',
body: computed(() => ({ email: email.value })),
immediate: false
}
);
const statusMessage = computed(() => {
if (!result.value) return '';
switch (result.value.status) {
case 'valid':
return '✓ Valid email';
case 'invalid':
return '✗ Invalid email';
default:
return '? Unknown status';
}
});
</script>Advanced Patterns
Debounced Verification
Reduce API calls with debounced email verification.
import { ref, watch } from 'vue';
import { useEmailVerification } from './composables/useEmailVerification';
export function useDebouncedEmailVerification(apiKey: string, delay = 500) {
const email = ref('');
const debounceTimer = ref<NodeJS.Timeout | null>(null);
const { verify, ...rest } = useEmailVerification(apiKey);
watch(email, (newEmail) => {
if (debounceTimer.value) {
clearTimeout(debounceTimer.value);
}
debounceTimer.value = setTimeout(() => {
if (newEmail) {
verify(newEmail);
}
}, delay);
});
return { email, verify, ...rest };
}Email Caching Strategy
Implement intelligent caching to reduce API calls.
const CACHE_KEY_PREFIX = 'emailverify_';
const CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours
export function useCachedEmailVerification(apiKey: string) {
const { verify: verifyWithAPI, result, isLoading } = useEmailVerification(apiKey);
const getCacheKey = (email: string) => `${CACHE_KEY_PREFIX}${email}`;
const verify = async (email: string) => {
const cacheKey = getCacheKey(email);
const cached = localStorage.getItem(cacheKey);
if (cached) {
const { result: cachedResult, timestamp } = JSON.parse(cached);
if (Date.now() - timestamp < CACHE_DURATION) {
result.value = cachedResult;
return;
}
}
await verifyWithAPI(email);
if (result.value) {
localStorage.setItem(
cacheKey,
JSON.stringify({
result: result.value,
timestamp: Date.now()
})
);
}
};
const clearCache = (email?: string) => {
if (email) {
localStorage.removeItem(getCacheKey(email));
} else {
const keys = Object.keys(localStorage);
keys.forEach(key => {
if (key.startsWith(CACHE_KEY_PREFIX)) {
localStorage.removeItem(key);
}
});
}
};
return { verify, result, isLoading, clearCache };
}Error Handling and Retry
Implement robust error handling with retry logic.
export function useEmailVerificationWithRetry(apiKey: string, maxRetries = 3) {
const { verify: verifyWithAPI, result, isLoading, error } = useEmailVerification(apiKey);
const verifyWithRetry = async (email: string, retryCount = 0): Promise<any> => {
try {
await verifyWithAPI(email);
return result.value;
} catch (err) {
if (retryCount < maxRetries) {
// Wait before retry (exponential backoff)
await new Promise(resolve => setTimeout(resolve, Math.pow(2, retryCount) * 1000));
return verifyWithRetry(email, retryCount + 1);
}
throw err;
}
};
return { verifyWithRetry, result, isLoading, error };
}TypeScript Support
The EmailVerify SDK includes full TypeScript support with type definitions.
interface VerificationRequest {
email: string;
apiKey: string;
}
interface VerificationResult {
status: 'valid' | 'invalid' | 'unknown' | 'accept_all';
result: {
disposable: boolean;
smtp_valid: boolean;
format_valid: boolean;
email_provider: string;
risk_level: 'low' | 'medium' | 'high';
};
score: number;
}
interface UseEmailVerificationReturn {
email: Ref<string>;
isLoading: Ref<boolean>;
error: Ref<string | null>;
result: Ref<VerificationResult | null>;
isValid: ComputedRef<boolean>;
isInvalid: ComputedRef<boolean>;
isDisposable: ComputedRef<boolean>;
verify: (emailToVerify?: string) => Promise<void>;
reset: () => void;
}Testing
Unit Tests with Vitest
Test your email verification composables with Vitest.
import { describe, it, expect, vi } from 'vitest';
import { useEmailVerification } from './composables/useEmailVerification';
vi.mock('emailverify', () => ({
emailverify: {
verify: vi.fn()
}
}));
describe('useEmailVerification', () => {
it('verifies valid email', async () => {
const { verify, result, isValid } = useEmailVerification('test-key');
await verify('user@example.com');
expect(result.value?.status).toBe('valid');
expect(isValid.value).toBe(true);
});
it('marks invalid email', async () => {
const { verify, result, isInvalid } = useEmailVerification('test-key');
await verify('invalid@');
expect(result.value?.status).toBe('invalid');
expect(isInvalid.value).toBe(true);
});
it('handles verification errors', async () => {
const { verify, error } = useEmailVerification('test-key');
try {
await verify('');
} catch (err) {
expect(error.value).toBeTruthy();
}
});
});Component Tests
Test your Vue components with Vue Test Utils.
import { mount } from '@vue/test-utils';
import { describe, it, expect } from 'vitest';
import EmailVerification from './EmailVerification.vue';
describe('EmailVerification', () => {
it('renders input field', () => {
const wrapper = mount(EmailVerification);
expect(wrapper.find('input[type="email"]').exists()).toBe(true);
});
it('shows verification status', async () => {
const wrapper = mount(EmailVerification);
await wrapper.find('input').setValue('user@example.com');
await wrapper.find('input').trigger('blur');
expect(wrapper.find('.status').exists()).toBe(true);
});
it('shows loading state during verification', async () => {
const wrapper = mount(EmailVerification);
await wrapper.find('input').setValue('user@example.com');
expect(wrapper.find('.status.loading').exists()).toBe(true);
});
});