Contact form on a static site6 min read
Add a contact form to your portfolio website
A portfolio contact form that asks the questions you'd ask on a first call: project, budget, timing and a link, with no server and replies from your inbox.
Ask for the four things you need before a first call: what the project is, a budget range, when it starts and a link to look at. Post the form to Ikiform from your static portfolio, so each enquiry lands in your inbox with Reply going to the client. Prefill the project type from the case study the visitor came from, so you know what sold them.
What's different about a portfolio form
On a portfolio, the contact form is the sales funnel. A bare name, email and message form brings in messages you have to answer with questions: what's the budget, when do you want to start, do you have a site already. Asking those up front saves a round of email, and the answers tell you which enquiries to answer first.
Portfolios also tend to live on free static hosting, built once and left alone for months. The form has to keep working without a server to patch, and without you checking a dashboard.
| Field | Why it's there | Type |
|---|---|---|
project | Who to answer and what to send back | select, so it's safe to filter on |
budget | Ranges filter better than an open number, and people answer them | radio with ranges |
start | "This month" and "later, still planning" need different replies | select |
link | Their current site or a brief, so you reply with something specific | url |
message | The project in their words | textarea, at least 30 characters |
Leave out phone numbers and company size. Each extra required field costs enquiries, and you'll learn both on the first call.
Preview
Runs Ikiform's validator in your browser. Nothing is sent.
seen is in the definition because a form with fields refuses any input it doesn't list; on the page it's a hidden input.422 files_not_supported.The form
Plain HTML, so it works in a hand-written portfolio, a Jekyll or Hugo theme, or an Astro template. The script at the end prefills the project type from the link that brought the visitor here, and records which page that was:
<form id="enquiry" action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
<label for="name">Your name</label>
<input id="name" name="name" autocomplete="name" maxlength="120" required />
<label for="email">Email</label>
<input id="email" name="email" type="email" autocomplete="email" required />
<label for="project">What do you need?</label>
<select id="project" name="project" required>
<option value="website">A website</option>
<option value="branding">Branding</option>
<option value="product">Product design</option>
<option value="other">Something else</option>
</select>
<fieldset>
<legend>Budget</legend>
<label><input type="radio" name="budget" value="under_5k" required /> Under $5k</label>
<label><input type="radio" name="budget" value="5k_15k" /> $5k to $15k</label>
<label><input type="radio" name="budget" value="over_15k" /> Over $15k</label>
<label><input type="radio" name="budget" value="unsure" /> Not sure yet</label>
</fieldset>
<label for="start">When would you like to start?</label>
<select id="start" name="start">
<option value="now">This month</option>
<option value="soon">In 1 to 3 months</option>
<option value="later">Later, still planning</option>
</select>
<label for="link">Your current site or a brief</label>
<input id="link" name="link" type="url" placeholder="https://" />
<label for="message">Tell me about the project</label>
<textarea id="message" name="message" rows="6" minlength="30" required></textarea>
<input type="hidden" name="seen" value="" />
<input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
<button type="submit">Send enquiry</button>
</form>
<script>
const params = new URLSearchParams(location.search);
const enquiry = document.querySelector("#enquiry");
const project = params.get("project");
if (project && enquiry.project.querySelector(`option[value="${CSS.escape(project)}"]`)) {
enquiry.project.value = project;
}
enquiry.seen.value = params.get("from") ?? document.referrer;
</script>On each case study, link to the form with the project type and the page name:
<a href="/contact.html?project=branding&from=case-study-northwind">Start a branding project</a>- The script only selects a project value that exists in the list, so a hand-edited link can't add new values.
seenholds the case study name, or the referrer when there is none. After a few months, the CSV tells you which work brings in which enquiries.- Keep
emailas the field name. Notification emails set it as the reply-to, so you answer from your own inbox. - For a framework portfolio, the pillar guide has the same form pattern in Next.js, Astro, SvelteKit, Nuxt, React and Vue. Static site generators have their own setup pages, such as Jekyll and Hugo.
Answer the right enquiries first
- A visitor arrives from a case study, and the form opens with the project type already chosen.
- The form posts to Ikiform, which runs the spam checks and stores the enquiry.
- You get a notification email, and Reply writes straight back to the client.
- A webhook sends the same enquiry to Telegram or Slack, where an automation can alert you for large budgets only.
- Every enquiry stays in the dashboard, so you can export a year of them as CSV and see which work brought them in.
Ikiform sends every enquiry the same way; filtering by budget happens in the tool that receives the webhook. In Zapier or n8n, add a filter on budget before the message step. To reach your phone directly, follow Telegram or Slack.
Ikiform doesn't email the person who fills in the form. Notification emails go to you and your team only, so there is no autoresponder, confirmation email or double opt-in. Send those from your email tool, triggered by a webhook. For a portfolio, a short thank-you page that says when you'll reply does most of what an autoresponder would.
A thank-you page that books the call
Set Redirect after submit to a thank-you page of your own. On a portfolio it can do more than say thanks: link your calendar booking page for people who want to talk now, and say when you reply to everyone else. Add two or three case studies they haven't seen; they're still deciding.
Keep the page out of search results with a noindex meta tag, and don't count visits to it as a conversion in analytics without also checking the dashboard, because spam gets the same redirect as a real enquiry.
Portfolio spam
Portfolios get a particular kind of spam: SEO offers, guest post pitches and "redesign your site" templates. Ikiform's scoring adds +2 for known spam phrases, +3 for a link in the name field and +2 for three or more links; at 5 a message is stored as spam and sends no email.
A client rarely sends two enquiries in ten minutes, so set the rate limit to Strict, 2 every 10 minutes per visitor. Add your domain under Allowed domains, and check the Spam filter now and then in the first weeks: a real enquiry with three links to past work can score high.
Check it works
- Under the form's Allowed domains, add your site's domain, plus
localhostwhile you develop. Leave it empty to accept posts from anywhere. - Open
contact.html?project=branding&from=testand check Branding is selected. - Send an enquiry. In Submissions,
seenshould readtest. - Remove
minlengthfrom the textarea in your browser's devtools and send a three-word message. Ikiform refuses it with422 validation_failed, namingmessage. - Check the inbox of each notification recipient. Each gets an email with the fields, and Reply goes to the submitter when the form has an
emailfield. - Nothing arrived? Switch the dashboard's status filter to Spam. Spam gets the same success answer as a real submission.
Gotchas
| Problem | What to do |
|---|---|
422 validation_failed for budget | A radio value in the HTML doesn't match the definition's options. They must match exactly, including underscores. |
| The project isn't prefilled | The project value in the link isn't one of the option values. Check spelling and case. |
| A real client's enquiry is in spam | Three or more links add to the score, and with a phrase from the spam list or a disposable address on top it crosses the threshold. Mark it as not spam; its email and webhooks go out then. |
422 files_not_supported | The form has an <input type="file"> with a file chosen. File uploads aren't supported yet; remove the input or ask for a link instead. |
Questions
Usually yes, as ranges rather than a number. Ranges are easy to answer, and they tell you which enquiries need a quick reply. Include a Not sure yet option so nobody leaves.
Not through Ikiform yet. Ask for a link instead: a shared doc, a drive folder or their current site. The url field checks it's a real web address.
No. The portfolio stays static on any host, and the form posts to Ikiform, which stores each enquiry and emails you.
In the seen field of each submission. Filter or search it in the dashboard, or export a CSV and count enquiries per page every few months.
No, there is no free plan. Testing is free: you can send 50 test submissions a month from localhost before you pay. To go live, Pro starts at $6/mo for 500 submissions, or you can pay once for 10,000 submissions for $49 or 50,000 submissions for $149, with credits that never expire.
Stuck on a step, or want a guide for your situation? Email hello@ikiform.com.