# Home Services Page

> A React home services and contractor website template: ZIP check, priced services, a quote estimator, before and after projects.

- Type: React template (Templates)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add home-services-page`
- Docs: https://wingo-ui.com/templates/home-services-page

## Interaction

A local contractor's home page. The hero shows Licensed and insured, the star rating, Get a free quote (glides down to the estimator and focuses its question), a tap-to-call button and a ZIP code check: type a ZIP and press Check or Enter, and it says whether the area is served (a returned promise shows a spinner), and the matching town lights up in the service area list. Service cards show starting prices and features; their Get a free quote button picks that service in the estimator and opens its next step. The estimator asks one question per screen: service, project size and timeline (card radio groups, arrow keys move inside each), then name, phone (with a country picker, a bottom sheet on phones), email, ZIP (prefilled from the area check) and notes. A stepper on top shows progress and jumps back to finished steps; Continue and Back move between steps and focus lands on each new question. The price range updates live in a side panel from 1024px and in a strip under the question on smaller screens, announced to screen readers. Contact fields are validated on submit with focus on the first problem; the button spins while onQuote runs, then a confirmation shows the service, range, timeline and a request number with Start another quote. Before and after pictures compare by dragging the handle, tapping, or with arrow keys; on phones the projects form a swipeable snap rail. The process steps, financing and guarantee tiles, service area list, reviews (filter by stars), an emergency band with a call button and Request urgent service (which picks the fastest timeline), and an FAQ with a call card follow. On phones a bar with Call and Get a free quote sticks to the bottom while the estimator is off screen.

## Installation

```bash
npx wingo-ui@latest add home-services-page
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/home-services-page` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `components/templates/home-services-page.tsx`
- `components/templates/home-services-page-data.ts`

npm dependencies: `npm i lucide-react motion`

Wingo UI dependencies (installed automatically):

- [Marketing Section](https://wingo-ui.com/components/marketing-section) ([Markdown](https://wingo-ui.com/components/marketing-section.md))
- [Marketing Header](https://wingo-ui.com/components/marketing-header) ([Markdown](https://wingo-ui.com/components/marketing-header.md))
- [Marketing Footer](https://wingo-ui.com/components/marketing-footer) ([Markdown](https://wingo-ui.com/components/marketing-footer.md))
- [CTA Section](https://wingo-ui.com/components/cta-section) ([Markdown](https://wingo-ui.com/components/cta-section.md))
- [FAQ Section](https://wingo-ui.com/components/faq-section) ([Markdown](https://wingo-ui.com/components/faq-section.md))
- [Steps Section](https://wingo-ui.com/components/steps-section) ([Markdown](https://wingo-ui.com/components/steps-section.md))
- [Badge](https://wingo-ui.com/components/badge) ([Markdown](https://wingo-ui.com/components/badge.md))
- [Button](https://wingo-ui.com/components/button) ([Markdown](https://wingo-ui.com/components/button.md))
- [Image compare](https://wingo-ui.com/components/image-compare) ([Markdown](https://wingo-ui.com/components/image-compare.md))
- [Input](https://wingo-ui.com/components/input) ([Markdown](https://wingo-ui.com/components/input.md))
- [Phone Input](https://wingo-ui.com/components/phone-input) ([Markdown](https://wingo-ui.com/components/phone-input.md))
- [Radio Group](https://wingo-ui.com/components/radio-group) ([Markdown](https://wingo-ui.com/components/radio-group.md))
- [Reviews](https://wingo-ui.com/components/reviews) ([Markdown](https://wingo-ui.com/components/reviews.md))
- [Steps](https://wingo-ui.com/components/steps) ([Markdown](https://wingo-ui.com/components/steps.md))
- [Sticky CTA Bar](https://wingo-ui.com/components/sticky-cta-bar) ([Markdown](https://wingo-ui.com/components/sticky-cta-bar.md))
- [Textarea](https://wingo-ui.com/components/textarea) ([Markdown](https://wingo-ui.com/components/textarea.md))
- [Navigation](https://wingo-ui.com/components/navigation) ([Markdown](https://wingo-ui.com/components/navigation.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
// app/page.tsx
"use client"

import Link from "next/link"
import { HomeServicesPage, type HomeServicesQuoteRequest } from "@/components/templates/home-services-page"

async function requestQuote(request: HomeServicesQuoteRequest) {
  const response = await fetch("/api/quotes", { method: "POST", body: JSON.stringify(request) })
  if (!response.ok) throw new Error("Quote request failed")
  return (await response.json()) as { reference: string }
}

export default function HomeRoute() {
  return <HomeServicesPage linkComponent={Link} onQuote={requestQuote} />
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `design?` | `"classic" \| "bold" \| "minimal"` | `"classic"` | The whole page's look. classic: a blue blueprint grid page with muted bands and raised cards; bold: bigger heavier type, an aurora page with tinted bands and cards for the process; minimal: a plain page with no bands, border-only cards and a vertical process. Each design picks its own backgrounds unless you set them. |
| `business?` | `HomeServicesBusiness` | `"HOME_SERVICES_BUSINESS"` | { name, tagline?, phone, emergencyPhone?, email, license, street, city, hours, rating?, reviewCount?, yearsInBusiness?, nextAvailable?, areas: { name, zips, note? }[] }: the brand, hero badges, phone links, the license line and the served areas the ZIP check looks up. |
| `services?` | `HomeService[]` | `"HOME_SERVICES"` | { id, name, description, icon?, startingAt, unit?, estimate: { min, max }, features?, popular? }: the service cards and the estimator's first question. icon is a drawn glyph by name ("plumbing", "hvac", "roofing", "remodeling", "electrical", "cleaning", "painting", "water-heater"); estimate is a typical medium job before the size and timeline factors. |
| `estimator?` | `HomeServicesEstimator` | `"HOME_SERVICES_ESTIMATOR"` | { sizes, timelines, defaultSize?, defaultTimeline?, round? }: the price rules. sizes and timelines are { id, label, description?, factor } options; the range is the service's estimate times both factors, rounded to round currency units (default 50). |
| `projects?` | `HomeServicesProject[]` | `"HOME_SERVICES_PROJECTS"` | { id, title, location, service, duration, budget?, description, scene?, before?, after? }: the before and after cards. before / after take image urls; without them the scene is drawn ("kitchen", "bathroom", "exterior"). |
| `steps?` | `HomeServicesStep[]` | `"HOME_SERVICES_STEPS"` | { icon?, title, description, meta? }: the How it works steps. |
| `promises?` | `HomeServicesPromise[]` | `"HOME_SERVICES_PROMISES"` | { id, icon?, title, description, href?, linkLabel? }: the financing and guarantee tiles. The promise with id financing links to financingHref. |
| `reviews?` | `ReviewData[]` | `"HOME_SERVICES_REVIEWS"` | The homeowner reviews in the Reviews shape. |
| `reviewsSummary?` | `ReviewsSummaryData` | `"HOME_SERVICES_REVIEWS_SUMMARY"` | { average, count, distribution, aspects? }: the rating summary beside the reviews. |
| `faqs?` | `FaqItem[]` | `"HOME_SERVICES_FAQS"` | { question, answer }: the FAQ. An empty array hides the section. |
| `heroImage?` | `string` | `""` | A photo url for the hero; empty draws a re-roofed house. |
| `defaultService?` | `string` | `""` | A service id picked when the estimator opens (it then starts on the size question); empty starts with no service. |
| `zipPattern?` | `string` | `"^[0-9]{5}$"` | A regular expression a ZIP code must match, in the area check and the contact step. |
| `financingHref?` | `string` | `"/financing"` | Where the financing tile and the footer link go. |
| `currency?` | `string` | `"USD"` | ISO 4217 currency of the starting prices, the estimate range and project budgets. |
| `locale?` | `string` | `""` | Intl locale for prices and numbers; empty uses the UIProvider's, else en-US. |
| `onQuote?` | `(request: HomeServicesQuoteRequest) => void \| HomeServicesQuoteResult \| Promise<HomeServicesQuoteResult \| void>` |  | Gets { service, size, timeline, name, phone (E.164), email, zip, notes, estimate: { min, max } }. A returned promise keeps the button loading; resolve with { reference } to show your own request number; a rejection keeps the form filled and shows labels.quoteFailed. Without it the page pretends for 900ms. |
| `onAreaCheck?` | `(zip: string) => HomeServicesAreaCheckResult \| Promise<HomeServicesAreaCheckResult>` |  | Decides whether a ZIP is served: return true / false, the matching area or its name, or a promise of one (the button spins). Default: the ZIP is looked up in business.areas. |
| `tone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | Accents: icon tiles, chosen estimator cards, the stepper, the matched area and the review stars. |
| `ctaTone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | Get a free quote buttons, the estimator's submit, the sticky bar and the emergency call button. |
| `radius?` | `"none" \| "sm" \| "md" \| "lg"` | `"lg"` | Corners of cards, panels and pictures (0 / 12 / 16 / 24px), with matching inner tiles. |
| `serviceColumns?` | `2 \| 3` | `3` | Service card columns from 1024px; phones get one, tablets two. |
| `background?` | `"plain" \| "soft" \| "glow" \| "aurora" \| "grid" \| "dots"` | `"grid"` | The main background. A design picks its own unless you set one (classic: grid, bold: aurora, minimal: plain). |
| `backgroundColor?` | `string` | `"oklch(0.58 0.12 245)"` | The tint of the main background; each design has its own (blueprint blue, green, slate). |
| `bandBackground?` | `"muted" \| "tinted" \| "gradient" \| "dots"` | `"muted"` | The look of the alternating bands (classic: muted, bold: tinted; minimal has no bands). Cards inside a band are lifted above it. |
| `bandColor?` | `string` | `"oklch(0.58 0.12 245)"` | The tint of tinted and gradient bands; defaults to backgroundColor. |
| `showServices?` | `boolean` | `true` | The service cards with starting prices. |
| `showEstimator?` | `boolean` | `true` | The quote estimator. Off, the quote buttons and the sticky bar call instead. |
| `showProjects?` | `boolean` | `true` | The before and after projects. |
| `showProcess?` | `boolean` | `true` | The How it works steps. |
| `showPromises?` | `boolean` | `true` | The financing and guarantee tiles. |
| `showAreas?` | `boolean` | `true` | The service area list and its ZIP check, plus the ZIP check in the hero. |
| `showReviews?` | `boolean` | `true` | The reviews with the rating summary. |
| `showEmergency?` | `boolean` | `true` | The 24/7 emergency band with tap-to-call. |
| `showFaq?` | `boolean` | `true` | The FAQ with a call card. |
| `stickyBar?` | `boolean` | `true` | On phones, a bar with Call and Get a free quote sticks to the bottom while the estimator is off screen. |
| `header?` | `boolean` | `true` | The sticky header with the brand, section links, the phone number and Get a free quote. |
| `footer?` | `boolean` | `true` | The footer with Services, Company and Help links. |
| `headerProps?` | `Partial<MarketingHeaderProps>` |  | Anything for MarketingHeader (layout, variant, items...). |
| `footerProps?` | `Partial<MarketingFooterProps>` |  | Anything for MarketingFooter (variant, columns, socials...). |
| `compareProps?` | `Partial<ImageCompareProps>` |  | Anything for every project's ImageCompare (handle, aspectRatio, mode...). |
| `processProps?` | `Partial<StepsSectionProps>` |  | Anything for the How it works StepsSection (layout, marker...). |
| `reviewsProps?` | `Partial<ReviewsProps>` |  | Anything for Reviews (layout, sortable, onWriteReview, hasMore...). |
| `faqProps?` | `Partial<FaqSectionProps>` |  | Anything for the FaqSection (layout, searchable, categories...). |
| `emergencyProps?` | `Partial<CtaSectionProps>` |  | Anything for the emergency CtaSection (variant, title, actions...). |
| `stickyBarProps?` | `Partial<StickyCtaBarProps>` |  | Anything for the phone StickyCtaBar (variant, showOn, hideOnScroll...). |
| `animateIn?` | `boolean` | `true` | The hero copy rises in once when the page mounts on the client; never during hydration, under reduced motion or in thumbnails. |
| `linkComponent?` | `(props: LinkComponentProps) => ReactNode` |  | The router link for the header, footer, financing and FAQ links (next/link in an app). |
| `labels?` | `Partial<HomeServicesPageLabels>` |  | Every string of the page (DEFAULT_HOME_SERVICES_PAGE_LABELS), in English by default; some are functions (callNumber, zipFound, estimateRange...). |
| `className?` | `string` |  | Classes for the root. |
| `classNames?` | `Partial<Record<HomeServicesPageSlot, string>>` |  | Classes for header, hero, heroMedia, areaCheck, services, serviceCard, estimator, estimate, confirmation, projects, projectCard, process, promises, areas, reviews, emergency, faq, footer and stickyBar. |

## Parts

### HOME_SERVICES_PAGE_SAMPLE (constant)

All the sample content of Keystone Home Services in one object: business, services, estimator, projects, steps, promises, reviews, reviewsSummary and faqs.

### HOME_SERVICES_BUSINESS (constant)

Keystone Home Services, Columbus, Ohio: phone lines, license, hours, rating and ten served towns with their ZIP codes.

### HOME_SERVICES (constant)

Plumbing, heating and cooling, water heaters, roofing, kitchen and bath remodeling, and electrical, with prices and estimate ranges.

### HOME_SERVICES_ESTIMATOR (constant)

Small / Medium / Large sizes and Within 48 hours / This month / Flexible timelines with their factors.

### HOME_SERVICES_PROJECTS (constant)

A Dublin kitchen, a Bexley bathroom and a Westerville roof, drawn before and after.

### HOME_SERVICES_STEPS (constant)

The four How it works steps.

### HOME_SERVICES_PROMISES (constant)

Financing, the two-year guarantee, flat pricing and background-checked pros.

### HOME_SERVICES_REVIEWS (constant)

Five verified homeowner reviews.

### HOME_SERVICES_REVIEWS_SUMMARY (constant)

4.9 from 1,268 reviews with the star distribution and three aspects.

### HOME_SERVICES_FAQS (constant)

Six questions about estimates, licenses, fees, financing, the guarantee and the service area.

### HOME_SERVICES_PAGE_DESIGNS (constant)

The design names: ["classic", "bold", "minimal"].

### DEFAULT_HOME_SERVICES_PAGE_LABELS (constant)

The English strings.

### drawHomeServicesScene (function)

(kind, "before" | "after") returns an svg data url of a drawn kitchen, bathroom or house exterior, for an img or an ImageCompare layer.

### HomeServicesPageProps (type)

Props of HomeServicesPage, including the attributes of the root element.

### HomeServicesPageLabels (type)

Every string of the page.

### HomeServicesPageSlot (type)

The keys of classNames.

### HomeServicesPageDesign (type)

"classic" | "bold" | "minimal".

### HomeServicesPageRadius (type)

"none" | "sm" | "md" | "lg".

### HomeServicesPageServiceColumns (type)

2 | 3.

### HomeServicesQuoteRequest (type)

What onQuote receives.

### HomeServicesQuoteResult (type)

What onQuote may resolve with: { reference? }.

### HomeServicesAreaCheckResult (type)

What onAreaCheck may return: boolean, an area, its name, null or undefined.

### HomeServicesBusiness (type)

The business: name, phones, license, address, hours, rating and areas.

### HomeServicesArea (type)

{ name, zips, note? }: one served town.

### HomeService (type)

One service card and estimator option.

### HomeServicesEstimator (type)

The price rules: sizes, timelines, defaults and rounding.

### HomeServicesEstimateOption (type)

{ id, label, description?, factor }: one size or timeline.

### HomeServicesProject (type)

One before and after project.

### HomeServicesStep (type)

{ icon?, title, description, meta? }: one How it works step.

### HomeServicesPromise (type)

One financing or guarantee tile.

### HomeServicesIcon (type)

The names of the drawn glyphs for services, steps and promises.

### HomeServicesScene (type)

"kitchen" | "bathroom" | "exterior".

## Examples

- **Bold design**: Bigger, heavier type on an aurora page with tinted bands, and the process as numbered cards.
- **Minimal design**: A plain page with no bands, border-only cards and a vertical process rail.
- **Brand accents and a picked service**: ctaTone and tone set to brand, and defaultService="hvac": the estimator opens on the size question with a live range.
- **A cleaning company**: Other services, prices and copy through props: the same page fits plumbing, HVAC, roofing, remodeling or cleaning.
- **Same team every visit**
- **Got the full deposit back**
- **Pick a plan**: Choose a one-time or recurring clean online in two minutes.
- **Meet your team**: The same two cleaners arrive on time with everything they need.
- **Check the result**: Missed a spot? Tell us within 24 hours and we come back free.
- **When the request fails**: onQuote rejects: the button spins, the answers stay and the form says the request could not be sent.

## See also

- [Restaurant Page](https://wingo-ui.com/templates/restaurant-page) ([Markdown](https://wingo-ui.com/templates/restaurant-page.md))
- [Landing Page](https://wingo-ui.com/templates/landing-page) ([Markdown](https://wingo-ui.com/templates/landing-page.md))
- [Image compare](https://wingo-ui.com/components/image-compare) ([Markdown](https://wingo-ui.com/components/image-compare.md))
- [Form Wizard](https://wingo-ui.com/components/form-wizard) ([Markdown](https://wingo-ui.com/components/form-wizard.md))
- [Reviews](https://wingo-ui.com/components/reviews) ([Markdown](https://wingo-ui.com/components/reviews.md))

---

Canonical: https://wingo-ui.com/templates/home-services-page
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
