Integration Examples

Complete widget implementations for different use cases.

Basic implementation

The simplest example: an inline widget on your booking page.

HTML
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Book Appointment | Mi Salon</title>
    <style>
        body {
            font-family: system-ui, sans-serif;
            max-width: 600px;
            margin: 40px auto;
            padding: 0 20px;
        }
        h1 {
            text-align: center;
            margin-bottom: 30px;
        }
    </style>
</head>
<body>

    <h1>Book your appointment</h1>

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

    <script src="https://app.salonbookit.com/static/widget/v1/salonbookit-widget.min.js"></script>
    <script>
        SalonBookIt.init({
            apiKey: 'YOUR_API_KEY',
            container: '#salonbookit-widget',
            theme: 'light',
            locale: 'es'
        });
    </script>

</body>
</html>

Button that opens modal

A floating button that opens the widget in a popup. Ideal for landing pages.

HTML

Preselected service

Useful for individual service pages where you want the widget to have a service already selected.

HTML
<!-- Page: /servicios/corte-clasico -->

<div class="service-page">
    <h1>Classic Cut</h1>
    <p>Our most popular haircut for gentlemen.</p>
    <p><strong>Price: 25€</strong> | <strong>Duration: 30 min</strong></p>

    <h2>Book this service</h2>
    <div id="widget"></div>
</div>

<script src="https://app.salonbookit.com/static/widget/v1/salonbookit-widget.min.js"></script>
<script>
    // Get the service ID from your backend or the URL
    const serviceId = 15; // ID del "Classic Cut"

    SalonBookIt.init({
        apiKey: 'YOUR_API_KEY',
        container: '#widget',
        serviceId: serviceId, // Preselect this service
        allowMultipleServices: false // Only allow this service
    });
</script>

Google Analytics Integration

Track each step of the booking funnel.

JavaScript
SalonBookIt.init({
    apiKey: 'YOUR_API_KEY',
    container: '#widget',

    onReady: (business) => {
        gtag('event', 'widget_loaded', {
            'business_name': business.nombre
        });
    },

    onStepChange: (step, data) => {
        gtag('event', 'booking_step', {
            'step_name': step,
            'services_count': data.services?.length || 0
        });
    },

    onServiceSelect: (service, selected, allSelected) => {
        if (selected) {
            gtag('event', 'add_to_cart', {
                'items': [{
                    'id': service.id,
                    'name': service.nombre,
                    'price': service.precio
                }]
            });
        }
    },

    onBookingComplete: (booking) => {
        gtag('event', 'purchase', {
            'transaction_id': booking.id,
            'value': booking.total,
            'currency': 'EUR',
            'items': booking.servicios.map(s => ({
                'id': s.id,
                'name': s.nombre,
                'price': s.precio
            }))
        });

        // Facebook Pixel
        fbq('track', 'Purchase', {
            value: booking.total,
            currency: 'EUR'
        });
    },

    onPaymentComplete: (payment) => {
        gtag('event', 'payment_complete', {
            'payment_method': 'stripe',
            'amount': payment.amount
        });
    },

    onError: (error) => {
        gtag('event', 'exception', {
            'description': error.message,
            'fatal': false
        });
    }
});

Custom styles

Fully customize the widget appearance to match your brand.

HTML + CSS
<style>
/* Custom theme */
#mi-widget .sbk-widget {
    /* Brand colors */
    --sbk-primary: #ff6b6b;
    --sbk-primary-hover: #ee5a5a;
    --sbk-background: #fafafa;
    --sbk-surface: #ffffff;
    --sbk-text: #2d3436;
    --sbk-text-secondary: #636e72;
    --sbk-border: #dfe6e9;

    /* Rounder borders */
    --sbk-radius-sm: 8px;
    --sbk-radius-md: 12px;
    --sbk-radius-lg: 20px;

    /* Custom font */
    --sbk-font-family: 'Poppins', sans-serif;

    /* More pronounced shadows */
    --sbk-shadow-md: 0 10px 40px rgba(0,0,0,0.15);
}

/* Primary button style */
#mi-widget .sbk-btn-primary {
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

/* Service cards */
#mi-widget .sbk-service-card {
    border: 2px solid transparent;
    transition: all 0.3s ease;
}

#mi-widget .sbk-service-card:hover {
    border-color: var(--sbk-primary);
    transform: translateY(-2px);
}

#mi-widget .sbk-service-card.selected {
    border-color: var(--sbk-primary);
    background: rgba(255, 107, 107, 0.05);
}
</style>

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

<script src="https://app.salonbookit.com/static/widget/v1/salonbookit-widget.min.js"></script>
<script>
    SalonBookIt.init({
        apiKey: 'YOUR_API_KEY',
        container: '#mi-widget',
        primaryColor: '#ff6b6b',
        borderRadius: '20px',
        fontFamily: 'Poppins, sans-serif'
    });
</script>

Complete React component

React (TypeScript)
import React, { useEffect, useRef, useState } from 'react';

interface BookingWidgetProps {
    apiKey: string;
    serviceId?: number;
    staffId?: number;
    onBookingComplete?: (booking: any) => void;
    onError?: (error: any) => void;
}

declare global {
    interface Window {
        SalonBookIt: any;
    }
}

const BookingWidget: React.FC<BookingWidgetProps> = ({
    apiKey,
    serviceId,
    staffId,
    onBookingComplete,
    onError
}) => {
    const containerRef = useRef<HTMLDivElement>(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState<string | null>(null);

    useEffect(() => {
        const loadScript = () => {
            return new Promise<void>((resolve, reject) => {
                if (window.SalonBookIt) {
                    resolve();
                    return;
                }

                const script = document.createElement('script');
                script.src = 'https://app.salonbookit.com/static/widget/v1/salonbookit-widget.min.js';
                script.async = true;
                script.onload = () => resolve();
                script.onerror = () => reject(new Error('Error loading widget'));
                document.body.appendChild(script);
            });
        };

        const initWidget = async () => {
            try {
                await loadScript();

                if (!containerRef.current) return;

                window.SalonBookIt.init({
                    apiKey,
                    container: containerRef.current,
                    serviceId,
                    staffId,
                    locale: 'es',
                    onReady: () => setLoading(false),
                    onBookingComplete: (booking: any) => {
                        onBookingComplete?.(booking);
                    },
                    onError: (err: any) => {
                        setError(err.message);
                        onError?.(err);
                    }
                });
            } catch (err) {
                setError('Error loading the widget');
            }
        };

        initWidget();

        return () => {
            // Cleanup if needed
        };
    }, [apiKey, serviceId, staffId]);

    if (error) {
        return (
            <div className="widget-error">
                <p>Error: {error}</p>
                <button onClick={() => window.location.reload()}>
                    Retry
                </button>
            </div>
        );
    }

    return (
        <div className="widget-wrapper">
            {loading && (
                <div className="widget-loading">
                    <div className="spinner" />
                    <p>Loading...</p>
                </div>
            )}
            <div ref={containerRef} style={{ opacity: loading ? 0 : 1 }} />
        </div>
    );
};

export default BookingWidget;

// Usage:
// <BookingWidget
//     apiKey="hh_pub_live_..."
//     serviceId={15}
//     onBookingComplete={(booking) => navigate(`/confirmacion/${booking.id}`)}
// />

WordPress with Shortcode

Create a shortcode to easily insert the widget in posts and pages.

PHP (functions.php)
<?php
/**
 * Shortcode for SalonBookIt widget
 * Usage: [salonbookit_widget service="15" staff="3"]
 */
function salonbookit_widget_shortcode($atts) {
    $atts = shortcode_atts([
        'service' => '',
        'staff' => '',
        'mode' => 'inline',
        'button_text' => 'Book Appointment'
    ], $atts);

    // Generate unique ID for container
    $container_id = 'sbk-widget-' . uniqid();

    // Build JS options
    $options = [
        'apiKey' => get_option('salonbookit_api_key'),
        'container' => '#' . $container_id,
        'mode' => esc_attr($atts['mode']),
        'buttonText' => esc_attr($atts['button_text']),
        'locale' => substr(get_locale(), 0, 2)
    ];

    if (!empty($atts['service'])) {
        $options['serviceId'] = intval($atts['service']);
    }
    if (!empty($atts['staff'])) {
        $options['staffId'] = intval($atts['staff']);
    }

    $options_json = json_encode($options);

    ob_start();
    ?>
    <div id="<?php echo esc_attr($container_id); ?>"></div>
    <script>
    document.addEventListener('DOMContentLoaded', function() {
        if (window.SalonBookIt) {
            SalonBookIt.init(<?php echo $options_json; ?>);
        }
    });
    </script>
    <?php
    return ob_get_clean();
}
add_shortcode('salonbookit_widget', 'salonbookit_widget_shortcode');

// Enqueue script
function salonbookit_enqueue_scripts() {
    wp_enqueue_script(
        'salonbookit-widget',
        'https://app.salonbookit.com/static/widget/v1/salonbookit-widget.min.js',
        [],
        '1.0',
        true
    );
}
add_action('wp_enqueue_scripts', 'salonbookit_enqueue_scripts');

Usage in WordPress editor:

Shortcode
[salonbookit_widget]

[salonbookit_widget service="15"]

[salonbookit_widget mode="button" button_text="Request Appointment"]