Local Service Hero with Quote Request Form

A hero for a plumber, heating engineer, roofer, cleaner or any business that wins work through enquiries. The left side has a review badge, a headline that names the problem, three trust points with ticks and a large phone number. The right side is a white card holding a quote request form with name, phone, postcode, a service dropdown, an optional message and one bright button. The navy background and yellow action keep the form the brightest thing on the screen. On phones the text comes first and the form follows at full width with large fields.

Code

const services = ["Boiler repair", "Boiler service", "New boiler quote", "Radiators and heating", "Leak or burst pipe", "Something else"];

const promises = [
  "Fixed price agreed before any work starts",
  "Licensed, insured engineers who live locally",
  "12-month guarantee on every repair",
];

export default function LocalServiceHeroWithQuoteForm() {
  const field = "mt-1.5 block w-full rounded-md border border-slate-300 bg-white px-3.5 py-2.5 text-base text-slate-900 placeholder:text-slate-400 focus:border-sky-700 focus:outline-none focus:ring-2 focus:ring-sky-700/30";
  const label = "block text-sm font-medium text-slate-800";
  return (
    <section className="bg-sky-950 px-6 py-14 text-white sm:py-20">
      <div className="mx-auto grid max-w-6xl items-center gap-12 lg:grid-cols-12">
        <div className="lg:col-span-7">
          <p className="inline-flex items-center gap-2 rounded-md bg-white/10 px-3 py-1.5 text-sm font-medium text-sky-100">
            <svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" className="h-4 w-4 text-yellow-400"><path fillRule="evenodd" d="M10 1.5 3 4.2v5.3c0 4.3 2.9 7.6 7 9 4.1-1.4 7-4.7 7-9V4.2l-7-2.7Zm3.5 6.9a.75.75 0 0 0-1.1-1l-3.3 3.5-1.5-1.5a.75.75 0 1 0-1.1 1.1l2.1 2a.75.75 0 0 0 1.1 0l3.8-4.1Z" clipRule="evenodd" /></svg>
            Rated 4.9 out of 5 by 312 neighbours
          </p>
          <h1 className="mt-6 text-4xl font-bold tracking-tight sm:text-5xl lg:text-6xl">
            No heat? No hot water? <span className="text-yellow-400">We can be there today.</span>
          </h1>
          <p className="mt-5 max-w-xl text-lg leading-relaxed text-sky-100">
            Harbour Heating has kept homes in Eastbank and Portside warm since 2009. Tell us what is wrong and we will send a clear price within the hour.
          </p>
          <ul className="mt-8 space-y-3 text-base text-white">
            {promises.map((p) => (
              <li key={p} className="flex items-start gap-3">
                <span className="mt-0.5 flex h-5 w-5 flex-none items-center justify-center rounded-full bg-yellow-400 text-sky-950">
                  <svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" className="h-3.5 w-3.5"><path fillRule="evenodd" d="M16.7 5.3a1 1 0 0 1 0 1.4l-7.5 7.5a1 1 0 0 1-1.4 0L3.3 9.7a1 1 0 1 1 1.4-1.4l3.8 3.8 6.8-6.8a1 1 0 0 1 1.4 0Z" clipRule="evenodd" /></svg>
                </span>
                {p}
              </li>
            ))}
          </ul>
          <div className="mt-9 flex flex-wrap items-center gap-x-5 gap-y-2 border-t border-white/15 pt-7">
            <span className="text-sm text-sky-100">Emergency? Call us, 7am to 10pm:</span>
            <a href="#" className="inline-flex min-h-[44px] items-center gap-2 rounded-md text-2xl font-bold tracking-tight text-white underline decoration-yellow-400 decoration-2 underline-offset-4 hover:text-yellow-400 focus:outline-none focus-visible:ring-2 focus-visible:ring-yellow-400 focus-visible:ring-offset-4 focus-visible:ring-offset-sky-950">
              <svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" className="h-5 w-5 text-yellow-400"><path d="M3.5 2A1.5 1.5 0 0 0 2 3.5V5c0 7.2 5.8 13 13 13h1.5a1.5 1.5 0 0 0 1.5-1.5v-2.3a1 1 0 0 0-.76-.97l-3.4-.85a1 1 0 0 0-1.03.36l-.74.99a.9.9 0 0 1-1.03.3 9.03 9.03 0 0 1-4.97-4.97.9.9 0 0 1 .3-1.03l.99-.74a1 1 0 0 0 .36-1.03l-.85-3.4A1 1 0 0 0 5.8 2H3.5Z" /></svg>
              020 7946 0155
            </a>
          </div>
        </div>

        <div className="lg:col-span-5">
          <form action="#" className="rounded-lg bg-white p-6 text-slate-900 shadow-2xl shadow-sky-950 sm:p-8">
            <h2 className="text-2xl font-bold tracking-tight">Get a free quote</h2>
            <p className="mt-1.5 text-sm text-slate-600">Takes about a minute. No call-out fee, no obligation.</p>
            <div className="mt-6 grid gap-4 sm:grid-cols-2">
              <div className="sm:col-span-2">
                <label htmlFor="quote-name" className={label}>Your name</label>
                <input id="quote-name" name="name" type="text" autoComplete="name" required placeholder="Priya Nair" className={field} />
              </div>
              <div>
                <label htmlFor="quote-phone" className={label}>Phone number</label>
                <input id="quote-phone" name="phone" type="tel" autoComplete="tel" required placeholder="07700 900123" className={field} />
              </div>
              <div>
                <label htmlFor="quote-postcode" className={label}>Postcode</label>
                <input id="quote-postcode" name="postcode" type="text" autoComplete="postal-code" required placeholder="EB4 2QT" className={field} />
              </div>
              <div className="sm:col-span-2">
                <label htmlFor="quote-service" className={label}>What do you need?</label>
                <div className="relative">
                  <select id="quote-service" name="service" defaultValue={services[0]} className={`${field} appearance-none pr-10`}>
                    {services.map((s) => <option key={s}>{s}</option>)}
                  </select>
                  <svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" className="pointer-events-none absolute bottom-3 right-3 h-5 w-5 text-slate-500"><path fillRule="evenodd" d="M5.2 7.2a.75.75 0 0 1 1.06 0L10 10.94l3.74-3.74a.75.75 0 1 1 1.06 1.06l-4.27 4.27a.75.75 0 0 1-1.06 0L5.2 8.26a.75.75 0 0 1 0-1.06Z" clipRule="evenodd" /></svg>
                </div>
              </div>
              <div className="sm:col-span-2">
                <label htmlFor="quote-details" className={label}>Anything we should know? <span className="font-normal text-slate-500">(optional)</span></label>
                <textarea id="quote-details" name="details" rows={3} placeholder="The boiler shows error F28 and the pressure keeps dropping." className={field} />
              </div>
            </div>
            <button type="submit" className="mt-6 flex w-full items-center justify-center gap-2 rounded-md bg-yellow-400 px-5 py-3.5 text-base font-bold text-sky-950 transition hover:bg-yellow-300 focus:outline-none focus-visible:ring-2 focus-visible:ring-sky-700 focus-visible:ring-offset-2">
              Request my quote
              <svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" className="h-5 w-5"><path fillRule="evenodd" d="M3 10a.75.75 0 0 1 .75-.75h10.64l-3.2-3.2a.75.75 0 1 1 1.06-1.06l4.5 4.5a.75.75 0 0 1 0 1.06l-4.5 4.5a.75.75 0 1 1-1.06-1.06l3.2-3.2H3.75A.75.75 0 0 1 3 10Z" clipRule="evenodd" /></svg>
            </button>
            <p className="mt-4 flex items-center justify-center gap-2 text-center text-sm text-slate-600">
              <span className="h-2 w-2 flex-none rounded-full bg-emerald-500" />
              We reply within one hour, 7am to 8pm
            </p>
          </form>
        </div>
      </div>
    </section>
  );
}

Install with one command: npx shadcn@latest add https://we.inc/r/local-service-hero-with-quote-form.json

When to use it

How to customize it

Accessibility

Questions

How many fields should a quote form have?

As few as you need to reply with a useful answer. Name, a way to contact the person and what they need is usually enough. Each extra required field lowers the number of people who finish, so mark anything else as optional.

Should the form or the phone number come first?

Show both. Some people want to talk straight away, especially in an emergency, and others would rather type. Here the form has the strongest colour and the number sits under the trust points in large type.

How do I make the form send an email?

The component is only the front end. Set the form's `action` to a form service or your own endpoint, or add an `onSubmit` handler that posts the fields. Then show a short confirmation message in place of the form.

Can I put the form on the left?

Yes. Add `lg:order-first` to the form column. On phones the text still comes first, which is better because visitors need to know who you are before they give you a phone number.

Does it work without JavaScript?

Yes. It is a plain HTML form with native required fields and a native select, so it submits normally with no script.

Related: Split Hero with Email Capture, Restaurant Split Hero with Opening Hours, Big Headline Hero with Stats Row, Button Set: Primary, Secondary, Ghost, Icon and Loading.

More hero sections or all free UI components. Start for free to have a full site built around it.

More in Hero Sections

We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.

Product

Who It's For

Features

Resources

Company

View Sitemap