|
| 1 | +--- |
| 2 | +sidebar_position: 2 |
| 3 | +title: "Third Party App Integrations" |
| 4 | +sidebar_label: Third Party App Integrations |
| 5 | +description: If you are the developer of third party option application, we'd love to collaborate and make our app compatible with your solution. This guide defines some ways how this is possible. |
| 6 | +tags: [shopify, 3d-bits] |
| 7 | +--- |
| 8 | +import Admonition from '@theme/Admonition'; |
| 9 | + |
| 10 | +## Overview |
| 11 | + |
| 12 | +We'd love to collaborate with product options app developers to create seamless integrations between our apps. This guide explains how implementing a standardized event-based communication layer can benefit our mutual customers who want to build advanced 3D product configurators. |
| 13 | + |
| 14 | +By working together, we can provide customers with stable, reliable experiences where 3D visualizations respond smoothly to product option changes—without either of us worrying about breaking integrations during routine updates. This strengthens the entire ecosystem of advanced product configurators on Shopify and creates better experiences for the customers we both serve. |
| 15 | + |
| 16 | +## The Current Integration Challenge |
| 17 | + |
| 18 | +Currently, 3D Bits integrates with third-party product options apps by monitoring HTML form inputs. While this works in many cases, it relies on stable `name` attributes and can be affected by DOM changes from framework updates. A native event-based approach provides a more reliable integration layer that's independent of your internal implementation. |
| 19 | + |
| 20 | +## The Solution: Native Event-Based Integration |
| 21 | + |
| 22 | +We propose establishing an official communication layer using **standard Browser Custom Events**. This approach provides a stable, versioned API contract between your app and 3D Bits, ensuring integrations remain functional regardless of your internal implementation or DOM structure changes. |
| 23 | + |
| 24 | +### How It Works |
| 25 | + |
| 26 | +When a mutual customer builds a 3D configurator using both your product options app and 3D Bits, they would: |
| 27 | + |
| 28 | +1. **Select Your App**: In the 3D Bits settings, they choose your app's name from the ["Input Collection Mode"](../tutorials/getting-started/common-settings#input-collection-mode) dropdown |
| 29 | +2. **Automatic Subscription**: 3D Bits adds a listener to the global `window` object |
| 30 | +3. **Real-Time Synchronization**: As users interact with your product options, your app dispatches a standard event. 3D Bits captures this payload and updates the 3D model immediately |
| 31 | + |
| 32 | +This creates a plug-and-play experience that requires no technical configuration from the end user. |
| 33 | + |
| 34 | +## Technical Implementation |
| 35 | + |
| 36 | +### Architecture: Native CustomEvent API |
| 37 | + |
| 38 | +Instead of creating a proprietary global object (which is prone to conflicts and security issues), we use the standard [Web API CustomEvent interface](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent). |
| 39 | + |
| 40 | +<Admonition type="info" title="Why Native CustomEvents?"> |
| 41 | +Using standard browser events (`window.dispatchEvent`) is superior to a custom "Event Bus" object because: |
| 42 | +- **Decoupling**: Your app does not need to check if 3D Bits is present or loaded. You simply fire the message into the `window`. |
| 43 | +- **Security**: There is no shared global state or mutable listener array that other malicious scripts can easily hijack or overwrite. |
| 44 | +- **Shadow DOM Support**: Native events can be configured to bubble through Shadow DOM boundaries, supporting encapsulated apps. |
| 45 | +</Admonition> |
| 46 | + |
| 47 | +### Implementation Guide |
| 48 | + |
| 49 | +Your app simply needs to dispatch a `CustomEvent` specifically named `3dbits.productOptions.changed` whenever a user modifies an option. |
| 50 | + |
| 51 | +#### The Dispatch Code |
| 52 | + |
| 53 | +Add this logic to your app's change handler (e.g., inside your React `useEffect`, Vue watcher, or vanilla JS `change` listener): |
| 54 | + |
| 55 | +```javascript |
| 56 | +// 1. Construct the payload |
| 57 | +const payload = { |
| 58 | + app: 'YourAppName', // Your unique app identifier |
| 59 | + version: '1.0.0', // Schema version |
| 60 | + |
| 61 | + // IMPORTANT: Use a flat structure - do NOT nest related options |
| 62 | + options: { |
| 63 | + 'Color': 'Red', |
| 64 | + 'Size': 'XL', |
| 65 | + 'Material': 'Walnut Wood', |
| 66 | + 'Width': 100, |
| 67 | + 'Height': 75, |
| 68 | + 'Depth': 50, |
| 69 | + 'LED Lighting': true, // Boolean for enabled/disabled features |
| 70 | + 'Wireless Charging': false, |
| 71 | + 'Part 1': true, // Flat structure - keep related options separate |
| 72 | + 'Part 1 Quantity': 2, // Rather than nesting under 'Part 1' |
| 73 | + 'Part 2': false, |
| 74 | + 'Part 2 Quantity': 0, |
| 75 | + 'Engraving Text': 'Custom Name', |
| 76 | + 'Gift Wrapping': true |
| 77 | + }, |
| 78 | + |
| 79 | + // Optional Metadata |
| 80 | + metadata: { |
| 81 | + timestamp: Date.now(), |
| 82 | + productId: '1234567890', |
| 83 | + variantId: '9876543210', |
| 84 | + currency: 'USD', |
| 85 | + totalPrice: 599.99 |
| 86 | + } |
| 87 | +}; |
| 88 | + |
| 89 | +// 2. Create the Native CustomEvent |
| 90 | +// Note: All data must be passed within the 'detail' property |
| 91 | +const event = new CustomEvent('3dbits.productOptions.changed', { |
| 92 | + detail: payload, |
| 93 | + bubbles: true, // Allows the event to bubble up the DOM |
| 94 | + composed: true // Crucial: Allows event to pass through Shadow DOM boundaries |
| 95 | +}); |
| 96 | + |
| 97 | +// 3. Dispatch to the window |
| 98 | +window.dispatchEvent(event); |
| 99 | +``` |
| 100 | + |
| 101 | +## Security Considerations |
| 102 | + |
| 103 | +This approach is secure because: |
| 104 | + |
| 105 | +1. **No Code Execution**: 3D Bits never executes code provided by your app; it only parses the JSON data in the `detail` property |
| 106 | +2. **Read-Only Integration**: 3D Bits acts purely as a subscriber and cannot modify your app's state |
| 107 | +3. **Isolation**: Because we use the browser's native event loop, an error in the 3D Bits listener will not crash your application, and vice-versa |
| 108 | + |
| 109 | +## Event Schema Specification |
| 110 | + |
| 111 | +<Admonition type="note" title="Flexible Integration"> |
| 112 | +While we recommend the schema below for consistency across integrations, we understand that your app may already have its own event system or preferred data structure. If you prefer to use a different event name or structure your data differently, **we're happy to adapt**. Simply [contact us](#contact-information) with your proposed event structure, and we'll add support for your specific implementation in 3D Bits. |
| 113 | +</Admonition> |
| 114 | + |
| 115 | +### Event Name |
| 116 | + |
| 117 | +Use the exact namespace: |
| 118 | + |
| 119 | +```javascript |
| 120 | +3dbits.productOptions.changed |
| 121 | +``` |
| 122 | + |
| 123 | +### Event Data Structure (TypeScript) |
| 124 | + |
| 125 | +The `detail` property of the event should follow this interface: |
| 126 | + |
| 127 | +```typescript |
| 128 | +interface ProductOptionsEventDetail { |
| 129 | + // Identifying information |
| 130 | + app: string; // Your app's unique identifier |
| 131 | + version: string; // Use '1.0.0' |
| 132 | + |
| 133 | + // Option data |
| 134 | + // Key: Human Readable Name (displayed to user) |
| 135 | + // Value: The selected value |
| 136 | + options: { |
| 137 | + [optionName: string]: string | number | boolean; |
| 138 | + }; |
| 139 | + |
| 140 | + // Optional metadata helpful for advanced logic |
| 141 | + metadata?: { |
| 142 | + timestamp?: number; |
| 143 | + productId?: string; |
| 144 | + variantId?: string; |
| 145 | + currency?: string; |
| 146 | + totalPrice?: number; |
| 147 | + }; |
| 148 | +} |
| 149 | +``` |
| 150 | + |
| 151 | +<Admonition type="info" title="Important: Option Naming Requirements"> |
| 152 | +**Option names in the `options` object should be the user-facing names.** 3D Bits maps the 3D configuration based on the labels the merchant sees in their 3D Bits debug mode. Also please make sure that these option names remain unaffected by translations. |
| 153 | + |
| 154 | +**Examples:** |
| 155 | +- ✅ **Good**: `"options": { "Material": "Oak" }` |
| 156 | +- ❌ **Bad**: `"options": { "mat_opt_id_88": "Oak" }` (Internal Database IDs) |
| 157 | +- ❌ **Bad**: `"options": { "material-selection": "Oak" }` (CSS Classes/Slugs) |
| 158 | + |
| 159 | +If your app relies on IDs internally, please map them to the display label before dispatching the event. |
| 160 | +</Admonition> |
| 161 | + |
| 162 | +## When to Fire Events |
| 163 | + |
| 164 | +Your app should dispatch the event: |
| 165 | + |
| 166 | +1. **On Initial Load**: Once your app has calculated the default selections |
| 167 | +2. **On User Interaction**: Immediately when any option value changes |
| 168 | +3. **Range Sliders**: For continuous inputs like range sliders (e.g., dimensions), either: |
| 169 | + - **Debounce** the dispatch by 100-150ms during active dragging, OR |
| 170 | + - **Fire only on drag end** (mouseup/touchend events) to avoid excessive event firing while the user is still adjusting the value |
| 171 | + |
| 172 | +## Benefits for Our Mutual Users |
| 173 | + |
| 174 | +### Stability and Reliability |
| 175 | + |
| 176 | +- **No More Breaking Changes**: Updates to your app's DOM structure won't break 3D integrations |
| 177 | +- **Predictable Behavior**: A versioned schema ensures forward compatibility |
| 178 | +- **Reduced Support Burden**: Fewer integration issues mean fewer support tickets |
| 179 | + |
| 180 | +### Enhanced Product Offerings |
| 181 | + |
| 182 | +- **Competitive Advantage**: Stand out as a developer who supports advanced 3D use cases |
| 183 | +- **Ecosystem Growth**: Enable integrations not just with 3D Bits but with any other script listening for standard events |
| 184 | + |
| 185 | +## Testing Your Integration |
| 186 | + |
| 187 | +### Debug Mode |
| 188 | + |
| 189 | +When users enable [Debug Mode](../tutorials/getting-started/common-settings#enable-debug-mode) in 3D Bits, they will see the incoming event stream in the 3D Bits UI. |
| 190 | + |
| 191 | +### Test Scenarios |
| 192 | + |
| 193 | +We recommend testing: |
| 194 | + |
| 195 | +1. **Initial Load**: Verify the event fires with default selections immediately after page load |
| 196 | +2. **Single Changes**: Test each option type (radio, checkbox, select, text) |
| 197 | +3. **Browser Console**: You can verify your own integration by opening the DevTools console and typing: |
| 198 | + |
| 199 | +```javascript |
| 200 | +window.addEventListener('3dbits.productOptions.changed', e => console.log(e.detail)); |
| 201 | +``` |
| 202 | + |
| 203 | +## Alternative Approach: Stable HTML Form Attributes |
| 204 | + |
| 205 | +We understand that implementing a JavaScript event system may not be feasible for all apps immediately. If you cannot implement the event dispatcher, we recommend following HTML form best practices to ensure stable integrations. |
| 206 | + |
| 207 | +### Use Descriptive, Stable `name` Attributes |
| 208 | + |
| 209 | +When 3D Bits falls back to automatic form detection, it relies on the `name` attribute. |
| 210 | + |
| 211 | +<Admonition type="tip" title="Best Practices for Form Attributes"> |
| 212 | +To maintain stable integrations without breaking changes, we recommend: |
| 213 | + |
| 214 | +- **Use Descriptive Names**: Consider using `name="color"` or `name="material"` |
| 215 | +- **Avoid Auto-Generated IDs**: Try to avoid names like `name="field_12345_xyz"`. If you re-deploy your app and that ID changes, the customer's 3D configuration may break |
| 216 | +- **Semantic Values**: Consider using `value="red"` rather than internal IDs like `value="opt_id_99"` |
| 217 | +</Admonition> |
| 218 | + |
| 219 | +### Example: Good HTML Structure |
| 220 | + |
| 221 | +```html |
| 222 | +<!-- ✅ GOOD: Stable, descriptive attributes --> |
| 223 | +<input type="radio" name="material" value="wood" checked /> |
| 224 | +<input type="checkbox" name="led_lighting" value="true" /> |
| 225 | +``` |
| 226 | + |
| 227 | +### Example: Poor HTML Structure |
| 228 | + |
| 229 | +```html |
| 230 | +<!-- ❌ BAD: Unstable names that break integrations --> |
| 231 | +<input type="radio" name="shopify_app_layer_12837" value="88374" /> |
| 232 | +<div data-value="wood" onclick="...">Custom Div (No Input Tag)</div> |
| 233 | +``` |
| 234 | + |
| 235 | +[**Learn More about Integration**](./integration.md) |
| 236 | + |
| 237 | +## Next Steps |
| 238 | + |
| 239 | +If you're interested in exploring this integration: |
| 240 | + |
| 241 | +1. **Review the Implementation**: Consider how this event pattern might fit into your app's architecture |
| 242 | +2. **Test Locally**: Try dispatching the events in a development environment |
| 243 | +3. **Reach Out**: We'd love to hear your feedback or discuss how we can adapt to your existing event structure |
| 244 | + |
| 245 | +We're eager to collaborate and make this integration work smoothly for our mutual customers. |
| 246 | + |
| 247 | +### What We Can Offer |
| 248 | + |
| 249 | +Once the integration is live, we'd be happy to: |
| 250 | + |
| 251 | +- **List Your App**: Feature your app in our [third-party apps documentation](../tutorials/getting-started/apps-for-3d-bits) with a note about the native integration support |
| 252 | +- **Provide Examples**: Include your app in our integration examples to help customers get started |
| 253 | +- **Share Knowledge**: Document any learnings that could help other developers |
| 254 | + |
| 255 | +Our goal is simply to make it easier for customers using both our apps to build great 3D configurators without worrying about breaking changes. |
| 256 | + |
| 257 | +## Contact Information |
| 258 | + |
| 259 | + |
| 260 | + |
| 261 | +We appreciate you taking the time to consider this integration approach and look forward to potentially working together. |
| 262 | + |
0 commit comments