Widget Integration Patterns
This guide demonstrates common patterns for integrating multiple AlphaSense widgets to create powerful, interconnected user experiences.
widget documentation:
- Generative Search Integration - Citation handling and conversation management
- Document Viewer Integration - Basic document selection patterns
Then return here for advanced multi-widget layouts and best practices. :::
Document Discovery and Viewing
The most common integration pattern combines widgets that help users discover documents with those that display them.
Generative Search + Document Viewer
Create an AI-powered research interface where users can ask questions and immediately view cited sources:
// Container layout (side-by-side)
// <div style="display: flex; height: 600px;">
// <div id="generative-search" style="width: 40%; margin-right: 10px;"></div>
// <div id="document-viewer" style="width: 60%;"></div>
// </div>
let documentViewer = new AlphaSenseWidget({
target: '#document-viewer',
widgetType: 'documentViewer',
documentViewerParams: {
docId: 'welcome-document-id', // Initial welcome document
},
width: '100%',
height: '100%',
}).init()
const generativeSearch = new AlphaSenseWidget({
target: '#generative-search',
widgetType: 'generativeSearch',
generativeSearchParams: {
initialPrompt: 'What are the latest trends in renewable energy?',
},
onDocumentClick: docId => {
updateDocumentViewer(docId)
},
width: '100%',
height: '100%',
}).init()
function updateDocumentViewer(docId) {
documentViewer.destroy()
documentViewer = new AlphaSenseWidget({
target: '#document-viewer',
widgetType: 'documentViewer',
documentViewerParams: {docId},
width: '100%',
height: '100%',
}).init()
}
Document List + Document Viewer
Traditional document browsing experience with list and preview:
// Container layout (side-by-side)
// <div style="display: flex; height: 600px;">
// <div id="document-list" style="width: 35%; margin-right: 10px;"></div>
// <div id="document-viewer" style="width: 65%;"></div>
// </div>
let documentViewer = new AlphaSenseWidget({
target: '#document-viewer',
widgetType: 'documentViewer',
documentViewerParams: {
docId: 'initial-document-id',
},
width: '100%',
height: '100%',
}).init()
const documentList = new AlphaSenseWidget({
target: '#document-list',
widgetType: 'documentList',
companyParams: {
tickerCode: 'AAPL',
},
onDocumentClick: docId => {
updateDocumentViewer(docId)
},
width: '100%',
height: '100%',
}).init()
function updateDocumentViewer(docId) {
documentViewer.destroy()
documentViewer = new AlphaSenseWidget({
target: '#document-viewer',
widgetType: 'documentViewer',
documentViewerParams: {docId},
width: '100%',
height: '100%',
}).init()
}
Three-Panel Research Interface
Combine multiple discovery methods with document viewing for comprehensive research:
// Container layout (three columns)
// <div style="display: flex; height: 600px;">
// <div id="document-list" style="width: 25%; margin-right: 10px;"></div>
// <div id="generative-search" style="width: 35%; margin-right: 10px;"></div>
// <div id="document-viewer" style="width: 40%;"></div>
// </div>
let documentViewer = new AlphaSenseWidget({
target: '#document-viewer',
widgetType: 'documentViewer',
documentViewerParams: {
docId: 'initial-document-id',
},
width: '100%',
height: '100%',
}).init()
// Document List for browsing
const documentList = new AlphaSenseWidget({
target: '#document-list',
widgetType: 'documentList',
companyParams: {
tickerCode: 'AAPL',
},
onDocumentClick: docId => {
updateDocumentViewer(docId, 'Document List')
},
width: '100%',
height: '100%',
}).init()
// Generative Search for AI-powered discovery
const generativeSearch = new AlphaSenseWidget({
target: '#generative-search',
widgetType: 'generativeSearch',
onDocumentClick: docId => {
updateDocumentViewer(docId, 'Generative Search')
},
width: '100%',
height: '100%',
}).init()
function updateDocumentViewer(docId, source) {
console.log(`Document selected from ${source}:`, docId)
documentViewer.destroy()
documentViewer = new AlphaSenseWidget({
target: '#document-viewer',
widgetType: 'documentViewer',
documentViewerParams: {docId},
width: '100%',
height: '100%',
}).init()
}
Tabbed Interface Pattern
Organize different widget types in tabs for better space utilization:
<!-- Tab navigation -->
<div class="tab-navigation">
<button class="tab-btn active" onclick="showTab('search')">AI Search</button>
<button class="tab-btn" onclick="showTab('browse')">Browse Documents</button>
<button class="tab-btn" onclick="showTab('company')">Company Info</button>
</div>
<!-- Tab content containers -->
<div style="height: 600px;">
<div id="search-tab" class="tab-content active" style="height: 100%;">
<div style="display: flex; height: 100%;">
<div id="generative-search" style="width: 40%; margin-right: 10px;"></div>
<div id="document-viewer-search" style="width: 60%;"></div>
</div>
</div>
<div id="browse-tab" class="tab-content" style="height: 100%; display: none;">
<div style="display: flex; height: 100%;">
<div id="document-list" style="width: 40%; margin-right: 10px;"></div>
<div id="document-viewer-browse" style="width: 60%;"></div>
</div>
</div>
<div id="company-tab" class="tab-content" style="height: 100%; display: none;">
<div id="company-summary" style="height: 100%;"></div>
</div>
</div>
let activeTab = 'search'
let widgets = {}
// Initialize all widgets
function initializeWidgets() {
// Search tab widgets
widgets.documentViewerSearch = new AlphaSenseWidget({
target: '#document-viewer-search',
widgetType: 'documentViewer',
documentViewerParams: {docId: 'initial-doc-id'},
width: '100%',
height: '100%',
}).init()
widgets.generativeSearch = new AlphaSenseWidget({
target: '#generative-search',
widgetType: 'generativeSearch',
onDocumentClick: docId => {
updateDocumentViewer('search', docId)
},
width: '100%',
height: '100%',
}).init()
// Browse tab widgets (initialize when first shown)
// Company tab widgets (initialize when first shown)
}
function showTab(tabName) {
// Hide all tabs
document.querySelectorAll('.tab-content').forEach(tab => {
tab.style.display = 'none'
})
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.classList.remove('active')
})
// Show selected tab
document.getElementById(`${tabName}-tab`).style.display = 'block'
event.target.classList.add('active')
// Initialize widgets for tab if not already done
if (tabName === 'browse' && !widgets.documentList) {
widgets.documentViewerBrowse = new AlphaSenseWidget({
target: '#document-viewer-browse',
widgetType: 'documentViewer',
documentViewerParams: {docId: 'initial-doc-id'},
width: '100%',
height: '100%',
}).init()
widgets.documentList = new AlphaSenseWidget({
target: '#document-list',
widgetType: 'documentList',
companyParams: {tickerCode: 'AAPL'},
onDocumentClick: docId => {
updateDocumentViewer('browse', docId)
},
width: '100%',
height: '100%',
}).init()
}
if (tabName === 'company' && !widgets.companySummary) {
widgets.companySummary = new AlphaSenseWidget({
target: '#company-summary',
widgetType: 'companySummary',
companyParams: {tickerCode: 'AAPL'},
width: '100%',
height: '100%',
}).init()
}
activeTab = tabName
}
function updateDocumentViewer(tab, docId) {
const viewerWidget = widgets[`documentViewer${tab.charAt(0).toUpperCase() + tab.slice(1)}`]
if (viewerWidget) {
viewerWidget.destroy()
widgets[`documentViewer${tab.charAt(0).toUpperCase() + tab.slice(1)}`] = new AlphaSenseWidget({
target: `#document-viewer-${tab}`,
widgetType: 'documentViewer',
documentViewerParams: {docId},
width: '100%',
height: '100%',
}).init()
}
}
// Initialize on page load
initializeWidgets()
Modal/Popup Document Viewer
Open documents in modal overlays while keeping the main interface intact:
const documentList = new AlphaSenseWidget({
target: '#document-list',
widgetType: 'documentList',
companyParams: {
tickerCode: 'AAPL',
},
onDocumentClick: docId => {
openDocumentModal(docId)
},
width: '100%',
height: '500px',
}).init()
function openDocumentModal(docId) {
// Create modal container
const modal = document.createElement('div')
modal.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.8);
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
`
const modalContent = document.createElement('div')
modalContent.style.cssText = `
width: 90%;
height: 90%;
background: white;
border-radius: 8px;
position: relative;
`
const closeButton = document.createElement('button')
closeButton.textContent = '×'
closeButton.style.cssText = `
position: absolute;
top: 10px;
right: 15px;
background: none;
border: none;
font-size: 24px;
cursor: pointer;
z-index: 1001;
`
const viewerContainer = document.createElement('div')
viewerContainer.id = 'modal-document-viewer'
viewerContainer.style.cssText = `
width: 100%;
height: 100%;
border-radius: 8px;
`
modalContent.appendChild(closeButton)
modalContent.appendChild(viewerContainer)
modal.appendChild(modalContent)
document.body.appendChild(modal)
// Initialize document viewer in modal
const modalViewer = new AlphaSenseWidget({
target: '#modal-document-viewer',
widgetType: 'documentViewer',
documentViewerParams: {docId},
width: '100%',
height: '100%',
}).init()
// Close modal functionality
const closeModal = () => {
modalViewer.destroy()
document.body.removeChild(modal)
}
closeButton.addEventListener('click', closeModal)
modal.addEventListener('click', e => {
if (e.target === modal) closeModal()
})
// ESC key to close
const handleEscape = e => {
if (e.key === 'Escape') {
closeModal()
document.removeEventListener('keydown', handleEscape)
}
}
document.addEventListener('keydown', handleEscape)
}
Best Practices for Integration
Memory Management
class WidgetManager {
constructor() {
this.widgets = new Map()
}
createWidget(id, config) {
// Destroy existing widget if it exists
if (this.widgets.has(id)) {
this.widgets.get(id).destroy()
}
const widget = new AlphaSenseWidget(config).init()
this.widgets.set(id, widget)
return widget
}
destroyWidget(id) {
if (this.widgets.has(id)) {
this.widgets.get(id).destroy()
this.widgets.delete(id)
}
}
destroyAll() {
this.widgets.forEach(widget => widget.destroy())
this.widgets.clear()
}
}
const widgetManager = new WidgetManager()
// Use throughout your application
const documentViewer = widgetManager.createWidget('main-viewer', {
target: '#document-viewer',
widgetType: 'documentViewer',
documentViewerParams: {docId: 'doc-123'},
})
Error Handling
function createWidgetWithErrorHandling(config, containerId) {
try {
return new AlphaSenseWidget(config).init()
} catch (error) {
console.error(`Failed to initialize widget in ${containerId}:`, error)
// Show error state in container
document.getElementById(containerId.replace('#', '')).innerHTML = `
<div class="widget-error">
<h4>Unable to load widget</h4>
<p>${error.message}</p>
<button onclick="retryWidget('${containerId}')">Retry</button>
</div>
`
return null
}
}
function retryWidget(containerId) {
// Clear error state and retry initialization
document.getElementById(containerId.replace('#', '')).innerHTML =
'<div class="loading">Loading widget...</div>'
// Retry with original configuration
// Implementation depends on your specific setup
}
Responsive Design
function createResponsiveLayout() {
const isMobile = window.innerWidth < 768
if (isMobile) {
// Stack widgets vertically on mobile
return {
searchConfig: {
target: '#search-container',
widgetType: 'generativeSearch',
width: '100%',
height: '300px',
},
viewerConfig: {
target: '#viewer-container',
widgetType: 'documentViewer',
documentViewerParams: {docId: 'initial-doc'},
width: '100%',
height: '400px',
},
}
} else {
// Side-by-side on desktop
return {
searchConfig: {
target: '#search-container',
widgetType: 'generativeSearch',
width: '100%',
height: '600px',
},
viewerConfig: {
target: '#viewer-container',
widgetType: 'documentViewer',
documentViewerParams: {docId: 'initial-doc'},
width: '100%',
height: '600px',
},
}
}
}
// Initialize with responsive configuration
const configs = createResponsiveLayout()
const searchWidget = new AlphaSenseWidget(configs.searchConfig).init()
const viewerWidget = new AlphaSenseWidget(configs.viewerConfig).init()
// Re-initialize on window resize
window.addEventListener(
'resize',
debounce(() => {
const newConfigs = createResponsiveLayout()
// Recreate widgets with new configurations
}, 300),
)
These integration patterns provide flexible ways to combine AlphaSense widgets for different use cases, from simple document viewing to complex research interfaces.