Custom Widgets via HTML Plugins
On this page, a Nodinite Administrator learns how to create and deploy custom HTML widgets to extend the Dashboard and Monitor View pages. This powerful extensibility feature enables organizations to add custom visualizations, KPIs, integrations, and business-specific functionality directly into the Nodinite Web Client.
Custom widgets transform Nodinite from a standard monitoring platform into a tailored command center for your integration landscape.
- ✅ Zero-code deployment - Drop HTML files into folders, no compilation needed
- ✅ Full framework access - Leverage Bootstrap, Font Awesome, and jQuery already loaded
- ✅ Flexible placement - Dashboard-wide widgets or Monitor View-specific panels
- ✅ Rapid iteration - Edit HTML and refresh - instant updates after IIS recycle
Why Use Custom Widgets?
Custom widgets enable business-specific dashboards and real-time integrations without modifying core Nodinite code.
Business Use Cases
Executive KPI Dashboards
- Scenario: CEO wants real-time integration health summary on Dashboard
- Solution: Custom widget fetches Log API data, displays uptime percentages, SLA compliance
- Benefit: Business leadership sees integration health without technical training
External Alerting Integration
- Scenario: Route critical alerts to Slack, Microsoft Teams, or ServiceNow
- Solution: Monitor View widget with JavaScript webhook calls on status changes
- Benefit: Operations teams get notifications in existing workflow tools
Custom Data Visualizations
- Scenario: Visualize message throughput trends with Chart.js graphs
- Solution: Dashboard widget queries Log API, renders interactive charts
- Benefit: Identify performance bottlenecks and capacity planning needs
Partner Portal Embedding
- Scenario: Display external partner API status alongside internal monitors
- Solution: Custom widget with iframe or AJAX calls to partner status endpoints
- Benefit: Unified view of internal and external integration dependencies
Widget Types
Nodinite supports two widget placement options:
| Feature | Dashboard Widget | Monitor View Widget |
|---|---|---|
| Visibility | All users with Dashboard access | Users with access to specific Monitor View |
| File Location | WebClient\wwwroot\Plugins\ |
WebClient\Plugins\MonitorViews\ |
| Naming Convention | Any name (e.g., kpi-widget.html) |
Must match Monitor View ID (e.g., 9.html) |
| Multiple Widgets | Yes - all .html files concatenated |
One widget per Monitor View |
| Injection Point | Top of Dashboard page | Above notifications section in Monitor View |
| Best For | Global KPIs, company-wide announcements, executive summaries | Context-specific tools, Monitor View-specific charts, environment badges |
Dashboard Widget
Add custom widgets to the main Dashboard page visible to all users.
Step-by-Step: Dashboard Widget
Prerequisites
- Nodinite Administrator access to web server
- File system permissions for
C:\Program Files\Nodinite\[Instance]\WebClient\wwwroot\ - IIS Manager access for app pool recycling
Steps
Navigate to WebClient Directory
C:\Program Files\Nodinite\[Instance]\WebClient\wwwroot\Replace
[Instance]with your Nodinite instance name (e.g.,Production,WaseemDev)Create Plugins Folder (if it doesn't exist)
C:\Program Files\Nodinite\[Instance]\WebClient\wwwroot\Plugins\Create HTML Widget File
- Create a file with any
.htmlname (e.g.,kpi-widget.html,team-announcements.html) - The file name does NOT need to match anything specific
- Add your HTML/CSS/JavaScript content to the file
- Create a file with any
Recycle IIS App Pool
- Open IIS Manager (
inetmgr) - Navigate to Application Pools
- Find the Nodinite app pool (e.g.,
Nodinite_Production) - Right-click → Recycle
- Open IIS Manager (
Verify Widget Appears
- Navigate to
http://[your-server]/Dashboard(e.g.,http://localhost:40000/Dashboard) - Widget appears at the top of the Dashboard page
- Navigate to
Dashboard Widget Example
<!-- File: C:\Program Files\Nodinite\Production\WebClient\wwwroot\Plugins\uptime-kpi.html -->
<div class="card mb-4" style="border-left: 4px solid #28a745;">
<div class="card-header bg-light">
<h5 class="mb-0">
<i class="fas fa-chart-line text-success" aria-hidden="true"></i>
Integration Uptime - Last 24 Hours
</h5>
</div>
<div class="card-body">
<div class="row text-center">
<div class="col-md-4">
<h2 class="text-success mb-1">99.8%</h2>
<p class="text-muted small">Production Uptime</p>
</div>
<div class="col-md-4">
<h2 class="text-info mb-1">45.2M</h2>
<p class="text-muted small">Messages Processed</p>
</div>
<div class="col-md-4">
<h2 class="text-warning mb-1">3</h2>
<p class="text-muted small">Active Incidents</p>
</div>
</div>
</div>
</div>
<script>
// Fetch live data from Nodinite Log API
fetch('/api/logs/summary?hours=24')
.then(response => response.json())
.then(data => {
// Update KPI values with real data
console.log('Live KPI data loaded', data);
})
.catch(error => console.error('KPI widget error:', error));
</script>
Accessibility Notes
- Use semantic HTML:
<div class="card">with proper heading hierarchy - Icons marked
aria-hidden="true"since text labels are present - Color contrast: Success green (#28a745) on white = 4.5:1 (WCAG AA compliant)
- Screen readers announce "Integration Uptime - Last 24 Hours" heading
Monitor View Widget
Add custom widgets to specific Monitor View pages by Monitor View ID.
Step-by-Step: Monitor View Widget
Prerequisites
- Monitor View ID from URL (e.g.,
9fromhttp://localhost:40000/Monitor/MonitorView/9) - File system permissions for
C:\Program Files\Nodinite\[Instance]\WebClient\ - IIS Manager access for app pool recycling
Steps
Identify Monitor View ID
- Navigate to the Monitor View in Nodinite
- Check the URL:
http://localhost:40000/Monitor/MonitorView/9 - The Monitor View ID is
9
Navigate to WebClient Directory
C:\Program Files\Nodinite\[Instance]\WebClient\Create MonitorViews Folder (if it doesn't exist)
C:\Program Files\Nodinite\[Instance]\WebClient\Plugins\MonitorViews\Create HTML Widget File
- File name MUST match Monitor View ID:
{MonitorViewId}.html - Example: For Monitor View ID
9, create9.html - Add your HTML/CSS/JavaScript content to the file
- File name MUST match Monitor View ID:
Recycle IIS App Pool
- Open IIS Manager (
inetmgr) - Navigate to Application Pools
- Find the Nodinite app pool
- Right-click → Recycle
- Open IIS Manager (
Verify Widget Appears
- Navigate to
http://[your-server]/Monitor/MonitorView/9 - Widget appears above the notifications section
- Navigate to
Monitor View Widget Example
<!-- File: C:\Program Files\Nodinite\Production\WebClient\Plugins\MonitorViews\9.html -->
<div class="alert alert-info" role="alert">
<i class="fas fa-info-circle" aria-hidden="true"></i>
<strong>Environment Badge:</strong> This is the <span class="badge bg-warning text-dark">UAT Environment</span>
<span class="visually-hidden">User Acceptance Testing Environment</span>
</div>
<div class="card mb-3">
<div class="card-header">
<i class="fas fa-chart-bar" aria-hidden="true"></i>
Message Throughput - Last Hour
</div>
<div class="card-body">
<canvas id="throughputChart" width="400" height="100" aria-label="Message throughput chart showing messages processed per minute"></canvas>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script>
<script>
// Create a simple bar chart for message throughput
const ctx = document.getElementById('throughputChart').getContext('2d');
const chart = new Chart(ctx, {
type: 'bar',
data: {
labels: ['10:00', '10:15', '10:30', '10:45', '11:00'],
datasets: [{
label: 'Messages/min',
data: [450, 520, 480, 510, 495],
backgroundColor: 'rgba(40, 167, 69, 0.6)',
borderColor: 'rgba(40, 167, 69, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
plugins: {
legend: { display: false }
},
scales: {
y: { beginAtZero: true }
}
}
});
</script>
Accessibility Notes
role="alert"for environment badge - screen readers announce importance<span class="visually-hidden">provides context for screen readersaria-labelon canvas element describes chart purpose- Color coding supplemented with text labels ("UAT Environment")
Common Features
Available Libraries
All widgets have automatic access to the following JavaScript libraries and CSS frameworks already loaded on Nodinite pages:
| Library | Description | Usage Example |
|---|---|---|
| Bootstrap 5.x | Responsive CSS framework | <div class="card">...</div> |
| Font Awesome 6.x | Icon library | <i class="fas fa-chart-line"></i> |
| jQuery 3.x | DOM manipulation library | $('.alert').fadeIn(); |
No need to include <link> or <script> tags for these libraries - they're already loaded.
External Libraries
You can include additional libraries via CDN:
<!-- Example: Chart.js for data visualization -->
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script>
<!-- Example: Moment.js for date formatting -->
<script src="https://cdn.jsdelivr.net/npm/moment@2.29.4/moment.min.js"></script>
<!-- Example: DataTables for interactive tables -->
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.4/css/dataTables.bootstrap5.min.css">
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
Accessing Nodinite APIs
Widgets can interact with Nodinite's Web API to fetch live data:
// Fetch recent log messages
fetch('/api/logs?view=1&top=10')
.then(response => response.json())
.then(data => {
console.log('Recent logs:', data);
// Update widget with live data
})
.catch(error => console.error('API error:', error));
// Fetch monitor status
fetch('/api/monitors/status?view=9')
.then(response => response.json())
.then(data => {
console.log('Monitor status:', data);
// Update status indicators
});
Refer to the Web API documentation for complete endpoint reference.
Best Practices
Do's
- Include ARIA attributes -
aria-label,aria-hidden,role="alert"for accessibility - Test keyboard navigation - All interactive elements accessible via Tab key
- Minimize external dependencies - Leverage Bootstrap and Font Awesome first, add CDN libraries only when needed
- Use descriptive file names - Dashboard widgets:
sales-kpi.htmlnotwidget1.html - Handle API errors gracefully - Show user-friendly error messages, not console errors
- Cache API responses - Avoid hammering Nodinite APIs every second; use reasonable intervals (30s+)
- Responsive design - Test widgets on mobile/tablet; use Bootstrap grid classes
Don'ts
- Avoid blocking scripts - Use
asyncordeferfor external<script>tags - Don't hardcode server URLs - Use relative paths (
/api/logs) nothttp://server/api/logs - Never expose credentials - Don't embed API keys, passwords, or connection strings in HTML
- Avoid excessive DOM manipulation - Batch updates to prevent performance issues
- Don't rely on color alone - Use icons + text for status indicators (WCAG AA)
- Avoid inline styles - Use CSS classes for maintainability
- Don't break existing layout - Test widgets don't overlap or hide Nodinite UI elements
Troubleshooting
Widget Not Appearing
Problem
Created HTML file but widget doesn't show on page
Solutions
Verify IIS App Pool Recycled
- Open IIS Manager → Application Pools → Find Nodinite pool → Recycle
- Wait 10-15 seconds for app pool to restart
Check File Location
- Dashboard:
WebClient\wwwroot\Plugins\my-widget.html - Monitor View:
WebClient\Plugins\MonitorViews\9.html(ID must match)
- Dashboard:
Verify File Extension
- Must be
.html(not.txt,.htm) - Check Windows file extensions are visible (Folder Options → View → Uncheck "Hide extensions")
- Must be
Check File Permissions
- IIS app pool identity must have Read access to the file
- Right-click file → Properties → Security → Ensure
IIS_IUSRSorNETWORK SERVICEhas Read permissions
Browser Cache
- Hard refresh:
Ctrl + F5(Windows) orCmd + Shift + R(macOS) - Clear browser cache and reload
- Hard refresh:
Monitor View Widget Wrong ID
Problem
Widget appears on wrong Monitor View
Solution
- File name must exactly match Monitor View ID from URL
- Monitor View URL:
http://localhost:40000/Monitor/MonitorView/9→ File name:9.html - Monitor View URL:
http://localhost:40000/Monitor/MonitorView/42→ File name:42.html
JavaScript Errors
Problem
Widget appears but JavaScript doesn't execute
Solutions
Check Browser Console
- Press
F12→ Console tab → Look for JavaScript errors - Common errors:
ReferenceError: $ is not defined(jQuery issue)
- Press
Verify Script Placement
- Place
<script>tags at the end of the HTML file (after DOM elements) - Or use
document.addEventListener('DOMContentLoaded', function() {...})
- Place
Check External Library Loading
- If using CDN libraries, verify URL is correct and accessible
- Test CDN URL directly in browser:
https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js
Styling Conflicts
Problem
Widget styling breaks Nodinite UI or vice versa
Solutions
Use Scoped CSS
<style> /* Scope styles to widget container */ .my-custom-widget .card { border: 2px solid #007bff; } </style> <div class="my-custom-widget"> <div class="card">...</div> </div>Avoid Global CSS Resets
- Don't use
* { margin: 0; }orbody { font-family: ... } - These override Nodinite styles
- Don't use
Use Bootstrap Utility Classes
- Leverage existing classes:
mb-3,text-center,alert-info - Consistent styling with Nodinite UI
- Leverage existing classes:
Security Considerations
Input Validation
If widgets accept user input or display external data:
// BAD: Directly inserting user input (XSS vulnerability)
document.getElementById('output').innerHTML = userInput;
// GOOD: Escape HTML entities
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
document.getElementById('output').textContent = userInput; // Or use escapeHtml()
API Authentication
When calling external APIs from widgets:
// Use POST requests for sensitive data
fetch('/api/external/status', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// Do NOT hardcode API keys here - use server-side proxy
},
body: JSON.stringify({ query: 'status' })
});
Never expose API keys in client-side JavaScript - Use server-side proxy endpoints instead.
Content Security Policy
Nodinite may enforce Content Security Policy (CSP) headers. If widgets fail to load external resources:
- Contact your Nodinite Administrator
- Request CSP exceptions for trusted CDN domains
- Or host libraries locally in
wwwroot\lib\folder
Advanced Examples
Real-Time KPI Dashboard Widget
<!-- File: kpi-realtime.html -->
<div class="card mb-4 shadow-sm">
<div class="card-header bg-primary text-white">
<h5 class="mb-0">
<i class="fas fa-tachometer-alt" aria-hidden="true"></i>
Real-Time Integration Health
</h5>
</div>
<div class="card-body">
<div class="row text-center">
<div class="col-md-3">
<h2 id="uptime" class="text-success mb-1">--</h2>
<p class="text-muted small">Uptime %</p>
</div>
<div class="col-md-3">
<h2 id="throughput" class="text-info mb-1">--</h2>
<p class="text-muted small">Msgs/min</p>
</div>
<div class="col-md-3">
<h2 id="errors" class="text-danger mb-1">--</h2>
<p class="text-muted small">Errors (24h)</p>
</div>
<div class="col-md-3">
<h2 id="latency" class="text-warning mb-1">--</h2>
<p class="text-muted small">Avg Latency</p>
</div>
</div>
<div class="text-end mt-2">
<small class="text-muted">
<i class="fas fa-sync-alt fa-spin" aria-hidden="true"></i>
Auto-refreshes every 30s
</small>
</div>
</div>
</div>
<script>
function updateKPIs() {
fetch('/api/logs/summary?hours=24')
.then(response => response.json())
.then(data => {
document.getElementById('uptime').textContent = (data.uptime * 100).toFixed(1) + '%';
document.getElementById('throughput').textContent = data.messagesPerMinute.toLocaleString();
document.getElementById('errors').textContent = data.errorCount.toLocaleString();
document.getElementById('latency').textContent = data.avgLatencyMs + 'ms';
})
.catch(error => {
console.error('KPI update failed:', error);
document.getElementById('uptime').textContent = 'Error';
});
}
// Update immediately and then every 30 seconds
updateKPIs();
setInterval(updateKPIs, 30000);
</script>
Environment Badge for Monitor View
<!-- File: 9.html (for Monitor View ID 9) -->
<div class="alert alert-warning mb-3" role="alert" style="border-left: 5px solid #ff9800;">
<div class="d-flex align-items-center">
<i class="fas fa-exclamation-triangle fa-2x me-3" aria-hidden="true"></i>
<div>
<strong>UAT Environment</strong>
<p class="mb-0">This Monitor View tracks User Acceptance Testing integrations. Changes here do NOT affect Production.</p>
</div>
</div>
</div>
Integration with External Alerting
<!-- File: alert-webhook.html -->
<div class="card mb-3">
<div class="card-header">
<i class="fab fa-slack" aria-hidden="true"></i>
Slack Notifications
</div>
<div class="card-body">
<p>Critical alerts are automatically sent to: <strong>#integrations-alerts</strong></p>
<button id="testSlack" class="btn btn-sm btn-outline-primary" aria-label="Send test message to Slack">
<i class="fas fa-paper-plane" aria-hidden="true"></i>
Send Test Alert
</button>
<div id="slackStatus" class="mt-2" role="status" aria-live="polite"></div>
</div>
</div>
<script>
document.getElementById('testSlack').addEventListener('click', function() {
const statusDiv = document.getElementById('slackStatus');
statusDiv.innerHTML = '<i class="fas fa-spinner fa-spin" aria-hidden="true"></i> Sending...';
fetch('https://hooks.slack.com/services/YOUR/WEBHOOK/URL', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: '🧪 Test alert from Nodinite Custom Widget',
channel: '#integrations-alerts'
})
})
.then(response => {
if (response.ok) {
statusDiv.innerHTML = '<div class="alert alert-success mt-2"><i class="fas fa-check" aria-hidden="true"></i> Test alert sent successfully!</div>';
} else {
throw new Error('Slack webhook failed');
}
})
.catch(error => {
statusDiv.innerHTML = '<div class="alert alert-danger mt-2"><i class="fas fa-times" aria-hidden="true"></i> Failed to send alert. Check webhook URL.</div>';
});
});
</script>
Next Steps
- Plan your widget - Identify Dashboard vs Monitor View placement, sketch layout
- Create HTML file - Start with simple HTML, add styling, then JavaScript
- Test locally - Verify widget appears after IIS recycle, test on different browsers
- Add interactivity - Integrate with Nodinite APIs for live data
- Deploy to production - Copy widget to production server, document for team
Pro Tip: Start with a Dashboard widget showing static content to learn the workflow, then add API calls and advanced features.
Next Step
Related Topics
- Stylesheets - Apply custom CSS to log messages and alerts
- Display Field Configurations - Customize column visibility in Log and Monitor Views
- Web Client - Core web application architecture