🔧 Integration Guide

Complete step-by-step guide to integrate ServiceFlow Pro widgets into your website

Quick Start Integration

1

Get Widget ID

Request your custom widget configuration and receive your unique widget ID.

2

Choose Method

Select your preferred integration method: HTML, WordPress, React, or custom SDK.

3

Add Code

Copy and paste the provided code into your website or application.

4

Go Live

Test the widget and publish your enhanced website with interactive visualizations.

Integration Methods

Simple HTML Embed

The easiest way to add a widget to any website. Perfect for static sites, landing pages, and quick implementations.

Basic Implementation

<!-- ServiceFlow Pro Visualizer Widget --> <div id="serviceflow-widget"></div> <script src="https://widgets.serviceflow-pro.com/sdk/v1/widget.js"></script> <script> ServiceFlowWidget.create('serviceflow-widget', { widgetId: 'your-widget-id', features: { enableADU: true, enablePool: true, enablePricing: true } }); </script>

Advanced Configuration

<!-- Advanced Widget Configuration --> <div id="advanced-widget" style="width: 100%; height: 600px;"></div> <script> ServiceFlowWidget.create('advanced-widget', { widgetId: 'your-widget-id', // Feature Configuration features: { enableADU: true, enablePool: true, enablePricing: true, enableSaveLoad: true, enableAR: false, showBranding: true }, // Custom Branding branding: { primaryColor: '#52c41a', secondaryColor: '#1890ff', fontFamily: 'Inter, sans-serif', logo: 'https://yoursite.com/logo.png' }, // Layout Settings layout: { width: '100%', height: '600px', responsive: true, theme: 'light' }, // Event Handlers onQuoteRequest: function(quoteData) { console.log('Quote requested:', quoteData); // Send lead to your CRM sendLeadToCRM(quoteData); }, onDesignSaved: function(design) { console.log('Design saved:', design); // Track analytics event gtag('event', 'design_saved', { 'design_type': design.type, 'design_id': design.id }); }, onError: function(error) { console.error('Widget error:', error); // Handle errors gracefully } }); function sendLeadToCRM(quoteData) { // Example: Send to your CRM or lead management system fetch('/api/leads', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ source: 'visualizer_widget', customer: quoteData.customer, design: quoteData.design, pricing: quoteData.pricing }) }); } </script>

Multiple Widgets on One Page

<!-- ADU-only Widget --> <div id="adu-widget"></div> <!-- Pool-only Widget --> <div id="pool-widget"></div> <script> // ADU Visualizer ServiceFlowWidget.create('adu-widget', { widgetId: 'your-adu-widget-id', features: { enableADU: true, enablePool: false, enablePricing: true }, branding: { primaryColor: '#52c41a' } }); // Pool Visualizer ServiceFlowWidget.create('pool-widget', { widgetId: 'your-pool-widget-id', features: { enableADU: false, enablePool: true, enablePricing: true }, branding: { primaryColor: '#1890ff' } }); </script>

WordPress Integration

Easy integration with WordPress using our official plugin or manual code insertion.

Plugin Installation

Recommended Method: Use our official WordPress plugin for the easiest setup and management.
  1. Download the ServiceFlow Pro Visualizer plugin
  2. Upload to your WordPress site via Plugins → Add New → Upload Plugin
  3. Activate the plugin
  4. Go to Settings → ServiceFlow Widgets
  5. Enter your widget ID and configure settings

Shortcode Usage

<!-- Basic Shortcode --> [serviceflow-visualizer id="your-widget-id"] <!-- With Custom Features --> [serviceflow-visualizer id="your-widget-id" features="adu,pool" theme="light"] <!-- With Custom Dimensions --> [serviceflow-visualizer id="your-widget-id" width="100%" height="700px"] <!-- ADU Only --> [serviceflow-visualizer id="your-widget-id" features="adu" title="Design Your ADU"] <!-- Pool Only --> [serviceflow-visualizer id="your-widget-id" features="pool" title="Pool Designer"]

Gutenberg Block

Use the ServiceFlow Visualizer block in the WordPress block editor:

  1. Add a new block
  2. Search for "ServiceFlow Visualizer"
  3. Configure widget settings in the block sidebar
  4. Preview and publish

Theme Integration

<?php // Add to your theme's functions.php or template files // Display widget in template echo do_shortcode('[serviceflow-visualizer id="your-widget-id"]'); // Or use the function directly if (function_exists('serviceflow_render_widget')) { serviceflow_render_widget('your-widget-id', [ 'features' => ['adu', 'pool'], 'theme' => 'light', 'width' => '100%', 'height' => '600px' ]); } // Custom widget with PHP configuration function display_custom_visualizer() { $widget_config = [ 'widgetId' => 'your-widget-id', 'features' => [ 'enableADU' => true, 'enablePool' => true, 'enablePricing' => true ], 'branding' => [ 'primaryColor' => get_theme_mod('primary_color', '#52c41a'), 'logo' => get_theme_mod('custom_logo') ] ]; echo '<div id="custom-visualizer"></div>'; echo '<script>'; echo 'ServiceFlowWidget.create("custom-visualizer", ' . json_encode($widget_config) . ');'; echo '</script>'; } ?>

WooCommerce Integration

<?php // Add visualizer to product pages add_action('woocommerce_single_product_summary', 'add_visualizer_to_product', 25); function add_visualizer_to_product() { global $product; // Only show on specific product categories if (has_term(['adu-services', 'pool-installation'], 'product_cat', $product->get_id())) { echo '<div class="product-visualizer">'; echo '<h3>Visualize This Service</h3>'; echo do_shortcode('[serviceflow-visualizer id="your-widget-id"]'); echo '</div>'; } } ?>

React Component Integration

Seamless integration with React applications using our React component library.

Installation

npm install @serviceflow-pro/react-widgets # or yarn add @serviceflow-pro/react-widgets

Basic Usage

import React from 'react'; import { ServiceFlowVisualizerWidget } from '@serviceflow-pro/react-widgets'; function PropertyVisualizerPage() { return ( <div className="visualizer-container"> <h2>Design Your Dream Space</h2> <ServiceFlowVisualizerWidget widgetId="your-widget-id" features={{ enableADU: true, enablePool: true, enablePricing: true }} style={{ width: '100%', height: '600px', borderRadius: '8px' }} /> </div> ); } export default PropertyVisualizerPage;

Advanced React Integration

import React, { useState, useCallback } from 'react'; import { ServiceFlowVisualizerWidget } from '@serviceflow-pro/react-widgets'; function AdvancedVisualizerPage() { const [quoteData, setQuoteData] = useState(null); const [isLoading, setIsLoading] = useState(false); const handleQuoteRequest = useCallback(async (quote) => { setIsLoading(true); try { // Send to your backend const response = await fetch('/api/leads', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(quote) }); const result = await response.json(); setQuoteData(result); // Track analytics if (window.gtag) { window.gtag('event', 'quote_requested', { design_type: quote.design.type, estimated_cost: quote.pricing.totalCost }); } } catch (error) { console.error('Failed to submit quote:', error); } finally { setIsLoading(false); } }, []); const handleDesignSaved = useCallback((design) => { console.log('Design saved:', design); // Save to local storage localStorage.setItem('saved_design', JSON.stringify(design)); // Show success message alert('Design saved successfully!'); }, []); return ( <div className="advanced-visualizer"> <header> <h1>Property Visualization Tool</h1> <p>Upload a photo and see how an ADU or pool would look in your space</p> </header> <ServiceFlowVisualizerWidget widgetId="your-widget-id" // Feature Configuration features={{ enableADU: true, enablePool: true, enablePricing: true, enableSaveLoad: true, enableAR: false }} // Custom Branding branding={{ primaryColor: '#52c41a', secondaryColor: '#1890ff', fontFamily: 'Inter, sans-serif', logo: '/logo.png' }} // Layout layout={{ width: '100%', height: '700px', responsive: true, theme: 'light' }} // Event Handlers onQuoteRequest={handleQuoteRequest} onDesignSaved={handleDesignSaved} onError={(error) => console.error('Widget error:', error)} // Loading State loading={isLoading} // Custom Styling className="custom-visualizer-widget" style={{ borderRadius: '12px', boxShadow: '0 4px 20px rgba(0, 0, 0, 0.1)' }} /> {quoteData && ( <div className="quote-summary"> <h3>Quote Generated</h3> <p>Total Cost: ${quoteData.totalCost.toLocaleString()}</p> <p>Quote ID: {quoteData.quoteId}</p> </div> )} </div> ); } export default AdvancedVisualizerPage;

Next.js Integration

import dynamic from 'next/dynamic'; import { useState } from 'react'; // Dynamically import to avoid SSR issues const ServiceFlowVisualizerWidget = dynamic( () => import('@serviceflow-pro/react-widgets').then(mod => mod.ServiceFlowVisualizerWidget), { ssr: false, loading: () => <div>Loading visualizer...</div> } ); export default function VisualizerPage() { const [mounted, setMounted] = useState(false); useEffect(() => { setMounted(true); }, []); if (!mounted) { return <div>Loading...</div>; } return ( <div> <h1>Property Visualizer</h1> <ServiceFlowVisualizerWidget widgetId={process.env.NEXT_PUBLIC_WIDGET_ID} features={{ enableADU: true, enablePool: true, enablePricing: true }} /> </div> ); }

React Hook for Widget Management

import { useState, useEffect, useCallback } from 'react'; export function useServiceFlowWidget(widgetId, config = {}) { const [widget, setWidget] = useState(null); const [isLoaded, setIsLoaded] = useState(false); const [error, setError] = useState(null); useEffect(() => { const loadWidget = async () => { try { // Ensure SDK is loaded if (!window.ServiceFlowWidget) { await loadSDK(); } const widgetInstance = window.ServiceFlowWidget.create(widgetId, config); setWidget(widgetInstance); setIsLoaded(true); } catch (err) { setError(err); } }; loadWidget(); }, [widgetId, config]); const updateConfig = useCallback((newConfig) => { if (widget) { widget.updateConfig(newConfig); } }, [widget]); const destroy = useCallback(() => { if (widget) { widget.destroy(); setWidget(null); setIsLoaded(false); } }, [widget]); return { widget, isLoaded, error, updateConfig, destroy }; } function loadSDK() { return new Promise((resolve, reject) => { if (window.ServiceFlowWidget) { resolve(); return; } const script = document.createElement('script'); script.src = 'https://widgets.serviceflow-pro.com/sdk/v1/widget.js'; script.onload = resolve; script.onerror = reject; document.head.appendChild(script); }); }

JavaScript SDK

Full control with our JavaScript SDK for custom implementations and advanced integrations.

Installation

npm install @serviceflow-pro/widget-sdk # or include via CDN <script src="https://widgets.serviceflow-pro.com/sdk/v1/widget.js"></script>

SDK Initialization

import { ServiceFlowWidget } from '@serviceflow-pro/widget-sdk'; // Initialize the SDK const sdk = new ServiceFlowWidget({ apiUrl: 'https://widgets.serviceflow-pro.com', debug: process.env.NODE_ENV === 'development' }); // Create widget instance const widget = sdk.createWidget('widget-container', { widgetId: 'your-widget-id', // Configuration config: { features: { enableADU: true, enablePool: true, enablePricing: true, enableSaveLoad: true }, branding: { primaryColor: '#52c41a', secondaryColor: '#1890ff', fontFamily: 'Inter, sans-serif' }, layout: { width: '100%', height: '600px', responsive: true, theme: 'light' } } }); // Event listeners widget.on('ready', () => { console.log('Widget is ready'); }); widget.on('quoteRequested', (data) => { console.log('Quote requested:', data); handleQuoteRequest(data); }); widget.on('designSaved', (design) => { console.log('Design saved:', design); saveDesignToDatabase(design); }); widget.on('error', (error) => { console.error('Widget error:', error); showErrorMessage(error.message); }); // Initialize the widget widget.render();

Advanced SDK Usage

class VisualizerManager { constructor() { this.widgets = new Map(); this.analytics = new AnalyticsTracker(); } async createWidget(containerId, config) { try { const widget = new ServiceFlowWidget({ containerId: containerId, widgetId: config.widgetId, config: config }); // Set up event tracking this.setupEventTracking(widget, config.widgetId); // Store widget reference this.widgets.set(containerId, widget); // Render widget await widget.render(); return widget; } catch (error) { console.error('Failed to create widget:', error); throw error; } } setupEventTracking(widget, widgetId) { widget.on('designStarted', (data) => { this.analytics.track('design_started', { widget_id: widgetId, design_type: data.type }); }); widget.on('designCompleted', (data) => { this.analytics.track('design_completed', { widget_id: widgetId, design_type: data.type, time_spent: data.timeSpent }); }); widget.on('quoteRequested', (data) => { this.analytics.track('quote_requested', { widget_id: widgetId, design_type: data.design.type, estimated_cost: data.pricing.totalCost }); // Send lead to CRM this.sendLeadToCRM(data); }); } async sendLeadToCRM(quoteData) { try { const response = await fetch('/api/crm/leads', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.getAuthToken()}` }, body: JSON.stringify({ source: 'visualizer_widget', customer: quoteData.customer, design: quoteData.design, pricing: quoteData.pricing, timestamp: new Date().toISOString() }) }); if (!response.ok) { throw new Error('Failed to send lead to CRM'); } const result = await response.json(); console.log('Lead sent to CRM:', result.leadId); return result; } catch (error) { console.error('CRM integration error:', error); // Fallback: save to local storage this.saveLeadLocally(quoteData); } } updateWidgetConfig(containerId, newConfig) { const widget = this.widgets.get(containerId); if (widget) { widget.updateConfig(newConfig); } } destroyWidget(containerId) { const widget = this.widgets.get(containerId); if (widget) { widget.destroy(); this.widgets.delete(containerId); } } destroyAllWidgets() { this.widgets.forEach((widget, containerId) => { widget.destroy(); }); this.widgets.clear(); } } // Usage const manager = new VisualizerManager(); // Create multiple widgets manager.createWidget('adu-widget', { widgetId: 'adu-widget-id', features: { enableADU: true, enablePool: false } }); manager.createWidget('pool-widget', { widgetId: 'pool-widget-id', features: { enableADU: false, enablePool: true } });

Custom Styling and Theming

// Custom theme configuration const customTheme = { colors: { primary: '#52c41a', secondary: '#1890ff', accent: '#722ed1', background: '#ffffff', surface: '#f8f9fa', text: '#333333', textSecondary: '#666666' }, fonts: { primary: 'Inter, sans-serif', secondary: 'Roboto, sans-serif' }, spacing: { small: '8px', medium: '16px', large: '24px', xlarge: '32px' }, borderRadius: { small: '4px', medium: '8px', large: '12px' }, shadows: { small: '0 2px 4px rgba(0, 0, 0, 0.1)', medium: '0 4px 8px rgba(0, 0, 0, 0.1)', large: '0 8px 16px rgba(0, 0, 0, 0.1)' } }; // Apply custom theme const widget = new ServiceFlowWidget({ containerId: 'themed-widget', widgetId: 'your-widget-id', config: { theme: customTheme, // Custom CSS overrides customCSS: ` .widget-header { background: linear-gradient(135deg, #52c41a 0%, #1890ff 100%); color: white; padding: 1rem; border-radius: 8px 8px 0 0; } .design-option { transition: all 0.3s ease; border: 2px solid transparent; } .design-option:hover { border-color: #52c41a; transform: translateY(-2px); box-shadow: 0 4px 12px rgba(82, 196, 26, 0.2); } .pricing-display { background: linear-gradient(135deg, #f6ffed 0%, #e6f7ff 100%); border: 1px solid #b7eb8f; border-radius: 8px; padding: 1rem; } ` } });

Custom Integrations

Advanced integration patterns for specific use cases and custom requirements.

Headless Widget API

// Use widget functionality without UI class HeadlessVisualizer { constructor(widgetId, apiKey) { this.widgetId = widgetId; this.apiKey = apiKey; this.baseUrl = 'https://widgets.serviceflow-pro.com/api'; } async getWidgetConfig() { const response = await fetch(`${this.baseUrl}/widgets/${this.widgetId}`, { headers: { 'Authorization': `Bearer ${this.apiKey}` } }); return response.json(); } async createSession(imageData) { const formData = new FormData(); formData.append('image', imageData); formData.append('widgetId', this.widgetId); const response = await fetch(`${this.baseUrl}/sessions`, { method: 'POST', headers: { 'Authorization': `Bearer ${this.apiKey}` }, body: formData }); return response.json(); } async getDesignOptions(sessionId) { const response = await fetch(`${this.baseUrl}/sessions/${sessionId}/designs`, { headers: { 'Authorization': `Bearer ${this.apiKey}` } }); return response.json(); } async calculatePricing(designId, location) { const response = await fetch(`${this.baseUrl}/pricing/calculate`, { method: 'POST', headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ designId: designId, location: location }) }); return response.json(); } } // Usage with custom UI const headlessWidget = new HeadlessVisualizer('your-widget-id', 'your-api-key'); // Build your own UI async function buildCustomVisualizerUI() { const config = await headlessWidget.getWidgetConfig(); // Create custom interface const container = document.getElementById('custom-visualizer'); container.innerHTML = ` <div class="custom-upload-area"> <input type="file" id="image-upload" accept="image/*"> <label for="image-upload">Upload Property Photo</label> </div> <div id="design-options" style="display: none;"></div> <div id="pricing-display" style="display: none;"></div> `; // Handle image upload document.getElementById('image-upload').addEventListener('change', async (e) => { const file = e.target.files[0]; if (file) { const session = await headlessWidget.createSession(file); displayDesignOptions(session.id); } }); }

Mobile App Integration

// React Native WebView integration import { WebView } from 'react-native-webview'; const VisualizerWebView = ({ widgetId, onQuoteRequest }) => { const injectedJavaScript = ` window.ReactNativeWebView.postMessage(JSON.stringify({ type: 'ready' })); // Override quote request handler window.addEventListener('message', function(event) { if (event.data.type === 'quoteRequested') { window.ReactNativeWebView.postMessage(JSON.stringify({ type: 'quoteRequested', data: event.data.quote })); } }); true; // Required for iOS `; const handleMessage = (event) => { const message = JSON.parse(event.nativeEvent.data); switch (message.type) { case 'ready': console.log('Widget is ready'); break; case 'quoteRequested': onQuoteRequest(message.data); break; } }; return ( <WebView source={{ uri: `https://widgets.serviceflow-pro.com/embed/${widgetId}?mobile=true` }} injectedJavaScript={injectedJavaScript} onMessage={handleMessage} style={{ flex: 1 }} /> ); };

Server-Side Rendering (SSR)

// SSR-compatible widget loading function loadWidgetSSR() { // Check if we're in browser environment if (typeof window === 'undefined') { return null; // Return placeholder for SSR } // Dynamically load widget only on client side return import('@serviceflow-pro/widget-sdk').then(({ ServiceFlowWidget }) => { return new ServiceFlowWidget({ containerId: 'ssr-widget', widgetId: 'your-widget-id', config: { // Widget configuration } }); }); } // Next.js example import dynamic from 'next/dynamic'; const DynamicWidget = dynamic( () => import('../components/VisualizerWidget'), { ssr: false, loading: () => <div>Loading visualizer...</div> } ); export default function Page() { return ( <div> <h1>Property Visualizer</h1> <DynamicWidget widgetId="your-widget-id" /> </div> ); }

A/B Testing Integration

// A/B test different widget configurations class WidgetABTester { constructor() { this.experiments = new Map(); } defineExperiment(experimentId, variants) { this.experiments.set(experimentId, { variants: variants, assignment: this.getVariantAssignment(experimentId, variants.length) }); } getVariantAssignment(experimentId, variantCount) { // Use consistent hashing based on user ID or session const userId = this.getUserId(); const hash = this.simpleHash(experimentId + userId); return hash % variantCount; } createWidget(containerId, baseConfig, experimentId) { const experiment = this.experiments.get(experimentId); if (!experiment) { return this.createStandardWidget(containerId, baseConfig); } const variant = experiment.variants[experiment.assignment]; const config = { ...baseConfig, ...variant.config }; // Track experiment exposure this.trackExperiment(experimentId, variant.name); return new ServiceFlowWidget({ containerId: containerId, widgetId: config.widgetId, config: config }); } trackExperiment(experimentId, variantName) { // Send to analytics if (window.gtag) { window.gtag('event', 'experiment_exposure', { experiment_id: experimentId, variant_name: variantName }); } } } // Usage const abTester = new WidgetABTester(); abTester.defineExperiment('widget-layout-test', [ { name: 'control', config: { layout: { theme: 'light' } } }, { name: 'dark-theme', config: { layout: { theme: 'dark' } } } ]); const widget = abTester.createWidget('test-widget', { widgetId: 'your-widget-id', features: { enableADU: true, enablePool: true } }, 'widget-layout-test');

Testing & Debugging

Debug Mode: Enable debug mode during development to see detailed logs and error messages.

Debug Configuration

// Enable debug mode ServiceFlowWidget.create('debug-widget', { widgetId: 'your-widget-id', debug: true, // Enable detailed logging // Development settings development: { showPerformanceMetrics: true, enableErrorBoundary: true, logLevel: 'verbose' }, // Test mode with mock data testMode: { enabled: process.env.NODE_ENV === 'development', mockData: { designs: ['studio-adu', 'pool-rectangular'], pricing: { basePrice: 50000, multiplier: 1.2 } } } });

Error Handling

// Comprehensive error handling const widget = ServiceFlowWidget.create('error-handled-widget', { widgetId: 'your-widget-id', onError: (error) => { console.error('Widget Error:', error); // Log to error tracking service if (window.Sentry) { window.Sentry.captureException(error); } // Show user-friendly message showErrorNotification('Something went wrong. Please try again.'); // Attempt recovery if (error.code === 'NETWORK_ERROR') { setTimeout(() => widget.retry(), 3000); } }, onWarning: (warning) => { console.warn('Widget Warning:', warning); // Handle non-critical issues if (warning.code === 'SLOW_NETWORK') { showWarningNotification('Slow connection detected. Loading may take longer.'); } } }); function showErrorNotification(message) { // Your notification system const notification = document.createElement('div'); notification.className = 'error-notification'; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => { document.body.removeChild(notification); }, 5000); }

Performance Optimization

Lazy Loading

// Lazy load widget when it comes into view const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { loadWidget(entry.target.id); observer.unobserve(entry.target); } }); }); function setupLazyWidget(containerId) { const container = document.getElementById(containerId); container.innerHTML = '<div class="widget-placeholder">Loading visualizer...</div>'; observer.observe(container); } function loadWidget(containerId) { ServiceFlowWidget.create(containerId, { widgetId: 'your-widget-id', // Configuration }); }

Preloading

// Preload widget resources ServiceFlowWidget.preload({ widgetId: 'your-widget-id', resources: ['models', 'textures', 'catalog'] }).then(() => { console.log('Widget resources preloaded'); // Create widget instantly ServiceFlowWidget.create('preloaded-widget', { widgetId: 'your-widget-id', preloaded: true }); });

Ready to Get Started?

Choose your integration method and start engaging customers with interactive property visualizations.

Get Widget Access Schedule Demo