Building forms in React? Here’s how to add client-side encryption to protect your users’ data before it leaves their browser.
Prerequisites
- React 18+ project
- VeilForms account (free tier works)
- Form ID and public key from dashboard
Installation
Install the VeilForms SDK:
npm install @veilforms/react
Or use the CDN in your HTML:
<script src="https://cdn.veilforms.com/v1/veilforms.esm.js"></script>
Basic Setup
Option 1: React Hook
import { useVeilForm } from '@veilforms/react';
function ContactForm() {
const { submit, isSubmitting, error, success } = useVeilForm({
formId: 'your-form-id',
publicKey: 'your-public-key'
});
const handleSubmit = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
await submit(Object.fromEntries(formData));
};
if (success) {
return <div className="success">Thank you! Your message has been sent securely.</div>;
}
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="name">Name</label>
<input type="text" id="name" name="name" required />
</div>
<div>
<label htmlFor="email">Email</label>
<input type="email" id="email" name="email" required />
</div>
<div>
<label htmlFor="message">Message</label>
<textarea id="message" name="message" rows={4} required />
</div>
{error && <div className="error">{error.message}</div>}
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Sending...' : 'Send Securely'}
</button>
</form>
);
}
Option 2: Data Attribute
Add the attribute to any existing form:
function ContactForm() {
return (
<form data-veilform="your-form-id">
<input type="text" name="name" required />
<input type="email" name="email" required />
<textarea name="message" required />
<button type="submit">Send</button>
</form>
);
}
Initialize in your app entry point:
// App.jsx or index.jsx
import { useEffect } from 'react';
function App() {
useEffect(() => {
// Initialize VeilForms after component mounts
if (window.VeilForms) {
window.VeilForms.init({
publicKey: 'your-public-key'
});
}
}, []);
return <ContactForm />;
}
Advanced Configuration
With TypeScript
import { useVeilForm, VeilFormConfig, SubmissionResult } from '@veilforms/react';
interface ContactFormData {
name: string;
email: string;
message: string;
}
function ContactForm() {
const config: VeilFormConfig = {
formId: 'contact',
publicKey: process.env.REACT_APP_VEILFORMS_KEY!,
onSuccess: (result: SubmissionResult) => {
console.log('Submission ID:', result.submissionId);
},
onError: (error: Error) => {
console.error('Submission failed:', error);
}
};
const { submit, isSubmitting } = useVeilForm<ContactFormData>(config);
const handleSubmit = async (data: ContactFormData) => {
await submit(data);
};
// ... form JSX
}
With React Hook Form
import { useForm } from 'react-hook-form';
import { useVeilForm } from '@veilforms/react';
function ContactForm() {
const { register, handleSubmit, formState: { errors } } = useForm();
const { submit, isSubmitting } = useVeilForm({
formId: 'contact',
publicKey: 'your-public-key'
});
const onSubmit = async (data) => {
await submit(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input
{...register('name', { required: 'Name is required' })}
placeholder="Name"
/>
{errors.name && <span>{errors.name.message}</span>}
<input
{...register('email', {
required: 'Email is required',
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: 'Invalid email address'
}
})}
placeholder="Email"
/>
{errors.email && <span>{errors.email.message}</span>}
<textarea
{...register('message', { required: 'Message is required' })}
placeholder="Message"
/>
{errors.message && <span>{errors.message.message}</span>}
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Sending...' : 'Send'}
</button>
</form>
);
}
With Formik
import { Formik, Form, Field, ErrorMessage } from 'formik';
import * as Yup from 'yup';
import { useVeilForm } from '@veilforms/react';
const validationSchema = Yup.object({
name: Yup.string().required('Required'),
email: Yup.string().email('Invalid email').required('Required'),
message: Yup.string().required('Required')
});
function ContactForm() {
const { submit } = useVeilForm({
formId: 'contact',
publicKey: 'your-public-key'
});
return (
<Formik
initialValues={{ name: '', email: '', message: '' }}
validationSchema={validationSchema}
onSubmit={async (values, { setSubmitting, resetForm }) => {
await submit(values);
setSubmitting(false);
resetForm();
}}
>
{({ isSubmitting }) => (
<Form>
<Field type="text" name="name" placeholder="Name" />
<ErrorMessage name="name" component="div" className="error" />
<Field type="email" name="email" placeholder="Email" />
<ErrorMessage name="email" component="div" className="error" />
<Field as="textarea" name="message" placeholder="Message" />
<ErrorMessage name="message" component="div" className="error" />
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Sending...' : 'Send'}
</button>
</Form>
)}
</Formik>
);
}
Handling PII Detection
Enable PII detection to identify sensitive data:
const { submit } = useVeilForm({
formId: 'feedback',
publicKey: 'your-public-key',
piiDetection: {
enabled: true,
types: ['email', 'phone', 'ssn', 'creditCard'],
onDetect: (detected) => {
console.log('PII detected:', detected);
// Show warning to user if needed
}
}
});
Custom Success/Error Handling
function ContactForm() {
const [status, setStatus] = useState(null);
const { submit, isSubmitting } = useVeilForm({
formId: 'contact',
publicKey: 'your-public-key',
onSuccess: (result) => {
setStatus({
type: 'success',
message: 'Message sent! ID: ' + result.submissionId
});
},
onError: (error) => {
setStatus({
type: 'error',
message: error.message || 'Something went wrong'
});
}
});
return (
<div>
{status && (
<div className={`alert ${status.type}`}>
{status.message}
</div>
)}
<form onSubmit={handleSubmit}>
{/* form fields */}
</form>
</div>
);
}
Environment Variables
Store your public key in environment variables:
# .env
REACT_APP_VEILFORMS_KEY=your-public-key
REACT_APP_VEILFORMS_FORM_ID=contact
const { submit } = useVeilForm({
formId: process.env.REACT_APP_VEILFORMS_FORM_ID,
publicKey: process.env.REACT_APP_VEILFORMS_KEY
});
Complete Example
Here’s a full contact form component with styling:
import { useState } from 'react';
import { useVeilForm } from '@veilforms/react';
import './ContactForm.css';
export function ContactForm() {
const [formData, setFormData] = useState({
name: '',
email: '',
subject: '',
message: ''
});
const { submit, isSubmitting, error, success, reset } = useVeilForm({
formId: process.env.REACT_APP_VEILFORMS_FORM_ID,
publicKey: process.env.REACT_APP_VEILFORMS_KEY
});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
};
const handleSubmit = async (e) => {
e.preventDefault();
await submit(formData);
};
if (success) {
return (
<div className="contact-success">
<h3>Message Sent!</h3>
<p>Your message has been encrypted and sent securely.</p>
<button onClick={() => {
reset();
setFormData({ name: '', email: '', subject: '', message: '' });
}}>
Send Another Message
</button>
</div>
);
}
return (
<form onSubmit={handleSubmit} className="contact-form">
<div className="form-group">
<label htmlFor="name">Name *</label>
<input
type="text"
id="name"
name="name"
value={formData.name}
onChange={handleChange}
required
/>
</div>
<div className="form-group">
<label htmlFor="email">Email *</label>
<input
type="email"
id="email"
name="email"
value={formData.email}
onChange={handleChange}
required
/>
</div>
<div className="form-group">
<label htmlFor="subject">Subject</label>
<input
type="text"
id="subject"
name="subject"
value={formData.subject}
onChange={handleChange}
/>
</div>
<div className="form-group">
<label htmlFor="message">Message *</label>
<textarea
id="message"
name="message"
rows={5}
value={formData.message}
onChange={handleChange}
required
/>
</div>
{error && (
<div className="form-error">
{error.message}
</div>
)}
<div className="form-footer">
<p className="security-note">
Your data is encrypted in your browser before transmission.
</p>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Encrypting & Sending...' : 'Send Securely'}
</button>
</div>
</form>
);
}
That’s it! Your React forms now encrypt data in the browser before submission. Users get the same form experience, but with military-grade encryption protecting their data.
Questions? Check out the full SDK documentation or contact us.