# 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.

Published 2026-10-25 · https://ikiform.com/guides/contact-form-static-site/portfolio-website

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.

Form: Work with me

- **Your name** (`name`, text, required)
- **Email** (`email`, email, required)
- **What do you need?** (`project`, select, required)
- **Budget** (`budget`, radio, required)
- **When would you like to start?** (`start`, select)
- **Your current site or a brief** (`link`, url)
- **Tell me about the project** (`message`, textarea, required)
- **Came from** (`seen`, text)

Paste this definition on the form's **Fields** tab. Ikiform then accepts only the listed budget and project values, and refuses a message under 30 characters with a field error. `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.

> File uploads aren't supported yet. A post with a file gets `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:

`contact.html`:

```html
<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:

`case-studies/northwind.html`:

```html
<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.
- `seen` holds 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 `email` as 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](https://ikiform.com/guides/contact-form-static-site.md#form) 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](https://ikiform.com/docs/guides/jekyll.md) and [Hugo](https://ikiform.com/docs/guides/hugo.md).

## Answer the right enquiries first

Diagram: Where a portfolio enquiry goes after the visitor presses Send

1. A visitor arrives from a case study, and the form opens with the project type already chosen.
2. The form posts to Ikiform, which runs the spam checks and stores the enquiry.
3. You get a notification email, and Reply writes straight back to the client.
4. A webhook sends the same enquiry to Telegram or Slack, where an automation can alert you for large budgets only.
5. Every enquiry stays in the dashboard, so you can export a year of them as CSV and see which work brought them in.

Email alone is enough to start. Add the phone alert when you start losing work to slow replies.

Ikiform sends every enquiry the same way; filtering by budget happens in the tool that receives the webhook. In [Zapier](https://ikiform.com/blog/form-submissions-to-zapier.md) or [n8n](https://ikiform.com/blog/form-submissions-to-n8n.md), add a filter on `budget` before the message step. To reach your phone directly, follow [Telegram](https://ikiform.com/blog/form-submissions-to-telegram.md) or [Slack](https://ikiform.com/blog/form-submissions-to-slack.md).

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](https://ikiform.com/docs/webhooks.md). 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

1. Under the form's **Allowed domains**, add your site's domain, plus `localhost` while you develop. Leave it empty to accept posts from anywhere.
2. Open `contact.html?project=branding&from=test` and check **Branding** is selected.
3. Send an enquiry. In **Submissions**, `seen` should read `test`.
4. Remove `minlength` from the textarea in your browser's devtools and send a three-word message. Ikiform refuses it with `422 validation_failed`, naming `message`.
5. 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 `email` field.
6. 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

### Should a portfolio contact form ask for a budget?

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.

### Can clients attach a brief or files?

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.

### Do I need a server for a portfolio contact form?

No. The portfolio stays static on any host, and the form posts to Ikiform, which stores each enquiry and emails you.

### Where do I see which case study brought an enquiry?

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.

### Is Ikiform free?

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.

## More guides

- [Contact form on a static site](https://ikiform.com/guides/contact-form-static-site.md): Add a working contact form to a static site with no server: where the POST goes, spam, replies by email, and code for every host and framework.
- [GitHub Pages](https://ikiform.com/guides/contact-form-static-site/github-pages.md): GitHub Pages can't run server code, so a form POST gets 405 Not Allowed; post to a form backend instead and get project-site redirects right.
- [Netlify](https://ikiform.com/guides/contact-form-static-site/netlify.md): Run a contact form on a Netlify site without Netlify Forms: no build-time detection, JSON posts from React, and a separate form for deploy previews.
- [Vercel](https://ikiform.com/guides/contact-form-static-site/vercel.md): A static page on Vercel answers a form POST with 405 Method Not Allowed; here is why, and how to send the form to a backend instead.
- [Cloudflare Pages](https://ikiform.com/guides/contact-form-static-site/cloudflare-pages.md): Add a contact form to Cloudflare Pages without writing a Function: post to a form backend, set headers in _headers and use your own Turnstile keys.
- [Amazon S3](https://ikiform.com/guides/contact-form-static-site/aws-s3.md): S3 website endpoints accept only GET and HEAD over plain HTTP, so post the contact form to a form backend and get redirects right with CloudFront.
