Skip to main content

Introduction

The Vaylo Widget is a powerful, embeddable booking solution that allows you to integrate tour booking functionality directly into your website. Built with React and TypeScript, it provides a seamless user experience with support for multiple languages, customizable themes, and comprehensive payment integration.

Note: This widget is designed to be framework-agnostic and can be embedded in any web application regardless of the underlying technology stack.

Key Features

  • 🌍 Multi-language support (English, Norwegian, Swedish)
  • 🎨 Fully customizable themes and styling
  • 💳 Integrated payment processing
  • 📱 Responsive design for all devices
  • 🔒 Secure booking flow
  • ♿ Accessibility-focused UI components

Choose your widget

The same script powers more than one widget. Pick the one that matches the page you are building:

  • type: "default" — search form, tour list, and the complete booking flow. Use it on your search page and on your booking page.
  • type: "trip_details" — a compact "book this trip" box listing the upcoming departures of one tour. Use it on a single tour page. This is the most common integration, and it has its own guide with a copy-paste example, a tour-tag lookup, and a troubleshooting table.

Installation

Follow these three simple steps to integrate the Vaylo booking widget into your website:

Step 1: Embed the Widget Script

Add the widget script inside your <head> tag. Select the appropriate environment URL:

Production
<script src="https://widget.bookingsystem.se/res_widget_script.iife.js"></script>
Staging
<script src="https://stage.widget.bookingsystem.se/res_widget_script.iife.js"></script>
Development
<script src="https://dev.widget.bookingsystem.se/res_widget_script.iife.js"></script>

Step 2: Create a Container Element

Add a <div> element in your page where you want the widget to appear. You can use any ID you prefer:

<div id="vaylo-widget"></div>

Step 3: Initialize the Widget

Add the initialization script, typically at the bottom of your <body> tag or in your JavaScript file:

window.addEventListener("load", function () {
  Widget.mount({
    containerID: "vaylo-widget",
  });
});
Note: The type parameter will default to "default" if not provided.

Requirements

For Default Mode (tour list with search):

  • A valid container element ID

For Trip Details Mode (type: "trip_details"):

  • A valid container element ID
  • type: "trip_details" — without it you get the search widget instead
  • tourTag is required: the ID of the tour tag whose departures are listed. Without it the widget shows an empty state. See Finding your Tour Tag ID.
  • redirects.bookingURL is required (unless it is already set on your Vaylo account): the absolute URL of the page where the Book button sends the visitor. That page must mount the "default" widget.
  • departureFrom (optional, defaults to today) and duration in months (optional, defaults to 3) control which departures are listed
Setting up a tour page? Follow the dedicated Trip Details Widget guide — it walks through the whole integration in four steps with a full copy-paste example.

Quick Start

Complete Integration Example

Here's a complete example showing all three steps together:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Booking Site</title>

    <!-- Step 1: Embed the Widget Script -->
    <script src="https://widget.bookingsystem.se/res_widget_script.iife.js"></script>
</head>
<body>
    <!-- Step 2: Create Container Element -->
    <div id="vaylo-widget"></div>

    <!-- Step 3: Initialize the Widget -->
    <script>
        window.addEventListener("load", function () {
            Widget.mount({
                containerID: "vaylo-widget",
            });
        });
    </script>
</body>
</html>
Success! The widget should now be visible and functional on your page.
Next Steps: Customize the widget appearance and behavior using the configuration options detailed in the sections below. Building a page for one specific tour instead? Go straight to the Trip Details Widget guide.

Widget Types

There is only one script to load. The type option decides which widget that script renders inside your container. You can use different types on different pages of the same website.

type What the visitor sees Typical page Must be provided
"default" default value Search form, tour list, and the full booking flow (traveller details, payment, confirmation) Search / "All tours" page and the booking page containerID
"trip_details" A compact list of the upcoming departures for one tour tag, with price, availability, and a Book button A single tour / campaign landing page (usually in the sidebar) containerID, tourTag, redirects.bookingURL
"payment" Only the payment screen for a booking that already exists A "pay for your booking" page opened from a Vaylo payment link containerID plus the booking parameters that Vaylo adds to the link
Which one do I need? If you want a "Book this trip" box on a page that describes one specific tour, you want "trip_details". If you want visitors to search across all of your tours, you want "default".

Trip Details Widget (type: "trip_details")

The Trip Details widget turns any page on your website into a bookable tour page. You write the tour description, photos, and itinerary yourself — the widget adds the part that has to stay in sync with your booking system: the upcoming departure dates, live prices, remaining seats, and a Book button.

The short version. Load the script, add an empty <div>, and call Widget.mount() with three things: containerID, tourTag (which tour to show), and redirects.bookingURL (where the Book button should send the visitor). Nothing else is required — language, currency, colours, and fonts are already taken from your Vaylo account settings.

What the widget renders

Inside your container, from top to bottom, the widget renders:

  • Your phone number and e-mail address as click-to-call and click-to-mail links. These come from your Vaylo account settings, not from the mount configuration.
  • One card per departure, sorted by departure date (soonest first). Each card shows the departure and return date, the tour name, the price per person, how many seats are left, and a Book button.
  • A "no suggestions available" message when no departure matches your settings. See Troubleshooting if you see this unexpectedly.
Note: This widget has no search form and no pagination — it always lists every matching departure. Options that only exist for searching (searchKeys, itemPerPage, showAllToursByDefault) have no effect here.

How the two pages fit together

The Trip Details widget is deliberately small: it lists departures and hands the visitor over to your booking page. That means a working setup uses two pages:

  PAGE 1 - Your tour page                    PAGE 2 - Your booking page
  https://yoursite.com/tours/lofoten         https://yoursite.com/booking

  +------------------------------+           +------------------------------+
  |  Your own content:           |           |                              |
  |  photos, itinerary, text     |           |   Widget.mount({             |
  |                              |  click    |     containerID: "...",      |
  |  +------------------------+  |  "Book"   |     type: "default"          |
  |  | trip_details widget    |  | --------> |   })                         |
  |  |  15 Jun - 22 Jun       |  |           |                              |
  |  |  Lofoten Islands       |  |           |   Traveller details ->       |
  |  |  12 500 SEK per person |  |           |   Payment -> Confirmation    |
  |  |  4 spots left  [Book]  |  |           |                              |
  |  +------------------------+  |           +------------------------------+
  +------------------------------+

  type: "trip_details"                       type: "default"
  tourTag: 4                                 (reads the tour from the URL)
  redirects.bookingURL: page 2

When the visitor clicks Book, the widget sends them to redirects.bookingURL and appends the selected departure to the URL:

https://yoursite.com/booking?widget_screen=booking&tour=237

The "default" widget on your booking page reads those two parameters and opens the booking flow for that exact departure. You do not have to build anything to read the URL yourself.

Already have a booking page? Then you only need to add the Trip Details widget and point redirects.bookingURL at the existing page. Re-use the same booking page for every tour page — one booking page is enough for your whole website.

Installation in 4 steps

Step 1: Load the script (once per page)

Add this inside <head>. It is the same script for every widget type — see Installation for the staging and development URLs.

<script src="https://widget.bookingsystem.se/res_widget_script.iife.js"></script>

Step 2: Add an empty container

Put this where you want the booking box to appear — typically in the sidebar next to your tour description. Any ID works, as long as it matches containerID in step 3.

<div id="trip-details-widget"></div>

Step 3: Mount the Trip Details widget

Add this near the end of your <body>. This is the smallest configuration that works:

<script>
    window.addEventListener("load", function () {
        Widget.mount({
            containerID: "trip-details-widget",
            type: "trip_details",
            tourTag: 4,
            redirects: {
                bookingURL: "https://yoursite.com/booking"
            }
        });
    });
</script>

Step 4: Make sure your booking page has the default widget

On the page you used as bookingURL, mount the "default" widget. This is the page that collects traveller details and takes the payment.

<div id="booking-widget"></div>

<script>
    window.addEventListener("load", function () {
        Widget.mount({
            containerID: "booking-widget",
            type: "default",
            redirects: {
                successURL: "https://yoursite.com/thank-you"
            }
        });
    });
</script>
That's it. Open your tour page — you should see the upcoming departures. Click Book and you should land on your booking page with the booking flow already open for that departure.

Complete copy-paste example

A full tour page with your own content and the Trip Details widget in a sidebar. Replace tourTag and the URLs with your own values.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Lofoten Islands - Book your trip</title>

    <!-- Step 1: Load the widget script -->
    <script src="https://widget.bookingsystem.se/res_widget_script.iife.js"></script>

    <style>
        .tour-page {
            display: grid;
            grid-template-columns: 1fr 360px;
            gap: 2rem;
            max-width: 1200px;
            margin: 0 auto;
        }

        /* Keep the booking box visible while the visitor scrolls */
        #trip-details-widget {
            position: sticky;
            top: 2rem;
            border: 1px solid #e5e7eb;
            border-radius: 12px;
            overflow: hidden;
        }

        @media (max-width: 900px) {
            .tour-page { grid-template-columns: 1fr; }
            #trip-details-widget { position: static; }
        }
    </style>
</head>
<body>
    <div class="tour-page">
        <article>
            <h1>Lofoten Islands</h1>
            <p>Your own tour description, photos and itinerary go here.</p>
        </article>

        <!-- Step 2: The widget container -->
        <aside>
            <div id="trip-details-widget"></div>
        </aside>
    </div>

    <!-- Step 3: Mount the widget -->
    <script>
        window.addEventListener("load", function () {
            Widget.mount({
                containerID: "trip-details-widget",
                type: "trip_details",

                // Which tour to show (required)
                tourTag: 4,

                // Which departures to show (optional)
                departureFrom: "2026-01-01",
                duration: 12,

                // Where the Book button goes (required, must be absolute)
                redirects: {
                    bookingURL: "https://yoursite.com/booking",
                    successURL: "https://yoursite.com/thank-you"
                },

                // Presentation (optional - falls back to your account settings)
                locale: "en",
                currencyCode: "SEK",
                dateFormat: "dd MMM, yyyy"
            });
        });
    </script>
</body>
</html>

Configuration reference for this widget type

Every option below is optional except the three marked Required. Any option you leave out falls back to your Vaylo account settings, and then to the widget default.

Option Type Default What it does here
containerID Required string ID of the empty <div> the widget is rendered into. Must exist in the page before mount() is called.
type Required string "default" Must be "trip_details" for this widget. If you leave it out you get the search widget instead.
tourTag Required number The tour tag ID whose departures are listed. Without it the widget renders the empty state and loads nothing. See Finding your Tour Tag ID.
redirects.bookingURL string Your account's booking redirect URL Absolute URL of the page that hosts the "default" widget. The Book button sends the visitor here. Required unless it is already configured on your Vaylo account.
redirects.successURL string Your account's success redirect URL Absolute URL the visitor lands on after a completed booking. Used by the booking page, not by this widget — set it there.
departureFrom string today Earliest departure date to list, in YYYY-MM-DD format.
duration number 3 How many months after departureFrom to keep listing departures.
currencyCode string Your account's default currency ISO code the prices are shown in, for example "SEK", "NOK", "EUR".
locale string Your account's default language Language of the widget's own labels ("per person", "spots left", "Book"): "en", "sv", or "nb".
dateFormat string Your account's default date format How the departure and return dates are written, for example "dd MMM, yyyy".
allowWaitlistBooking boolean Your account's waitlist setting When true, fully booked departures show Join waitlist instead of a disabled button.
theme object Your account's theme Colours, fonts, and font sizes. See Theme Customization.
classPrefix string "rm_widget" Prefix for the widget's CSS class names, if you need to avoid clashing with your own styles. See Styling.
Two easy mistakes. redirects.bookingURL must be an absolute URL including https:// — a relative path such as "/booking" will not work. departureFrom must be exactly YYYY-MM-DD and duration must be a number (12, not "12"); anything else can stop the widget from rendering.

Finding your Tour Tag ID

tourTag is a number, not the tag's name. It is the single value you need per tour page, so it is worth writing them all down once. There are three ways to get it:

Option 1: From your Vaylo admin panel (recommended)

Open the tour tags list in your Vaylo admin panel. Each tag has an ID — that number is your tourTag value. If you cannot find it, ask your Vaylo contact for "the tour tag IDs for our tours"; it is a one-line answer.

Option 2: Read it from a search page URL

If you already run the "default" widget somewhere with the Tour tag filter enabled:

  1. Open that page.
  2. Pick your tour in the Tour tag filter and run the search.
  3. Look at the address bar. You will see tour_tag=4 — the number is the ID you need.
https://yoursite.com/search?widget_screen=list&tour_tag=4
                                                        ^
                                              this is your tourTag

Option 3: One tag per tour page

The usual setup is one tour tag per tour, so each tour page mounts the widget with its own tourTag. Keep a small table like this and the integration stays trivial to maintain:

Your page tourTag
/tours/lofoten 4
/tours/iceland 7
/tours/greenland 11
Using a CMS? Store the tour tag ID in a custom field on your tour page template and print it into the Widget.mount() call. Then editors can publish new tour pages without touching any code.

Which departures are shown

The widget always lists departures inside a date window that it calculates for you:

window start = departureFrom                     (default: today)
window end   = departureFrom + duration months   (default: 3 months)

Departures outside that window are not listed, even if they are on sale in Vaylo. Departures are always sorted with the soonest first.

Goal Configuration Result
Always show the next 12 months (recommended) duration: 12 Rolling window from today. Nothing to maintain.
Show one specific season departureFrom: "2026-06-01", duration: 3 Only departures between 1 Jun 2026 and 1 Sep 2026.
Show everything you have on sale duration: 36 A three-year window — in practice every published departure.
Watch out for fixed dates. If you set departureFrom to a hard-coded date, that date will eventually be in the past and the list will look wrong. Unless you are showing one specific season, leave departureFrom out and only set duration — the window then follows today's date automatically.

Sold-out departures and the waitlist

Each card shows the remaining capacity that Vaylo reports for that departure:

  • Seats available — the card shows "N spots left" and an active Book button.
  • Fully booked, waitlist off — the card shows "Fully booked" and the Book button is disabled. The departure stays visible, so visitors can see that the trip is popular.
  • Fully booked, waitlist on — the button becomes Join waitlist and continues into the normal booking flow, which is registered as a waitlist booking.

The waitlist is normally switched on for your whole account in Vaylo. Set allowWaitlistBooking: true in Widget.mount() only if you want to enable it for this page alone.

Styling the widget

Colours, fonts, and font sizes come from your Vaylo theme, and you can override them per page with the theme option. Two things are left to your own CSS: where the widget sits, and how wide it is.

The widget fills the width of its container, so size the container, not the widget. This is a typical sidebar placement:

#trip-details-widget {
    width: 100%;
    max-width: 360px;
    position: sticky;   /* follows the visitor while scrolling */
    top: 2rem;
    border: 1px solid #e5e7eb;
    border-radius: 12px;
    overflow: hidden;
}

@media (max-width: 900px) {
    #trip-details-widget {
        max-width: 100%;
        position: static;
    }
}

CSS class hooks

Every element the widget renders carries a stable class name, so you can style individual parts without fighting the theme. The names below use the default prefix rm_widget; change it with the classPrefix option (classPrefix: "acme" gives acme_tour_name, and so on).

Class Element
.widget-main-container Outermost wrapper around the whole widget
.rm_widget_tour_suggestion_container The departure list as a whole
.rm_widget_telephone Click-to-call phone row
.rm_widget_email Click-to-mail e-mail row
.rm_widget_tour_suggestion_list_item One departure card
.rm_widget_dates Departure – return date line
.rm_widget_tour_name Tour name
.rm_widget_availability "N spots left" / "Fully booked" pill
.rm_widget_tour_suggestion_book_button Book / Join waitlist button
Tip: Prefer the theme option over CSS overrides for colours and fonts. Theme values are applied consistently across every screen of the booking flow, so the booking page keeps matching your tour page.

Several tour pages on one website

Each tour page mounts the widget once with its own tourTag and points at the same shared booking page. Nothing else changes between pages:

// On /tours/lofoten
Widget.mount({
    containerID: "trip-details-widget",
    type: "trip_details",
    tourTag: 4,
    duration: 12,
    redirects: { bookingURL: "https://yoursite.com/booking" }
});

// On /tours/iceland
Widget.mount({
    containerID: "trip-details-widget",
    type: "trip_details",
    tourTag: 7,
    duration: 12,
    redirects: { bookingURL: "https://yoursite.com/booking" }
});
One widget per page. Call Widget.mount() once per page load. If you need two booking boxes on the same page (for example one at the top and one at the bottom), talk to your Vaylo contact first — use a single container and CSS placement instead.

Single-page apps (React, Vue, Next.js, ...)

Mount the widget after the container element exists in the DOM, and only once per page. In React that means calling Widget.mount() from useEffect after the script has loaded:

import { useEffect } from "react";

const SCRIPT_SRC = "https://widget.bookingsystem.se/res_widget_script.iife.js";

export const TripDetailsWidget = ({ tourTag }) => {
    useEffect(() => {
        const mountWidget = () =>
            window.Widget?.mount({
                containerID: "trip-details-widget",
                type: "trip_details",
                tourTag,
                duration: 12,
                redirects: { bookingURL: "https://yoursite.com/booking" },
            });

        // The script may already be there from an earlier page load
        if (window.Widget) {
            mountWidget();
            return;
        }

        const script = document.createElement("script");
        script.src = SCRIPT_SRC;
        script.async = true;
        script.addEventListener("load", mountWidget);
        document.head.appendChild(script);

        return () => script.removeEventListener("load", mountWidget);
    }, [tourTag]);

    return <div id="trip-details-widget" />;
};
Mount once per page load. The widget is built to be mounted a single time after the page loads. If your app moves between two widget pages with a client-side route change, make those links normal page loads (a plain <a href> rather than a client-side router push) so the widget starts from a clean state.
Note: The Book button performs a normal browser navigation to bookingURL. That is intentional — it works the same whether your booking page is part of the same app or a completely separate page.

Troubleshooting

What you see Most likely cause Fix
Nothing at all appears The container ID does not match, or mount() ran before the element existed Check that containerID matches the <div id="..."> exactly, and call mount() from window.addEventListener("load", ...). The browser console will log [Widget] Mount target "..." not found if the element is missing.
The search form and tour list appear instead of the departure list type is missing or misspelled Set type: "trip_details" — exactly that string, in lowercase with an underscore.
"There are no suggestions available at the moment." tourTag is missing or wrong Confirm the ID as described in Finding your Tour Tag ID. Pass it as a number (tourTag: 4).
Same empty message, but the tag is correct No departure falls inside the date window Increase duration (try duration: 12) and remove any hard-coded departureFrom in the past. See Which departures are shown.
Fewer departures than expected The missing departures are outside the window, or not published in Vaylo Widen duration, then check that the departures are published and carry the same tour tag.
The Book button does nothing redirects.bookingURL is empty, or is a relative path Set it to an absolute URL including https://, for example "https://yoursite.com/booking".
The Book button lands on a page with no booking flow The target page has no "default" widget Mount the "default" widget on that page — see step 4 of Installation in 4 steps.
The Book button is greyed out The departure is fully booked and the waitlist is off Expected behaviour. Enable the waitlist in Vaylo, or set allowWaitlistBooking: true, to offer Join waitlist instead.
Prices are in the wrong currency currencyCode is not set for this page Add the ISO code, for example currencyCode: "NOK". Without it the account default is used.
Labels are in the wrong language locale is not set for this page Add locale: "sv", "nb", or "en". On a multi-language site, pass the language of the current page.
Phone number or e-mail address is wrong or missing Those come from your Vaylo account settings Update the contact details in Vaylo. They cannot be set from Widget.mount().
The widget is too wide or too narrow The widget fills its container Size the container with your own CSS — see Styling the widget.

Go-live checklist

  • Script tag points at the production URL (widget.bookingsystem.se), not staging or development.
  • type: "trip_details" and tourTag are set on every tour page, with the right tag per page.
  • redirects.bookingURL is an absolute https:// URL, and that page mounts the "default" widget.
  • redirects.successURL is set on the booking page and points at your own thank-you page.
  • duration is wide enough to cover the departures you sell, and no stale departureFrom is left behind.
  • locale and currencyCode match the page the widget is on.
  • You completed one real test booking end to end, and checked the widget on a phone-sized screen.

Configuration Options

The widget accepts a configuration object with the following properties. You can easily customize the widget's appearance and behavior:

Property Type Description
containerID string ID of the DOM element where the widget will be mounted
type string Which widget to render: "default" (tour list with search and the booking flow), "trip_details" (departure list for one tour — see the Trip Details Widget guide), or "payment". Defaults to "default".
theme object Full control over colors, fonts, font sizes, and header height
locale string Language of the widget (e.g., "en", "sv", "nb")
currencyCode string ISO currency code (e.g., "SEK", "USD", "EUR")
itemPerPage number Number of tours to display per page. Only used by type: "default" — the trip details widget is not paginated.
dateFormat string Customize how dates appear (e.g., "dd MMM, yyyy")
departureFrom string Earliest departure date to list (Format: "YYYY-MM-DD"). Defaults to today. Used by type: "trip_details" only.
duration number Number of months to look ahead from departureFrom. Defaults to 3. Used by type: "trip_details" only.
tourTag number The tour tag ID whose departures are listed. Required for type: "trip_details" and ignored by the other types. See Finding your Tour Tag ID.
allowWaitlistBooking boolean Allow visitors to join a waitlist on fully booked departures. Defaults to your Vaylo account setting.
showAllToursByDefault boolean Show the tour list before the visitor searches. Only used by type: "default".
classPrefix string Prefix for the widget's CSS class names. Defaults to "rm_widget". See CSS class hooks.
searchKeys array Define which search filters are available to users. See Search Behavior section for all available options.
redirects object Define where users are sent on search, booking, and after booking success. See Redirect URLs section for details.

Example Configuration

Widget.mount({
    containerID: "vaylo-widget",
    type: "default",
    locale: "en",
    currencyCode: "SEK",
    itemPerPage: 10,
    dateFormat: "dd MMM, yyyy",
    departureFrom: "2025-04-24",
    duration: 3,
    tourTag: 101,
    showAllToursByDefault: false,
    searchKeys: [
        "tour_tag",
        "tags",
        "country",
        "location",
        "category",
        "passenger_types",
        "from_departure_date",
        "to_departure_date",
    ],
    redirects: {
        searchURL: "https://yourdomain.com/search",
        bookingURL: "https://yourdomain.com/bookings",
        successURL: "https://yourdomain.com/success",
    },
    theme: {
        color: {
            themeColor: "#FF5733",
            textColor: "#0A2540",
            secondaryTextColor: "#999999",
            backgroundColor: "#ffffff",
            foregroundColor: "#E9EBEE",
            borderColor: "#D1D9E6",
        },
        font: {
            default: "Inter",
            heading: "Playfair Display",
        },
        size: {
            heading: 31,
            title: 20,
            regular: 16,
            default: 13,
        },
        headerHeight: 81,
    },
});

What You Can Change Yourself

You can freely adjust the following configuration options to customize the widget behavior and appearance:

Colors and Fonts

Customize inside the theme section:

  • Theme colors (themeColor, textColor, backgroundColor, etc.)
  • Font families (default and heading fonts)
  • Font sizes (heading, title, regular, default)
  • Header height

Text Language

Set the locale property to change the widget language:

  • "en" - English
  • "sv" - Swedish
  • "nb" - Norwegian Bokmål

Displayed Tours

Control which tours are shown by adjusting:

  • tourTag - The tour tag ID whose departures are listed (type: "trip_details" only, and required there)
  • departureFrom - Earliest departure date (Format: YYYY-MM-DD, defaults to today; type: "trip_details" only)
  • duration - Number of months to look ahead (defaults to 3; type: "trip_details" only)
  • showAllToursByDefault - Whether to display all tours before the user searches (type: "default" only)
Note: With type: "default", the shown date range comes from the visitor's own search, not from departureFrom / duration.

Search Behavior

Configure available search filters using the searchKeys array. Important: Keys must match exactly (case-sensitive).

Available search keys:

  • "tour_tag"
  • "tags"
  • "category"
  • "country"
  • "location"
  • "destination"
  • "passenger_types"
  • "number_of_passengers"
  • "from_departure_date"
  • "to_departure_date"
  • "pickup_locations"
  • "departure_date"
Important: Be careful with spelling - search keys must match exactly as shown above.

Redirect URLs

Define user flow by setting URLs in the redirects section:

  • searchURL - Where users are redirected on search
  • bookingURL - Where users go when booking
  • successURL - Post-booking success page

Widget Container CSS

Customize the container element's appearance with your own CSS:

#vaylo-widget {
    width: 1200px;
    max-width: 100%;
    margin: -10rem auto 0;
}

You can adjust width, margins, padding, and other CSS properties to fit your page layout.

Theme Customization

Customize the widget's appearance by providing a theme configuration with colors, fonts, sizes, and header height:

Color Properties

Property Type Description
themeColor string Primary brand color for buttons, links, and accents
textColor string Main text color
secondaryTextColor string Secondary text color for less prominent text
backgroundColor string Background color of the widget
foregroundColor string Foreground/surface color for cards and panels
borderColor string Border color for inputs and dividers

Font Properties

Property Type Description
default string Default font family for body text
heading string Font family for headings and titles

Available font options: "Inter", "Poppins", "Montserrat", "Playfair Display", "Raleway", "Bebas Neue", "Roboto", "Open Sans", "Lato", "Doto", "Quicksand"

Size Properties

Property Type Description
heading number Font size for main headings (in pixels)
title number Font size for section titles (in pixels)
regular number Font size for regular text (in pixels)
default number Base font size (in pixels)

Other Properties

Property Type Description
headerHeight number Gap spacing from your website's header (in pixels). Used for sticky positioning to prevent overlap with your site's header

Complete Theme Example

Widget.mount({
    containerID: "vaylo-widget",
    locale: "en",
    currencyCode: "SEK",
    theme: {
        color: {
            themeColor: "#2563eb",
            textColor: "#0A2540",
            secondaryTextColor: "#64748b",
            backgroundColor: "#ffffff",
            foregroundColor: "#f8fafc",
            borderColor: "#e2e8f0",
        },
        font: {
            default: "Inter",
            heading: "Poppins",
        },
        size: {
            heading: 28,
            title: 20,
            regular: 16,
            default: 14,
        },
        headerHeight: 70,
    },
    redirects: {
        searchURL: "https://yourdomain.com/search"
    }
});

Usage Examples

Basic HTML Integration

Simple integration in a static HTML page:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Booking Site</title>

    <!-- Load Widget Script -->
    <script src="https://widget.bookingsystem.se/res_widget_script.iife.js"></script>

    <style>
        #vaylo-widget {
            width: 1200px;
            max-width: 100%;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    <div id="vaylo-widget"></div>

    <script>
        window.addEventListener("load", function () {
            Widget.mount({
                containerID: "vaylo-widget",
                type: "default",
                locale: "en",
                currencyCode: "SEK",
                itemPerPage: 10,
                dateFormat: "dd MMM, yyyy",
                redirects: {
                    searchURL: "https://yourdomain.com/search",
                    bookingURL: "https://yourdomain.com/bookings",
                    successURL: "https://yourdomain.com/success"
                }
            });
        });
    </script>
</body>
</html>

React Integration

Using the widget in a React application:

import { useEffect } from 'react';

function BookingWidget() {
    useEffect(() => {
        const script = document.createElement('script');
        script.src = 'https://widget.bookingsystem.se/res_widget_script.iife.js';
        script.async = true;

        script.onload = () => {
            if (window.Widget) {
                window.Widget.mount({
                    containerID: "vaylo-widget",
                    type: "default",
                    locale: "en",
                    currencyCode: "SEK",
                    itemPerPage: 10,
                    dateFormat: "dd MMM, yyyy",
                    redirects: {
                        searchURL: "https://yourdomain.com/search",
                        bookingURL: "https://yourdomain.com/bookings",
                        successURL: "https://yourdomain.com/success"
                    }
                });
            }
        };

        document.head.appendChild(script);

        return () => {
            // Cleanup if needed
            document.head.removeChild(script);
        };
    }, []);

    return (
        <div
            id="vaylo-widget"
            style={{ width: '1200px', maxWidth: '100%', margin: '0 auto' }}
        />
    );
}

export default BookingWidget;

Vue.js Integration

Using the widget in a Vue.js application:

<template>
    <div id="vaylo-widget" style="width: 1200px; max-width: 100%; margin: 0 auto;"></div>
</template>

<script>
export default {
    name: 'BookingWidget',
    mounted() {
        const script = document.createElement('script');
        script.src = 'https://widget.bookingsystem.se/res_widget_script.iife.js';
        script.async = true;

        script.onload = () => {
            if (window.Widget) {
                window.Widget.mount({
                    containerID: "vaylo-widget",
                    type: "default",
                    locale: this.$i18n?.locale || "en",
                    currencyCode: "SEK",
                    itemPerPage: 10,
                    dateFormat: "dd MMM, yyyy",
                    redirects: {
                        searchURL: "https://yourdomain.com/search",
                        bookingURL: "https://yourdomain.com/bookings",
                        successURL: "https://yourdomain.com/success"
                    }
                });
            }
        };

        document.head.appendChild(script);
    },
    beforeUnmount() {
        // Cleanup if needed
    }
};
</script>

With Custom Theme

Applying custom colors and fonts:

<script>
window.addEventListener("load", function () {
    Widget.mount({
        containerID: "vaylo-widget",
        type: "default",
        locale: "en",
        currencyCode: "SEK",
        itemPerPage: 10,
        dateFormat: "dd MMM, yyyy",
        theme: {
            color: {
                themeColor: "#8b5cf6",
                textColor: "#1e1b4b",
                secondaryTextColor: "#64748b",
                backgroundColor: "#ffffff",
                foregroundColor: "#faf5ff",
                borderColor: "#e9d5ff"
            },
            font: {
                default: "Poppins",
                heading: "Playfair Display"
            },
            size: {
                heading: 32,
                title: 22,
                regular: 16,
                default: 14
            },
            headerHeight: 80
        },
        redirects: {
            searchURL: "https://yourdomain.com/search",
            bookingURL: "https://yourdomain.com/bookings",
            successURL: "https://yourdomain.com/success"
        }
    });
});
</script>

Advanced Usage

Custom Search Filters

Control which search filters are available to users:

Widget.mount({
    containerID: "vaylo-widget",
    locale: "en",
    currencyCode: "SEK",
    itemPerPage: 10,
    dateFormat: "dd MMM, yyyy",
    // Only enable specific search filters
    searchKeys: [
        "country",
        "category",
        "from_departure_date",
        "to_departure_date"
    ],
    redirects: {
        searchURL: "https://yourdomain.com/search",
        bookingURL: "https://yourdomain.com/bookings",
        successURL: "https://yourdomain.com/success"
    }
});
Important: Make sure search keys match exactly. Available options are: "tour_tag", "tags", "category", "country", "location", "destination", "passenger_types", "number_of_passengers", "from_departure_date", "to_departure_date", "pickup_locations", "departure_date"

Multiple Widgets on One Page

Contact us before using this. Mounting the widget more than once on the same page is not currently supported: only the first container is rendered. If you need two booking boxes on one page, use a single container and place it with CSS, or talk to your Vaylo contact.

Display different widget configurations on the same page:

<!-- First Widget: Summer Tours -->
<div id="summer-tours-widget"></div>

<!-- Second Widget: Winter Tours -->
<div id="winter-tours-widget"></div>

<script>
window.addEventListener("load", function () {
    // Summer tours widget
    Widget.mount({
        containerID: "summer-tours-widget",
        locale: "en",
        currencyCode: "SEK",
        itemPerPage: 6,
        dateFormat: "dd MMM, yyyy",
        redirects: {
            searchURL: "https://yourdomain.com/search?type=summer"
        }
    });

    // Winter tours widget
    Widget.mount({
        containerID: "winter-tours-widget",
        locale: "en",
        currencyCode: "SEK",
        itemPerPage: 6,
        dateFormat: "dd MMM, yyyy",
        redirects: {
            searchURL: "https://yourdomain.com/search?type=winter"
        }
    });
});
</script>

Date Range Configuration

Control the date range for available tours using the departureFrom and duration parameters. These settings determine which tour dates are displayed to users.

Applies to type: "trip_details". departureFrom and duration are read by the Trip Details Widget only. With type: "default" the date range comes from the visitor's search instead, so setting these two options there has no effect.

How It Works

The date range is calculated as follows:

  • Start Date: Specified by departureFrom (format: YYYY-MM-DD)
  • End Date: Automatically calculated as departureFrom + duration months
  • Default Behavior: If not specified, departureFrom is today and duration is 3 — so the widget shows the next three months. It never shows "all future dates" by default; widen duration if you sell departures further ahead.

Parameter Details

Parameter Type Description
departureFrom string Starting date for tour availability in YYYY-MM-DD format (e.g., "2025-04-01")
duration number Number of months from the start date to display tours (e.g., 12 for one year)

Example 1: Fixed Date Range (12 Months)

Display tours from April 1, 2025, through March 31, 2026:

Widget.mount({
    containerID: "vaylo-widget",
    type: "trip_details",
    tourTag: 4,
    locale: "en",
    currencyCode: "SEK",
    itemPerPage: 10,
    dateFormat: "dd MMM, yyyy",
    departureFrom: "2025-04-01",
    duration: 12,
    redirects: {
        searchURL: "https://yourdomain.com/search",
        bookingURL: "https://yourdomain.com/bookings",
        successURL: "https://yourdomain.com/success"
    }
});

Example 2: Dynamic Date Range

Set the start date dynamically to today's date:

const today = new Date().toISOString().split('T')[0]; // Format: YYYY-MM-DD

Widget.mount({
    containerID: "vaylo-widget",
    type: "trip_details",
    tourTag: 4,
    locale: "en",
    currencyCode: "SEK",
    departureFrom: today,
    duration: 12,
    redirects: {
        searchURL: "https://yourdomain.com/search",
        bookingURL: "https://yourdomain.com/bookings",
        successURL: "https://yourdomain.com/success"
    }
});

Best Practices

  • Plan Ahead: Set a date range that aligns with your tour planning and booking cycles
  • Consider Lead Time: Account for how far in advance customers typically book tours
  • Regular Updates: If using fixed dates, remember to update them periodically
  • Test Coverage: Ensure your date range covers all tours you want to display
Note: Tours outside the specified date range will not be displayed, even if they are available in your system. Ensure your date range covers all tours you want to offer. If a departure you expect is missing, start with Trip Details Troubleshooting.