Form Prefilling with URL Parameters and API Data
Prefilled forms increase conversion and reduce friction. A user clicking a link from an email or CRM sees fields already populated—they only need to review and submit. Forms prefilled from an API save time during repeat interactions.
Prefilling Sources
URL parameters — the simplest approach. A link like /apply?name=John&[email protected]&plan=pro fills corresponding fields on page load.
JWT token in URL — data is encrypted and cannot be directly edited by the user, allowing sensitive values to be passed securely.
API request — the form loads user data via their authorization token or by entity ID (order, request, profile).
SessionStorage / Cookie — data from a previous session or from another page of the same website.
Prefilling from URL Parameters
function prefillFromURL() {
const params = new URLSearchParams(window.location.search);
// Whitelist — never fill hidden/system fields from URL
const allowed = ['name', 'email', 'phone', 'company', 'plan', 'promo'];
for (const field of allowed) {
const value = params.get(field);
if (!value) continue;
const el = document.querySelector(`[name="${field}"]`);
if (!el) continue;
// Sanitize — text only, no HTML
el.value = DOMPurify.sanitize(value, { ALLOWED_TAGS: [] });
el.dispatchEvent(new Event('input', { bubbles: true }));
}
}
document.addEventListener('DOMContentLoaded', prefillFromURL);
Prefilling via JWT
More secure than plain parameters — data is signed and cannot be substituted without knowing the secret:
// Link format: /form?token=eyJhbGci...
async function prefillFromToken() {
const token = new URLSearchParams(window.location.search).get('token');
if (!token) return;
const res = await fetch('/api/form-prefill', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token }),
});
if (!res.ok) return;
const data = await res.json();
applyFormData(data);
}
On the Laravel server:
public function decodePrefillToken(Request $request)
{
try {
$payload = JWT::decode($request->token, new Key(config('app.key'), 'HS256'));
return response()->json((array) $payload->form_data);
} catch (\Exception $e) {
return response()->json(['error' => 'Invalid token'], 422);
}
}
Token generation (for example, when sending an email):
$payload = [
'form_data' => [
'name' => $user->name,
'email' => $user->email,
'plan' => 'pro',
],
'exp' => now()->addHours(24)->timestamp,
];
$token = JWT::encode($payload, config('app.key'), 'HS256');
$link = route('form') . '?token=' . $token;
Prefilling from API (For Authenticated Users)
async function prefillFromProfile() {
const res = await fetch('/api/user/profile');
if (!res.ok) return;
const profile = await res.json();
const mapping = {
'contact[name]': profile.full_name,
'contact[email]': profile.email,
'contact[phone]': profile.phone,
'address[city]': profile.city,
'address[street]': profile.address,
};
for (const [fieldName, value] of Object.entries(mapping)) {
if (value == null) continue;
const el = document.querySelector(`[name="${fieldName}"]`);
if (el) el.value = value;
}
}
React Hook Form with Prefilling
function ApplicationForm({ userId }) {
const { register, reset, handleSubmit } = useForm();
useEffect(() => {
async function load() {
const res = await fetch(`/api/users/${userId}/prefill`);
const data = await res.json();
reset(data); // fills all form fields
}
if (userId) load();
}, [userId, reset]);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('name')} placeholder="Name" />
<input {...register('email')} placeholder="Email" />
{/* ... */}
</form>
);
}
Security
-
Never prefill fields like
role,is_admin,price,discountfrom the URL - Sanitize all URL values before insertion (XSS via
value) - Verify data from tokens on the server — don't trust the frontend
- Use
rel="noopener noreferrer"when generating links in external systems
Visual Indication
Prefilled fields should be visually highlighted so users understand the data is already populated and can verify it:
.field--prefilled {
background: #f0f9ff;
border-color: #38bdf8;
}
.field--prefilled::after {
content: '✓ prefilled';
font-size: 0.75rem;
color: #0369a1;
}
Timeframe
Prefilling from URL with sanitization — 1 working day. JWT tokens + server API with field mapping and indication — 3–4 days.







