Skip to main content
Version: Beta

Widget Integration Patterns

This guide demonstrates common patterns for integrating multiple AlphaSense widgets to create powerful, interconnected user experiences.

Quick Start If you're new to widget integration, start with the basic examples in individual

widget documentation:

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.