# Travel Page

> A React travel agency and tour operator template: tour search, tour cards, a day by day detail sheet, destinations and a custom trip form.

- 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 travel-page`
- Docs: https://wingo-ui.com/templates/travel-page

## Interaction

The search card in the hero filters the tours live as you type a country, region or tour name, pick a travel window in the date range picker (a bottom sheet on phones) or step the travelers up and down: a tour stays when it has a departure in that window with enough seats, and the count is announced. Search glides down to the tours. On phones the search is a stacked card, the tours become a swipeable snap rail and a Find a tour bar slides up once the hero scrolls away, taking you back to the search. Every tour card opens its detail sheet (from the right on desktop, a bottom sheet with a grab handle on phones): the rating, duration, group and pace, the day by day itinerary, what is and is not included, and the dates as radio cards with seats left (sold out and too small departures are disabled, arrow keys move between them). Changing travelers updates the total and the deposit live; Reserve your spot spins, then confirms the held seats. The heart on a card saves the tour with a burst. Region chips filter the destination tiles; a tile with tours searches that destination, a tile without tours jumps to the custom trip form with the destination filled in. The FAQ opens one answer at a time. The custom trip form checks name and email, focuses the first problem, takes budget and interest chips, spins on submit and then thanks you.

## Installation

```bash
npx wingo-ui@latest add travel-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/travel-page` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `components/templates/travel-page.tsx`
- `components/templates/travel-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))
- [FAQ Section](https://wingo-ui.com/components/faq-section) ([Markdown](https://wingo-ui.com/components/faq-section.md))
- [Feature Grid](https://wingo-ui.com/components/feature-grid) ([Markdown](https://wingo-ui.com/components/feature-grid.md))
- [Stats Section](https://wingo-ui.com/components/stats-section) ([Markdown](https://wingo-ui.com/components/stats-section.md))
- [Testimonials Section](https://wingo-ui.com/components/testimonials-section) ([Markdown](https://wingo-ui.com/components/testimonials-section.md))
- [Button](https://wingo-ui.com/components/button) ([Markdown](https://wingo-ui.com/components/button.md))
- [Date Range Picker](https://wingo-ui.com/components/date-range-picker) ([Markdown](https://wingo-ui.com/components/date-range-picker.md))
- [Favorite button](https://wingo-ui.com/components/favorite-button) ([Markdown](https://wingo-ui.com/components/favorite-button.md))
- [Input](https://wingo-ui.com/components/input) ([Markdown](https://wingo-ui.com/components/input.md))
- [Number Input](https://wingo-ui.com/components/number-input) ([Markdown](https://wingo-ui.com/components/number-input.md))
- [Rating](https://wingo-ui.com/components/rating) ([Markdown](https://wingo-ui.com/components/rating.md))
- [Select](https://wingo-ui.com/components/select) ([Markdown](https://wingo-ui.com/components/select.md))
- [Sheet](https://wingo-ui.com/components/sheet) ([Markdown](https://wingo-ui.com/components/sheet.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))
- [Timeline](https://wingo-ui.com/components/timeline) ([Markdown](https://wingo-ui.com/components/timeline.md))
- [Toggle Group](https://wingo-ui.com/components/toggle-group) ([Markdown](https://wingo-ui.com/components/toggle-group.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 { TravelPage, type TravelReservation } from "@/components/templates/travel-page"

async function reserve({ tour, departure, travelers }: TravelReservation) {
  await fetch("/api/holds", {
    method: "POST",
    body: JSON.stringify({ tour: tour.id, departure: departure.id, travelers }),
  })
}

export default function HomeRoute() {
  return <TravelPage linkComponent={Link} onReserve={reserve} />
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `tours?` | `TravelTour[]` | `"TRAVEL_TOURS"` | { id, title, destination, region, route, days, groupSize, rating, reviews, price, previousPrice?, scene, color, image?, badge?, pace?, summary, itinerary: { day, title, description? }[], included, excluded?, departures: { id, start (yyyy-mm-dd), seats, price? }[], href?, favorite? }. scene picks the drawn cover (mountains, coast, desert, city, savanna, fjord, ruins) tinted with color; image replaces it. price is the per person from price; href is carried along for your own links (the card opens the detail sheet). |
| `destinations?` | `TravelDestination[]` | `"TRAVEL_DESTINATIONS"` | { name, region, note?, scene, color, image? } tiles. The tour count and from price come from the tours whose destination matches the name; a destination without tours links to the custom trip form. An empty array hides the section. |
| `reviews?` | `Testimonial[]` | `"TRAVEL_REVIEWS"` | Traveler quotes for the reviews grid (the TestimonialsSection shape, with rating). |
| `faqs?` | `FaqItem[]` | `"TRAVEL_FAQS"` | { question, answer } pairs for the FAQ; an empty array hides it. |
| `stats?` | `StatsSectionItem[]` | `"TRAVEL_STATS"` | { value, decimals?, prefix?, suffix?, label, description? } numbers for the stats section; an empty array hides it. |
| `gallery?` | `TravelGalleryItem[]` | `"TRAVEL_GALLERY"` | { id, caption, location, scene, color, image? } tiles of the gallery band; the first one is large from 768px. |
| `why?` | `FeatureGridItem[]` | `"TRAVEL_WHY"` | The Why travel with us cards: { icon, title, description } for the FeatureGrid. |
| `phone?` | `string` | `"(303) 555-0142"` | The office number in the header, the phone menu and the custom trip band (a tel: link); empty hides it. |
| `deposit?` | `number` | `300` | The deposit per traveler: the last hero point and the line under the total in the detail sheet; 0 hides both. |
| `datePresets?` | `DateRangePreset[]` | `"TRAVEL_DATE_PRESETS"` | { label, value: { from, to } \| () => range, key? } quick ranges beside the search calendar (chips above it on phones); an empty array hides them. |
| `labels?` | `Partial<TravelPageLabels>` |  | Every string on the page: the brand, the hero, the search, the section headings, the detail sheet, the form and its errors. The ones that take a count, a price or a name are functions (call, matches, dayCount, seatsLeft, tourCount, totalFor, deposit, reservedDescription, successDescription and more). |
| `currency?` | `string` | `"USD"` | ISO currency of every price, total, deposit and budget chip. |
| `locale?` | `string` | `""` | Intl locale for prices, dates and numbers; empty uses the UIProvider's, else en-US. |
| `onSearch?` | `(search: TravelSearch) => void` |  | Runs on Search and on a destination tile with { destination, dates: { from, to }, travelers }. The tours filter live either way; use it to fetch or to sync the URL. |
| `onReserve?` | `(reservation: TravelReservation) => void \| Promise<unknown>` |  | Runs on Reserve your spot with { tour, departure, travelers, total }. A returned promise keeps the button loading; a rejection shows labels.reserveFailed in the sheet. Without it the page pretends for 900 ms, then confirms. |
| `onInquiry?` | `(values: TravelInquiryValues) => void \| Promise<unknown>` |  | Runs on Send my trip ideas with { name, email, destination, season, travelers, budget, interests, notes }. A returned promise keeps the button loading; a rejection keeps the form and shows labels.failed. Without it the page pretends for 900 ms, then thanks. |
| `onFavoriteChange?` | `(tour: TravelTour, favorite: boolean) => void \| Promise<unknown>` |  | Runs when a heart is pressed. A returned promise keeps the heart on while pending; a rejection rolls it back. |
| `featuredCount?` | `number` | `6` | Tour cards shown from 768px before Show all; phones get every match in the rail. |
| `tone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | Accents: the stars, the chosen departure, the itinerary dots, the region, budget and interest chips, the date range selection and the stat numbers. |
| `ctaTone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | Search, Plan a trip, Reserve your spot, Send my trip ideas and the Find a tour bar. |
| `favoriteTone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | The heart on a saved tour; neutral is white on the cover. |
| `radius?` | `"none" \| "sm" \| "md" \| "lg"` | `"lg"` | Corners of the tour, destination, gallery and form cards: 0 / 12 / 16 / 24px; the covers inside follow. |
| `background?` | `"plain" \| "soft" \| "glow" \| "aurora" \| "grid" \| "dots"` | `"soft"` | The main background: plain is the bare page color; the others tint the page and add a wash, a glow, an aurora, a grid or dots over the first screen. |
| `backgroundColor?` | `string` | `"oklch(0.66 0.12 60)"` | The tint of the main background, a warm sand by default. |
| `bandBackground?` | `"muted" \| "tinted" \| "gradient" \| "dots"` | `"muted"` | The secondary background of every second section (destinations, stats, reviews and the custom trip form with all sections on). Cards and fields inside are lifted one step above it. |
| `bandColor?` | `string` | `"oklch(0.66 0.12 60)"` | The tint of tinted and gradient bands; left equal to backgroundColor it follows it. |
| `showDestinations?` | `boolean` | `true` | The destination tiles with the region chips (and their header link). |
| `showWhy?` | `boolean` | `true` | The Why travel with us cards. |
| `showStats?` | `boolean` | `true` | The stats section. |
| `showGallery?` | `boolean` | `true` | The gallery band of drawn travel moments. |
| `showReviews?` | `boolean` | `true` | The reviews grid. |
| `showFaq?` | `boolean` | `true` | The FAQ with its contact card. |
| `showInquiry?` | `boolean` | `true` | The Plan a custom trip band and form. Off, Plan a trip in the header calls the office and destinations without tours search instead. |
| `stickySearchBar?` | `boolean` | `true` | The Find a tour bar pinned to the bottom of phones once the hero scrolls away. |
| `header?` | `boolean` | `true` | The sticky header with the brand, the section links, the phone number and Plan a trip. |
| `footer?` | `boolean` | `true` | The footer with Tours, Travel and Company links and the seller of travel line. |
| `headerProps?` | `Partial<MarketingHeaderProps>` |  | Anything for MarketingHeader (layout, variant, items...); wins over the page's own values. |
| `footerProps?` | `Partial<MarketingFooterProps>` |  | Anything for MarketingFooter (variant, columns, socials, newsletter...). |
| `whyProps?` | `Partial<FeatureGridProps>` |  | Anything for the Why travel with us FeatureGrid (layout, columns, card...). |
| `statsProps?` | `Partial<StatsSectionProps>` |  | Anything for the StatsSection (variant, size, countUp...). |
| `reviewsProps?` | `Partial<TestimonialsSectionProps>` |  | Anything for the reviews TestimonialsSection (layout, columns, card...). |
| `faqProps?` | `Partial<FaqSectionProps>` |  | Anything for the FaqSection (layout, searchable, categories...). |
| `stickyBarProps?` | `Partial<StickyCtaBarProps>` |  | Anything for the Find a tour StickyCtaBar (variant, size, showOn...). |
| `animateIn?` | `boolean` | `true` | The hero and the search rise 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, feature and FAQ links (next/link in an app). |
| `className?` | `string` |  | Classes for the root. |
| `classNames?` | `Partial<Record<TravelPageSlot, string>>` |  | Classes for header, hero, search, tours, tour, cover, detail, destinations, destination, why, stats, gallery, reviews, faq, inquiry, form, stickyBar and footer. |

## Parts

### TRAVEL_PAGE_SAMPLE (constant)

All the sample content in one object: tours, destinations, reviews, faqs, stats and gallery.

### TRAVEL_TOURS (constant)

Ten Wayfare Journeys tours on four continents, from Utah's Mighty Five to a Serengeti safari, with itineraries and 2027 departures.

### TRAVEL_DESTINATIONS (constant)

Twelve destinations in Europe, Asia, Africa and the Americas; Portugal and Nepal are custom trips only.

### TRAVEL_REVIEWS (constant)

Six five star traveler quotes, each naming the tour and the month.

### TRAVEL_FAQS (constant)

Seven booking questions: group size, inclusions, deposit, changes, solo travel, fitness and custom trips.

### TRAVEL_STATS (constant)

Four numbers: years running, travelers, average rating and repeat travelers.

### TRAVEL_GALLERY (constant)

Six drawn moments from recent departures with captions and places.

### TRAVEL_WHY (constant)

Six reasons to travel with Wayfare, with lucide icons.

### TRAVEL_DATE_PRESETS (constant)

Spring, summer and fall 2027 and winter 2027 to 2028 as search ranges.

### TRAVEL_BUDGETS (constant)

The four budget chips of the custom trip form as per person [min, max] ranges.

### DEFAULT_TRAVEL_PAGE_LABELS (constant)

The English strings.

### TravelPageProps (type)

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

### TravelPageLabels (type)

Every string of the page.

### TravelPageRadius (type)

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

### TravelPageSlot (type)

The keys of classNames.

### TravelTour (type)

One tour: facts, price, drawn cover, itinerary, inclusions and departures.

### TravelItineraryDay (type)

{ day, title, description? }: one stop of the day by day itinerary.

### TravelDeparture (type)

{ id, start, seats, price? }: one date of a tour.

### TravelDestination (type)

One destination tile.

### TravelGalleryItem (type)

One gallery tile.

### TravelRegion (type)

"europe" | "asia" | "africa" | "americas".

### TravelScene (type)

"mountains" | "coast" | "desert" | "city" | "savanna" | "fjord" | "ruins": the drawn covers.

### TravelSearch (type)

What onSearch receives: { destination, dates, travelers }.

### TravelReservation (type)

What onReserve receives: { tour, departure, travelers, total }.

### TravelInquiryValues (type)

What onInquiry receives.

### TravelBudget (type)

"under-3k" | "3k-6k" | "6k-10k" | "10k-plus".

### TravelInterest (type)

"hiking" | "food" | "culture" | "wildlife" | "beaches" | "photography" | "slow".

### TravelSeason (type)

"flexible" | "spring" | "summer" | "fall" | "winter".

## Examples

- **Brand accents**: tone, ctaTone and a red favoriteTone on a glow page with tinted bands: orange stars, chips and buttons, red hearts.
- **A tour catalog, search first**: Tours, destinations and FAQ only, nine featured tours on square-ish cards and a teal aurora: a lean booking site for one operator.
- **European prices**: currency="EUR" and locale="de-DE": euro prices, German date and number formats, English copy.
- **When booking fails**: onReserve and onInquiry reject: after the spinner the sheet and the form keep everything and show the error.
- **Inside a site layout**: No header, footer or sticky bar, on a plain page with dotted bands: the sections alone.

## See also

- [Date Range Picker](https://wingo-ui.com/components/date-range-picker) ([Markdown](https://wingo-ui.com/components/date-range-picker.md))
- [Listing card](https://wingo-ui.com/components/listing-card) ([Markdown](https://wingo-ui.com/components/listing-card.md))
- [Timeline](https://wingo-ui.com/components/timeline) ([Markdown](https://wingo-ui.com/components/timeline.md))
- [Real Estate Page](https://wingo-ui.com/templates/real-estate-page) ([Markdown](https://wingo-ui.com/templates/real-estate-page.md))
- [Landing Page](https://wingo-ui.com/templates/landing-page) ([Markdown](https://wingo-ui.com/templates/landing-page.md))

---

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