Ajouter un formulaire de contact à votre site Vitepress
Création du formulaire
Nous allons créer un composant Vue pour notre formulaire de contact.
Ce composant est à placer dans .vitepress/theme/components/ContactForm.vue:
<script setup>
import { ref, computed } from 'vue'
import { useData } from 'vitepress'
const formData = ref({
name: '',
email: '',
subject: '',
message: ''
})
const isSubmitting = ref(false)
const isSuccess = ref(false)
const errorMessage = ref('')
const isValidEmail = (email) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
return emailRegex.test(email)
}
const isFormValid = computed(() => {
return formData.value.name.trim() !== '' &&
formData.value.email.trim() !== '' &&
isValidEmail(formData.value.email) &&
formData.value.subject.trim() !== '' &&
formData.value.message.trim() !== ''
})
const submitForm = async () => {
errorMessage.value = ''
if (!formData.value.name.trim()) {
errorMessage.value = 'Name is required'
return
}
if (!formData.value.email.trim()) {
errorMessage.value = 'Email address is required'
return
}
if (!isValidEmail(formData.value.email)) {
errorMessage.value = 'Email address is invalid'
return
}
if (!formData.value.subject.trim()) {
errorMessage.value = 'Message subject is required'
return
}
if (!formData.value.message.trim()) {
errorMessage.value = 'Message is required'
return
}
isSubmitting.value = true
try {
const formDataToSend = new FormData()
formDataToSend.append('name', formData.value.name)
formDataToSend.append('email', formData.value.email)
formDataToSend.append('subject', formData.value.subject)
formDataToSend.append('message', formData.value.message)
const response = await fetch('/sendmail.php', {
method: 'POST',
body: formDataToSend
})
const message = await response.text()
if (response.ok) {
isSuccess.value = true
errorMessage.value = ''
} else {
errorMessage.value = message || 'An error occurred. Please try again.'
}
} catch (error) {
errorMessage.value = 'Network error'
} finally {
isSubmitting.value = false
}
}
</script>
<template>
<div class="contact-form">
<form @submit.prevent="submitForm">
<div class="form-group">
<label for="name">Name <span class="required">*</span></label>
<input
id="name"
v-model="formData.name"
type="text"
:disabled="isSuccess"
placeholder="Your name"
/>
</div>
<div class="form-group">
<label for="email">Email <span class="required">*</span></label>
<input
id="email"
v-model="formData.email"
type="email"
:disabled="isSuccess"
placeholder="Your email address"
/>
</div>
<div class="form-group">
<label for="subject">Subject <span class="required">*</span></label>
<input
id="subject"
v-model="formData.subject"
type="text"
:disabled="isSuccess"
placeholder="Message subject"
/>
</div>
<div class="form-group">
<label for="message">Message <span class="required">*</span></label>
<textarea
id="message"
v-model="formData.message"
:disabled="isSuccess"
rows="5"
placeholder="Your message"
></textarea>
</div>
<!-- Error message area -->
<div v-if="errorMessage" class="error-message">
{{ errorMessage }}
</div>
<!-- Success message area -->
<div v-if="isSuccess" class="success-message">
Thank you! Your message has been sent successfully.
</div>
<button
v-if="!isSuccess"
type="submit"
:disabled="!isFormValid || isSubmitting || isSuccess"
class="submit-button"
>
{{ isSubmitting ? 'Sending...' : 'Send message' }}
</button>
</form>
</div>
</template>
<style scoped>
.contact-form {
max-width: 600px;
margin: 2rem auto;
}
.form-group {
margin-bottom: 1.5rem;
}
label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
color: var(--vp-c-text-1);
}
.required {
color: #e74c3c;
}
input,
textarea {
width: 100%;
padding: 0.75rem;
border: 1px solid var(--vp-c-divider);
border-radius: 4px;
font-size: 1rem;
font-family: inherit;
background-color: var(--vp-c-bg);
color: var(--vp-c-text-1);
transition: border-color 0.2s;
}
input:focus,
textarea:focus {
outline: none;
border-color: var(--vp-c-brand);
}
input:disabled,
textarea:disabled {
opacity: 0.6;
cursor: not-allowed;
background-color: var(--vp-c-bg-soft);
}
textarea {
resize: vertical;
}
.error-message {
padding: 0.75rem 1rem;
margin-bottom: 1rem;
background-color: #fee;
border-left: 4px solid #e74c3c;
color: #c0392b;
border-radius: 4px;
}
.success-message {
padding: 0.75rem 1rem;
margin-bottom: 1rem;
background-color: #d4edda;
border-left: 4px solid #28a745;
color: #155724;
border-radius: 4px;
font-weight: 500;
}
.submit-button {
width: 100%;
padding: 0.75rem 1.5rem;
background-color: var(--vp-c-brand-1);
color: white;
border: none;
border-radius: 4px;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s;
}
.submit-button:hover:not(:disabled) {
background-color: var(--vp-c-brand-2);
}
.submit-button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
</style>Intégration du composant sur le site
Éditez .vitepress/theme/index.js pour ajouter:
enhanceApp({ app }) {
app.component('ContactForm', ContactForm)
}
Note: Si vous avez déjà
enhanceAppdans ce fichier, par exemple sous la formeenhanceApp({ router }), complétez-le en y intégrant l'entrée ci-dessus et en ajoutantappcomme suit:enhanceApp({ app, router }).
À présent, vous pouvez intégrer le composant dans n'importe quelle page Markdown comme ceci:
# Contact
Écrivez-moi avec le formulaire ci-dessous:
<ContactForm />Côté serveur
Pour l'envoi effectif d'emails, nous allons utiliser le script PHP ci-dessous.
Dans l'en-tête, vous devez configurer vos coordonnées en modifiant CONTACT_INFO:
- to_name et to_email: votre nom, et votre adresse email sur laquelle vous souhaitez recevoir les messages de vos visiteurs,
- cc_name et cc_email: nom et addresse email de copie carbone des messages (facultatif),
- site_email: l'adresse depuis laquelle votre site vous envoie les messages (vous pouvez mettre une adresse qui n'existe pas, comme
no-reply@votre-domaine.tld).
Note: Votre site vous enverra les messages des visiteurs en son nom, avec l'email site_email que vous aurez configuré. Cependant il configure un champ
Reply-Todans les emails, qui vous permettra de répondre aux messages en toute transparence.
Ce script est à placer dans public/sendmail.php:
<?php
$CONTACT_INFO['to_email'] = 'me@example.com';
$CONTACT_INFO['to_name'] = 'My name';
$CONTACT_INFO['cc_email'] = '';
$CONTACT_INFO['cc_name'] = '';
$CONTACT_INFO['site_email'] = 'My website <no-reply@example.com>';
header("Content-Type: text/plain");
$from_name = trim($_POST['name']);
$from_email = trim($_POST['email']);
$subject = trim($_POST['subject']);
$message = trim($_POST['message']);
$regex_head = '/[\n\r]/';
$regex_mail = '/^[-+.\w]{1,64}@[-.\w]{1,64}\.[-.\w]{2,6}$/i';
if (empty($from_name)
|| empty($from_email)
|| empty($subject)
|| empty($message)) {
http_response_code(400);
echo 'Required fields must be filled';
} else if (!preg_match($regex_mail, $from_email)) {
http_response_code(400);
echo 'Invalid email address';
} else if (preg_match($regex_head, $from_name)
|| preg_match($regex_head, $from_email)
|| preg_match($regex_head, $subject)) {
http_response_code(400);
echo 'Malformed data';
} else {
$to = $CONTACT_INFO['to_name']
.'<'.$CONTACT_INFO['to_email'].'>';
$headers = 'From: '.$CONTACT_INFO['site_email']."\r\n";
$headers .= 'Reply-To: '.$from_name
.' <'.$from_email.'>'."\r\n";
if (!empty($CONTACT_INFO['cc_email'])) {
$headers .= 'Cc: '.$CONTACT_INFO['cc_name']
.' <'.$CONTACT_INFO['cc_email'].'>'."\r\n";
}
$headers .= "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: text/plain; charset=UTF-8\r\n";
$headers .= "Content-Transfer-Encoding: base64\r\n";
$contents = vsprintf('
%s
--------
%s <%s>', array(
$message, $from_name, $from_email
));
$contents = chunk_split(base64_encode($contents));
if(!mail($to, $subject, $contents, $headers)) {
http_response_code(500);
echo 'Unable to send message';
}
}
?>Vous pouvez si besoin modifier le composant Vue ainsi que le script PHP pour leur adjoindre une protection anti-spam.
Licence
Tout le code présenté sur cette page est sous licence CC0 1.0 Universal.
Vous pouvez le copier, le modifier, le distribuer et l'utiliser, même à des fins commerciales, sans demander la permission.