> ## Documentation Index
> Fetch the complete documentation index at: https://docs.letshum.com/llms.txt
> Use this file to discover all available pages before exploring further.

# JavaScript Integration

> Step-by-step guide to integrate the Hum widget into your website using JavaScript and HTML

# JavaScript Widget Integration

The Hum widget can be embedded directly into your website with just a few lines of JavaScript and HTML. This integration method is ideal for web applications, landing pages, and any environment where you have control over the HTML and JavaScript.

<Info>
  This guide covers the JavaScript/HTML embed integration. For mobile app WebView integration, see [WebView Integration](/widget-integration/webview).
</Info>

<Note>
  To test with a Sandbox key, load the Sandbox widget bundle instead of the Production bundle shown below. See [Environments and API Keys](/environments#widget-environments).
</Note>

## Quick Start

Get up and running with the Hum widget in three simple steps:

<Steps>
  <Step title="Add the Widget Container">
    Add a container div where you want the widget to render:

    ```html theme={null}
    <div id="hum-widget"></div>
    ```

    <Tip>
      You can customize the container ID, but make sure it matches the element you pass to the widget constructor.
    </Tip>
  </Step>

  <Step title="Include the Widget Script">
    Load the Hum widget script in your HTML page:

    ```html theme={null}
    <script src="https://cdn.letshum.com/widget.js"></script>
    ```

    <Note>
      The script loads asynchronously and is hosted on a CDN for optimal performance.
    </Note>
  </Step>

  <Step title="Initialize the Widget">
    Initialize the widget with your API key:

    ```javascript theme={null}
    const hum = new HUM(
        'YOUR_API_KEY_HERE',
        document.getElementById('hum-widget')
    );
    hum.initialize();

    // Store the instance globally for access by other components
    window.humInstance = hum;
    ```

    <Check>
      Verify the widget loads by checking your browser's developer console for any error messages.
    </Check>
  </Step>
</Steps>

## Configuration

The Hum widget accepts an optional configuration object as the third parameter. For a complete list of available options, see the [Widget Overview](/widget-integration/overview#configuration-options).

### Basic Configuration Example

```javascript theme={null}
const hum = new HUM(
    'YOUR_API_KEY_HERE',
    document.getElementById('hum-widget'),
    {
        resultLayout: 'checkout',
        primaryColor: '#1274f9',
        campaignId: 'Q4-2024-campaign'
    }
);
hum.initialize();
```

### Advanced Configuration Example

Here's an example with all available options:

```javascript theme={null}
const hum = new HUM(
    'YOUR_API_KEY',
    document.getElementById('hum-widget'),
    {
        // Display & Layout
        resultLayout: 'checkout',
        primaryColor: '#1274f9',
        showSavePlanButton: true,          // Enable save/unsave plan button
        showAddressCompletionForm: false,  // Let the widget collect the address itself
        
        // Tracking & Analytics
        campaignId: 'Q4-2024-landing-page',
        
        // Filtering & Limiting
        limitProviders: ['130077', '130317'],
        primaryProviders: ['130077'],
        limitTechnologies: ['Fiber', 'Cable', 'Wireless'],
        
        // Customer Data Pre-population
        customerData: {
            firstName: 'Jane',
            lastName: 'Smith',
            email: 'jane.smith@example.com',
            phoneNumber: '555-987-6543'
        }
    }
);

hum.initialize();
```

## Integration Examples

### Example 1: Form-Based Integration

Integrate the widget with an HTML form for address input:

<Note>
  A `campaign_id` field inside widget address data is ignored. Set campaign attribution with the widget's `campaignId` configuration option.
</Note>

```html index.html theme={null}
<!DOCTYPE html>
<html>
<head>
    <title>Hum Widget Demo</title>
    <script src="https://cdn.letshum.com/widget.js"></script>
</head>
<body>
    <!-- Widget container -->
    <div id="hum-widget"></div>

    <!-- Address input form -->
    <form id="address-search">
        <input type="text" name="street1" placeholder="Street Address" required>
        <input type="text" name="street2" placeholder="Apartment/Suite (Optional)">
        <input type="text" name="city" placeholder="City" required>
        <input type="text" name="state" placeholder="State (2 letters)" maxlength="2" required>
        <input type="text" name="zip" placeholder="ZIP Code" pattern="[0-9]{5}(-[0-9]{4})?" required>
        <button type="submit">Check Availability</button>
    </form>

    <script>
        // Initialize Hum widget
        const hum = new HUM(
            'YOUR_API_KEY',
            document.getElementById('hum-widget'),
            {
                campaignId: 'your-campaign-id'
            }
        );
        
        hum.initialize()
            .then(() => {
                console.log('Hum widget initialized successfully');
            })
            .catch(error => {
                console.error('Failed to initialize Hum widget:', error);
                document.getElementById('hum-widget').innerHTML = 
                    '<p style="color: red;">Unable to load widget. Please try again later.</p>';
            });

        window.humInstance = hum;

        // Handle form submission
        document.getElementById('address-search').addEventListener('submit', async (event) => {
            event.preventDefault();
            
            const submitButton = event.target.querySelector('button[type="submit"]');
            const originalText = submitButton.textContent;
            submitButton.textContent = 'Loading...';
            submitButton.disabled = true;
            
            try {
                const formData = new FormData(event.target);
                const addressData = Object.fromEntries(formData.entries());
                
                // Remove empty optional fields
                if (!addressData.street2) delete addressData.street2;

                // Validate widget-required fields
                if (!addressData.street1 || !addressData.zip) {
                    throw new Error('Please fill in all required fields');
                }

                // Submit address to widget
                const sessionToken = await hum.internetServiceFromAddress(addressData);
                console.log('Widget loaded with session token:', sessionToken);
                
            } catch (error) {
                console.error('Error loading internet service options:', error);
                alert('Unable to load internet service options. Please check your address and try again.');
            } finally {
                submitButton.textContent = originalText;
                submitButton.disabled = false;
            }
        });
    </script>
</body>
</html>
```

<Check>
  Test your integration by entering a valid US address and verifying that service options load correctly.
</Check>

### Example 2: Direct Address Loading

Load address data directly when you already have the information:

```html direct-integration.html theme={null}
<!DOCTYPE html>
<html>
<head>
    <title>Hum Widget - Direct Integration</title>
    <script src="https://cdn.letshum.com/widget.js"></script>
</head>
<body>
    <div id="hum-widget"></div>

    <script>
        const hum = new HUM(
            'YOUR_API_KEY',
            document.getElementById('hum-widget'),
            {
                resultLayout: 'summary',
                campaignId: 'summer-2024'
            }
        );
        
        window.humInstance = hum;

        // Address data in the required format
        const addressData = {
            street1: "1001 Woodward Ave",
            street2: "Suite 500",
            city: "Detroit",
            state: "MI",
            zip: "48226",
            latitude: 42.3317,
            longitude: -83.0479
        };

        // Initialize and load address data
        async function loadWidget() {
            try {
                await hum.initialize();
                console.log('Hum widget initialized successfully');
                
                const sessionToken = await hum.internetServiceFromAddress(addressData);
                console.log('Internet service options loaded:', sessionToken);
                
            } catch (error) {
                console.error('Widget initialization failed:', error);
                
                const container = document.getElementById('hum-widget');
                container.innerHTML = `
                    <div style="padding: 20px; border: 1px solid #e74c3c; background: #fadbd8; color: #c0392b; border-radius: 4px;">
                        <h3>Unable to Load Internet Service Options</h3>
                        <p>We're having trouble loading service options for this address. Please try again later.</p>
                        <p><strong>Error:</strong> ${error.message}</p>
                    </div>
                `;
            }
        }

        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', loadWidget);
        } else {
            loadWidget();
        }
    </script>
</body>
</html>
```

<Tip>
  Use direct integration when you already have address data from a previous form, user profile, or application state.
</Tip>

### Example 3: React Integration

Integrate the Hum widget into a React application:

```javascript HumWidget.jsx theme={null}
import React, { useEffect, useRef, useState } from 'react';

function HumWidget({ apiKey, addressData, config }) {
  const containerRef = useRef(null);
  const humInstanceRef = useRef(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    // Initialize widget
    const initializeWidget = async () => {
      try {
        setIsLoading(true);
        setError(null);

        // Create HUM instance
        const hum = new window.HUM(
          apiKey,
          containerRef.current,
          config
        );

        await hum.initialize();
        humInstanceRef.current = hum;

        // Load address if provided
        if (addressData) {
          await hum.internetServiceFromAddress(addressData);
        }

        setIsLoading(false);
      } catch (err) {
        console.error('Failed to initialize Hum widget:', err);
        setError(err.message);
        setIsLoading(false);
      }
    };

    if (containerRef.current) {
      initializeWidget();
    }

    // Cleanup
    return () => {
      if (humInstanceRef.current) {
        // Cleanup logic if needed
        humInstanceRef.current = null;
      }
    };
  }, [apiKey, addressData, config]);

  // Method to update address
  const updateAddress = async (newAddress) => {
    if (humInstanceRef.current) {
      try {
        await humInstanceRef.current.internetServiceFromAddress(newAddress);
      } catch (err) {
        console.error('Failed to update address:', err);
        setError(err.message);
      }
    }
  };

  if (error) {
    return (
      <div style={{ padding: '20px', color: 'red', border: '1px solid red' }}>
        Error loading widget: {error}
      </div>
    );
  }

  return (
    <div>
      {isLoading && <div>Loading widget...</div>}
      <div ref={containerRef} id="hum-widget" />
    </div>
  );
}

// Usage
function App() {
  const addressData = {
    street1: "1001 Woodward Ave",
    city: "Detroit",
    state: "MI",
    zip: "48226"
  };

  const config = {
    resultLayout: 'checkout',
    primaryColor: '#1274f9',
    campaignId: 'react-app'
  };

  return (
    <div>
      <h1>Internet Service Finder</h1>
      <HumWidget
        apiKey="your-api-key-here"
        addressData={addressData}
        config={config}
      />
    </div>
  );
}

export default App;
```

### Example 4: Vue.js Integration

Integrate the widget into a Vue.js application:

```vue HumWidget.vue theme={null}
<template>
  <div>
    <div v-if="isLoading">Loading widget...</div>
    <div v-if="error" class="error">{{ error }}</div>
    <div ref="widgetContainer" id="hum-widget"></div>
  </div>
</template>

<script>
export default {
  name: 'HumWidget',
  props: {
    apiKey: {
      type: String,
      required: true
    },
    addressData: {
      type: Object,
      default: null
    },
    config: {
      type: Object,
      default: () => ({})
    }
  },
  data() {
    return {
      humInstance: null,
      isLoading: true,
      error: null
    };
  },
  async mounted() {
    await this.initializeWidget();
  },
  beforeUnmount() {
    // Cleanup if needed
    this.humInstance = null;
  },
  methods: {
    async initializeWidget() {
      try {
        this.isLoading = true;
        this.error = null;

        // Create HUM instance
        this.humInstance = new window.HUM(
          this.apiKey,
          this.$refs.widgetContainer,
          this.config
        );

        await this.humInstance.initialize();

        // Load address if provided
        if (this.addressData) {
          await this.humInstance.internetServiceFromAddress(this.addressData);
        }

        this.isLoading = false;
      } catch (err) {
        console.error('Failed to initialize Hum widget:', err);
        this.error = err.message;
        this.isLoading = false;
      }
    },
    async updateAddress(newAddress) {
      if (this.humInstance) {
        try {
          await this.humInstance.internetServiceFromAddress(newAddress);
        } catch (err) {
          console.error('Failed to update address:', err);
          this.error = err.message;
        }
      }
    }
  }
};
</script>

<style scoped>
.error {
  padding: 20px;
  color: red;
  border: 1px solid red;
  border-radius: 4px;
  margin-bottom: 20px;
}
</style>
```

## Address Data Format

The widget expects address data in a specific JSON format:

```json theme={null}
{
  "street1": "1001 Woodward Ave",
  "street2": "Suite 500",
  "city": "Detroit",
  "state": "MI",
  "zip": "48226",
  "latitude": 42.3317,
  "longitude": -83.0479
}
```

For complete field specifications, see the [Address Data Format](/widget-integration/overview#address-data-format) section in the overview.

## Address Validation

The widget validates addresses using these rules:

<AccordionGroup>
  <Accordion title="Required Field Validation">
    * **street1**: Primary address (street number and name)
    * **zip**: 5 or 9-digit ZIP code format (12345 or 12345-6789)
  </Accordion>

  <Accordion title="Optional Field Validation">
    * **street2**: Unit/apartment information (no specific format required)
    * **city**: City name (recommended for improved address match quality)
    * **state**: 2-letter US state code (recommended for improved address match quality)
    * **latitude/longitude**: Must be provided together and within US territorial bounds
  </Accordion>

  <Accordion title="Additional Validation Rules">
    * Address must be geocodable by our system
    * Address must be serviceable by at least one provider
    * Coordinates must be within the bounds of the US and its territories
  </Accordion>
</AccordionGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Widget Not Loading">
    **Check these common issues:**

    * Verify the container div exists on the page
    * Ensure the widget script loads before your initialization code
    * Check that your API key is valid and properly formatted
    * Look for JavaScript errors in the browser console

    **Expected behavior:** The widget should initialize within 2-3 seconds of calling `hum.initialize()`.
  </Accordion>

  <Accordion title="Address Validation Errors">
    **Common address issues:**

    * Ensure the required fields (`street1` and `zip`) are provided
    * Include `city` and `state` when available to improve address match quality
    * Verify the state is a valid 2-letter US state code
    * Check that the ZIP code is in the correct format (12345 or 12345-6789)
    * Make sure the address is a real, serviceable US address

    **Expected behavior:** Valid addresses should return service options within 5-10 seconds.
  </Accordion>

  <Accordion title="Performance Issues">
    **Optimization tips:**

    * Include latitude and longitude coordinates when available
    * Use the summary layout for faster loading when full e-commerce isn't needed
    * Ensure the widget script is loaded asynchronously
    * Check network connectivity and CDN availability

    **Expected behavior:** The widget should load service options in under 10 seconds for most addresses.
  </Accordion>

  <Accordion title="Integration Problems">
    **Common integration issues:**

    * Verify the widget instance is stored globally if accessed by other scripts
    * Check for conflicts with other JavaScript libraries
    * Ensure proper error handling for failed API calls
    * Verify the container element is visible and properly sized

    **Expected behavior:** The widget should integrate seamlessly without affecting other page functionality.
  </Accordion>
</AccordionGroup>

<Warning>
  If issues persist, check the browser console for detailed error messages and contact support with the specific error details.
</Warning>

## Best Practices

<AccordionGroup>
  <Accordion title="Error Handling">
    Always implement proper error handling for API calls:

    ```javascript theme={null}
    hum.initialize()
        .then(() => console.log('Success'))
        .catch(error => {
            console.error('Error:', error);
            // Show user-friendly error message
        });
    ```
  </Accordion>

  <Accordion title="Loading States">
    Use loading states to improve user experience:

    ```javascript theme={null}
    button.textContent = 'Loading...';
    button.disabled = true;

    try {
        await hum.internetServiceFromAddress(addressData);
    } finally {
        button.textContent = 'Check Availability';
        button.disabled = false;
    }
    ```
  </Accordion>

  <Accordion title="Performance Optimization">
    * Load the widget script asynchronously
    * Include latitude/longitude coordinates for faster results
    * Cache widget instances when using multiple widgets
    * Monitor console logs during development
  </Accordion>

  <Accordion title="User Experience">
    * Test with various address formats and edge cases
    * Provide clear error messages
    * Use appropriate layouts for your use case
    * Include campaign IDs for better tracking
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Widget Hooks" icon="webhook" href="/widget-integration/hooks">
    Learn how to listen for and handle events from the widget
  </Card>

  <Card title="Configuration Options" icon="sliders" href="/widget-integration/overview#configuration-options">
    Explore all available configuration parameters
  </Card>

  <Card title="WebView Integration" icon="mobile-screen" href="/widget-integration/webview">
    Integrate the widget into mobile applications
  </Card>

  <Card title="Get Support" icon="life-ring" href="mailto:support@letshum.com">
    Contact our team for integration assistance
  </Card>
</CardGroup>
