Quote request form template
A free request a quote form with service, budget and project details. Copy the HTML, CSS and JS, or use it in React, Next.js, Vue, Svelte or Astro.
Free to use. Updated
Live preview
Try it. The preview checks the fields like the real form, but nothing is sent.
Copy the quote request form code
Pick your stack. Styled code matches the preview, with the fields, text, theme and color you set above. Unstyled code has the same fields as bare markup that picks up your own CSS, like Tailwind.
<form id="nf-quote-request-form" class="nf-form" action="https://api.nisuform.com/s/YOUR_FORM_KEY" method="POST">
<style>
#nf-quote-request-form {
--nf-accent: #e60058;
--nf-on-accent: #ffffff;
--nf-text: #171717;
--nf-muted: #525252;
--nf-field: #f5f5f5;
--nf-field-text: #171717;
--nf-placeholder: #6b6b6b;
--nf-control: #8a8a8a;
--nf-error: #b91c1c;
color-scheme: light;
box-sizing: border-box;
width: 100%;
max-width: 480px;
margin: 0;
padding: 28px;
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: 16px;
background: linear-gradient(180deg, #ffffff, #ffffff);
box-shadow: 0 16px 40px -16px rgba(0, 0, 0, 0.12);
color: var(--nf-text);
font: inherit;
font-size: 16px;
line-height: 1.5;
text-align: left;
}
#nf-quote-request-form *, #nf-quote-request-form *::before, #nf-quote-request-form *::after { box-sizing: border-box; }
#nf-quote-request-form [hidden] { display: none !important; }
#nf-quote-request-form .nf-title { margin: 0; font-size: 1.25em; font-weight: 650; line-height: 1.25; letter-spacing: -0.01em; color: var(--nf-text); }
#nf-quote-request-form .nf-desc { margin: 6px 0 0; font-size: 0.9375em; color: var(--nf-muted); }
#nf-quote-request-form .nf-fields { display: grid; gap: 18px; margin-top: 22px; }
#nf-quote-request-form .nf-head + .nf-fields { margin-top: 22px; }
#nf-quote-request-form > .nf-fields:first-child { margin-top: 0; }
#nf-quote-request-form .nf-field { margin: 0; padding: 0; border: 0; min-width: 0; }
#nf-quote-request-form .nf-label { display: block; margin: 0 0 6px; padding: 0; font-size: 0.875em; font-weight: 600; color: var(--nf-text); }
#nf-quote-request-form .nf-req { color: var(--nf-muted); font-weight: 400; }
#nf-quote-request-form .nf-help { margin: 6px 0 0; font-size: 0.8125em; color: var(--nf-muted); }
#nf-quote-request-form .nf-input { display: block; width: 100%; margin: 0; padding: 10px 12px; border: 1px solid transparent; border-radius: 10px; background: var(--nf-field); color: var(--nf-field-text); font: inherit; font-size: 1em; line-height: 1.4; outline: 2px solid transparent; outline-offset: 1px; transition: border-color 150ms, box-shadow 150ms; }
#nf-quote-request-form input.nf-input, #nf-quote-request-form select.nf-input { height: 2.75em; padding-top: 0; padding-bottom: 0; }
#nf-quote-request-form textarea.nf-input { min-height: 112px; padding-top: 10px; padding-bottom: 10px; resize: vertical; }
#nf-quote-request-form .nf-input::placeholder { color: var(--nf-placeholder); opacity: 1; }
#nf-quote-request-form .nf-input:hover { border-color: color-mix(in srgb, var(--nf-text) 14%, transparent); }
#nf-quote-request-form .nf-input:focus { border-color: var(--nf-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--nf-accent) 24%, transparent); }
#nf-quote-request-form .nf-input:user-invalid { border-color: var(--nf-error); }
#nf-quote-request-form .nf-input:user-invalid:focus { box-shadow: 0 0 0 3px color-mix(in srgb, var(--nf-error) 24%, transparent); }
#nf-quote-request-form .nf-input:-webkit-autofill { -webkit-text-fill-color: var(--nf-field-text); caret-color: var(--nf-field-text); box-shadow: inset 0 0 0 100px var(--nf-field); }
#nf-quote-request-form .nf-input:-webkit-autofill:focus { box-shadow: inset 0 0 0 100px var(--nf-field), 0 0 0 3px color-mix(in srgb, var(--nf-accent) 24%, transparent); }
#nf-quote-request-form input[type="number"].nf-input { -moz-appearance: textfield; appearance: textfield; }
#nf-quote-request-form input[type="number"].nf-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
#nf-quote-request-form input[type="number"].nf-input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
#nf-quote-request-form input[type="date"].nf-input { min-width: 0; text-align: left; }
#nf-quote-request-form input[type="date"].nf-input::-webkit-date-and-time-value { text-align: left; }
#nf-quote-request-form input[type="date"].nf-input.nf-empty:not(:focus) { color: var(--nf-placeholder); }
#nf-quote-request-form input[type="date"].nf-input::-webkit-calendar-picker-indicator { margin: 0; padding: 4px; border-radius: 6px; opacity: 0.55; cursor: pointer; transition: opacity 150ms; }
#nf-quote-request-form input[type="date"].nf-input::-webkit-calendar-picker-indicator:hover { opacity: 1; }
#nf-quote-request-form select.nf-input { -webkit-appearance: none; appearance: none; padding-right: 40px; background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23525252%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M6%209l6%206l6%20-6%22%2F%3E%3C%2Fsvg%3E"); background-repeat: no-repeat; background-position: right 12px center; background-size: 16px 16px; cursor: pointer; }
#nf-quote-request-form select.nf-input:has(option[value=""]:checked) { color: var(--nf-placeholder); }
#nf-quote-request-form select.nf-input option { color: var(--nf-field-text); }
#nf-quote-request-form .nf-options { display: grid; gap: 8px; }
#nf-quote-request-form .nf-option { display: flex; align-items: center; gap: 10px; min-height: 2.75em; margin: 0; padding: 10px 12px; border: 1px solid color-mix(in srgb, var(--nf-text) 14%, transparent); border-radius: 10px; background: transparent; color: var(--nf-text); font-size: 0.9375em; line-height: 1.4; cursor: pointer; transition: border-color 150ms, background-color 150ms; }
#nf-quote-request-form .nf-option:hover { border-color: color-mix(in srgb, var(--nf-text) 28%, transparent); }
#nf-quote-request-form .nf-option:has(input:checked) { border-color: var(--nf-accent); background: color-mix(in srgb, var(--nf-accent) 8%, transparent); }
#nf-quote-request-form .nf-option span { min-width: 0; overflow-wrap: anywhere; }
#nf-quote-request-form .nf-option input { -webkit-appearance: none; appearance: none; flex: none; width: 18px; height: 18px; margin: 0; border: 1.5px solid var(--nf-control); border-radius: 50%; background: transparent; outline: 2px solid transparent; cursor: pointer; transition: border-color 150ms, box-shadow 150ms; }
#nf-quote-request-form .nf-option input:checked { border-color: var(--nf-accent); background: radial-gradient(circle, var(--nf-on-accent) 0 3px, var(--nf-accent) 3.5px); }
#nf-quote-request-form .nf-option input:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--nf-accent) 24%, transparent); }
#nf-quote-request-form .nf-check { display: flex; align-items: flex-start; gap: 10px; margin: 0; font-size: 0.9375em; line-height: 1.5; color: var(--nf-text); cursor: pointer; }
#nf-quote-request-form .nf-check input { -webkit-appearance: none; appearance: none; flex: none; width: 20px; height: 20px; margin: 1px 0 0; border: 1.5px solid var(--nf-control); border-radius: 6px; background: var(--nf-field) center / 14px 14px no-repeat; outline: 2px solid transparent; cursor: pointer; transition: background-color 150ms, border-color 150ms, box-shadow 150ms; }
#nf-quote-request-form .nf-check input:checked { border-color: var(--nf-accent); background-color: var(--nf-accent); background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23ffffff%22%20stroke-width%3D%223.5%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M5%2012l5%205l10%20-10%22%2F%3E%3C%2Fsvg%3E"); }
#nf-quote-request-form .nf-check input:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--nf-accent) 24%, transparent); }
#nf-quote-request-form .nf-check input:user-invalid { border-color: var(--nf-error); }
#nf-quote-request-form .nf-rating { display: flex; flex-wrap: wrap; margin: 0 0 0 -4px; }
#nf-quote-request-form .nf-rating label { display: inline-flex; padding: 4px; border-radius: 8px; color: var(--nf-control); color: color-mix(in srgb, var(--nf-control) 45%, transparent); cursor: pointer; transition: color 150ms; }
#nf-quote-request-form .nf-rating svg { width: 30px; height: 30px; fill: currentColor; stroke: currentColor; stroke-width: 1.5; stroke-linejoin: round; transition: transform 150ms; }
#nf-quote-request-form .nf-rating input:checked + label, #nf-quote-request-form .nf-rating label:has(~ input:checked) { color: var(--nf-accent); }
#nf-quote-request-form .nf-rating:hover input + label { color: var(--nf-control); color: color-mix(in srgb, var(--nf-control) 45%, transparent); }
#nf-quote-request-form .nf-rating:hover label:hover, #nf-quote-request-form .nf-rating:hover label:has(~ label:hover) { color: var(--nf-accent); }
#nf-quote-request-form .nf-rating label:active svg { transform: scale(0.88); }
#nf-quote-request-form .nf-rating input:focus-visible + label { outline: 2px solid var(--nf-accent); outline-offset: 0; }
#nf-quote-request-form .nf-file { position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; min-height: 112px; padding: 16px; border: 1.5px dashed var(--nf-control); border-color: color-mix(in srgb, var(--nf-control) 55%, transparent); border-radius: 10px; background: var(--nf-field); color: var(--nf-muted); text-align: center; transition: border-color 150ms, background-color 150ms, box-shadow 150ms; }
#nf-quote-request-form .nf-file-input { position: absolute; top: 0; left: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
#nf-quote-request-form .nf-file svg { width: 22px; height: 22px; margin-bottom: 4px; }
#nf-quote-request-form .nf-file-text { margin: 0; max-width: 100%; font-size: 0.875em; overflow-wrap: anywhere; }
#nf-quote-request-form .nf-file-cta { font-weight: 600; color: var(--nf-text); }
#nf-quote-request-form .nf-file-types { margin: 0; font-size: 0.75em; color: var(--nf-placeholder); }
#nf-quote-request-form .nf-file:hover { border-color: var(--nf-control); }
#nf-quote-request-form .nf-file:focus-within { border-color: var(--nf-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--nf-accent) 24%, transparent); }
#nf-quote-request-form .nf-file.nf-file-over { border-color: var(--nf-accent); background: color-mix(in srgb, var(--nf-accent) 8%, var(--nf-field)); }
#nf-quote-request-form .nf-file.nf-file-chosen { border-style: solid; border-color: var(--nf-accent); }
#nf-quote-request-form .nf-file.nf-file-chosen .nf-file-text { font-weight: 600; color: var(--nf-text); }
#nf-quote-request-form .nf-file:has(.nf-file-input:user-invalid) { border-color: var(--nf-error); }
#nf-quote-request-form .nf-sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
#nf-quote-request-form .nf-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
#nf-quote-request-form .nf-captcha { margin-top: 18px; }
#nf-quote-request-form .nf-submit { display: inline-flex; align-items: center; justify-content: center; width: 100%; min-height: 44px; margin: 22px 0 0; padding: 10px 18px; border: 0; border-radius: 10px; background: var(--nf-accent); color: var(--nf-on-accent); font: inherit; font-size: 1em; font-weight: 600; cursor: pointer; transition: filter 150ms, transform 150ms; }
#nf-quote-request-form .nf-submit:hover { filter: brightness(0.94); }
#nf-quote-request-form .nf-submit:active { transform: scale(0.98); }
#nf-quote-request-form .nf-submit:focus-visible { outline: 2px solid var(--nf-accent); outline-offset: 3px; }
#nf-quote-request-form .nf-submit:disabled { opacity: 0.6; cursor: default; }
#nf-quote-request-form .nf-error { margin: 14px 0 0; padding: 10px 12px; border-radius: 10px; background: color-mix(in srgb, var(--nf-error) 10%, transparent); font-size: 0.875em; color: var(--nf-error); }
#nf-quote-request-form.nf-sent { display: flex; flex-direction: column; justify-content: center; min-height: min(var(--nf-height, 0px), 440px); }
#nf-quote-request-form .nf-success { display: flex; flex-direction: column; align-items: center; padding: 20px 0; text-align: center; outline: none; }
#nf-quote-request-form .nf-success-icon { display: flex; flex: none; align-items: center; justify-content: center; width: 56px; height: 56px; margin: 0 0 20px; border-radius: 50%; background: var(--nf-accent); color: var(--nf-on-accent); box-shadow: 0 0 0 8px color-mix(in srgb, var(--nf-accent) 12%, transparent); }
#nf-quote-request-form .nf-success-icon svg { display: block; width: 28px; height: 28px; margin: 0; stroke-dasharray: 1; }
#nf-quote-request-form .nf-success-title { margin: 0; max-width: 24em; font-size: 1.125em; font-weight: 650; line-height: 1.35; letter-spacing: -0.01em; color: var(--nf-text); text-wrap: balance; overflow-wrap: anywhere; }
#nf-quote-request-form .nf-success-text { margin: 0; max-width: 30em; font-size: 0.9375em; line-height: 1.5; color: var(--nf-text); text-wrap: pretty; overflow-wrap: anywhere; }
#nf-quote-request-form .nf-success-title + .nf-success-text { margin-top: 6px; color: var(--nf-muted); }
#nf-quote-request-form.nf-sent .nf-success { animation: nf-success-in 400ms cubic-bezier(0.16, 1, 0.3, 1) both; }
#nf-quote-request-form.nf-sent .nf-success-icon { animation: nf-success-pop 500ms cubic-bezier(0.34, 1.56, 0.64, 1) both; }
#nf-quote-request-form.nf-sent .nf-success-icon path { animation: nf-success-draw 360ms 160ms cubic-bezier(0.65, 0, 0.35, 1) both; }
@keyframes nf-success-in { from { opacity: 0; transform: translateY(6px); } }
@keyframes nf-success-pop { from { transform: scale(0.6); } }
@keyframes nf-success-draw { from { stroke-dashoffset: 1; } }
@media (prefers-reduced-motion: reduce) { #nf-quote-request-form * { transition: none !important; animation: none !important; } }
</style>
<div class="nf-head">
<h2 class="nf-title">Request a quote</h2>
<p class="nf-desc">Tell us about your project and we’ll send an estimate within two business days.</p>
</div>
<div class="nf-fields">
<div class="nf-field">
<label class="nf-label" for="nf-quote-request-form-name">Name<span class="nf-req" aria-hidden="true"> *</span></label>
<input class="nf-input" id="nf-quote-request-form-name" name="name" type="text" autocomplete="name" placeholder="Jane Smith" required>
</div>
<div class="nf-field">
<label class="nf-label" for="nf-quote-request-form-email">Email<span class="nf-req" aria-hidden="true"> *</span></label>
<input class="nf-input" id="nf-quote-request-form-email" name="email" type="email" autocomplete="email" placeholder="jane@example.com" required>
</div>
<div class="nf-field">
<label class="nf-label" for="nf-quote-request-form-company">Company</label>
<input class="nf-input" id="nf-quote-request-form-company" name="company" type="text" placeholder="Acme Inc.">
</div>
<div class="nf-field">
<label class="nf-label" for="nf-quote-request-form-service">Service<span class="nf-req" aria-hidden="true"> *</span></label>
<select class="nf-input" id="nf-quote-request-form-service" name="service" required>
<option value="">Choose a service</option>
<option value="Website design">Website design</option>
<option value="Web development">Web development</option>
<option value="Branding">Branding</option>
<option value="Something else">Something else</option>
</select>
</div>
<div class="nf-field">
<label class="nf-label" for="nf-quote-request-form-budget">Budget</label>
<select class="nf-input" id="nf-quote-request-form-budget" name="budget">
<option value="">Choose a range</option>
<option value="Under $1,000">Under $1,000</option>
<option value="$1,000 to $5,000">$1,000 to $5,000</option>
<option value="$5,000 to $20,000">$5,000 to $20,000</option>
<option value="Over $20,000">Over $20,000</option>
</select>
</div>
<div class="nf-field">
<label class="nf-label" for="nf-quote-request-form-details">Project details<span class="nf-req" aria-hidden="true"> *</span></label>
<textarea class="nf-input" id="nf-quote-request-form-details" name="details" rows="4" placeholder="What do you need, and by when?" required></textarea>
</div>
</div>
<div class="nf-hp" aria-hidden="true"><input type="text" name="_gotcha" tabindex="-1" autocomplete="off"></div>
<button class="nf-submit" type="submit">Request a quote</button>
<p class="nf-error" role="alert" hidden></p>
<div class="nf-success" role="status" tabindex="-1" hidden>
<span class="nf-success-icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" focusable="false"><path d="M5 12l5 5l10 -10" pathLength="1"/></svg></span>
<p class="nf-success-title">Thanks!</p>
<p class="nf-success-text">We’ll send your quote soon.</p>
</div>
</form>
<script>
(function () {
var form = document.getElementById("nf-quote-request-form")
if (!form || !window.fetch) return
var started = Date.now()
var button = form.querySelector('.nf-submit')
var error = form.querySelector('.nf-error')
var success = form.querySelector('.nf-success')
form.addEventListener('submit', function (event) {
event.preventDefault()
var data = new FormData(form)
data.set('_elapsed', String(Date.now() - started))
var label = button.textContent
button.disabled = true
button.textContent = 'Sending...'
error.hidden = true
fetch(form.action, {
method: 'POST',
headers: { Accept: 'application/json' },
body: new URLSearchParams(data),
})
.then(function (response) {
return response.json().catch(function () { return {} }).then(function (body) {
if (!response.ok || !body.ok) throw new Error(body.message || 'Something went wrong. Please try again.')
})
})
.then(function () {
var redirect = form.getAttribute('data-redirect')
if (redirect) {
window.location.href = redirect
return
}
form.style.setProperty('--nf-height', form.offsetHeight + 'px')
Array.prototype.forEach.call(form.children, function (child) {
if (child !== success && child.tagName !== 'STYLE') child.hidden = true
})
form.classList.add('nf-sent')
success.hidden = false
success.focus()
})
.catch(function (reason) {
error.textContent = reason && reason.message ? reason.message : 'Something went wrong. Please try again.'
error.hidden = false
})
.finally(function () {
button.disabled = false
button.textContent = label
if (window.turnstile) window.turnstile.reset()
})
})
})()
</script>
Includes the CSS and a small script that sends the form without a page reload and shows the success message. HTML guide
Pasted into the code for you.
What's in this quote request form
This form asks for the service, a budget range and the project details, so you can reply with an estimate. Company and budget are optional.
| Field | Name | Type | Required |
|---|---|---|---|
| Name | name | Short text | Yes |
email | Yes | ||
| Company | company | Short text | No |
| Service | service | DropdownWebsite design, Web development, Branding, Something else | Yes |
| Budget | budget | DropdownUnder $1,000, $1,000 to $5,000, $5,000 to $20,000, Over $20,000 | No |
| Project details | details | Long text | Yes |
The name is what each answer is saved under in your inbox and CSV export. To add, remove or rename fields, use the field editor above and copy the code again. Fields you add to the code by hand later are saved too, because Nisuform keeps every named input.
Add it to your site
Create a free form
Sign up for Nisuform and create a form with Endpoint only. Copy the endpoint from its Setup tab. The last part is your form key.
Paste the code
Copy the code for your stack and paste it where the form should appear. Replace
YOUR_FORM_KEYwith your key, or type the key under the code and copy it again.Send a test
Submit the form once. It appears in your inbox, and you get an email within seconds.
When to use it
- Agencies and freelancers selling design, development or marketing
- Contractors, cleaners, movers and other local services
- B2B products with custom pricing
- Print shops, caterers and event suppliers
Customize it
Open a field in the editor above to change its label, type, placeholder, options or help text, make it required, or move it up and down. The Text tab edits the title, description, button label and the message people see after they send the form.
Pick a theme and an accent color in the preview and the styled code updates to match. The colors live in CSS variables at the top of the style block, so you can also change --nf-accent by hand later. The button, focus rings and selected options follow it.
List your own services and prices
Edit the <option> elements to match what you sell and how you price it. Answers are saved as text, exactly as written in the option.
Reply faster
On Pro, send each new request to Slack, Discord or Telegram, so your team sees it right away.
Track where leads come from
Nisuform records the page each request was sent from. Add a hidden field like campaign to tell ads and landing pages apart.
Spam protection is built in
Every version includes a hidden _gotcha field. People never see it, bots fill it in, and Nisuform drops those submissions without telling them. The JavaScript versions also send _elapsed, how long the form was open, so a form sent faster than a person could type is dropped too. Rate limits, link checks and duplicate checks run on every submission, and you can turn on Cloudflare Turnstile or Google reCAPTCHA on forms that get a lot of spam. Read more about spam filtering.
Works with any stack
The HTML version works anywhere you can paste HTML, including WordPress, Webflow, Framer, Squarespace and static sites. The React version is a component you import, and the Next.js version is the same component marked as a client component, so it works in the App Router and the Pages Router. The Vue version is a single-file component that also works in Nuxt, the Svelte version works in Svelte 5 and SvelteKit, and the unstyled Astro version ships no client-side JavaScript at all.
Frequently asked questions
What should a request a quote form include?
Contact details, the service the person needs, a budget range and a description of the job.
Can I get quote requests in Slack or my CRM?
On Pro, every request can go to Slack, Discord or Telegram, and to most CRMs through webhooks, Zapier or Make.
Can people attach files like briefs or floor plans?
On Pro, add a file input to accept PDFs and images. Each submission can include up to 5 files.