Integration Methods
HTML Embed
WordPress
React
JavaScript SDK
Custom
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.
Download the ServiceFlow Pro Visualizer plugin
Upload to your WordPress site via Plugins → Add New → Upload Plugin
Activate the plugin
Go to Settings → ServiceFlow Widgets
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:
Add a new block
Search for "ServiceFlow Visualizer"
Configure widget settings in the block sidebar
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
});
});