Introduction
Note: This guide provides conceptual overviews and architectural patterns for building custom SAC widgets. Code examples are illustrative and designed to teach principles rather than provide production-ready implementations. For enterprise-grade custom widget development, contact our team for consultation.
While SAP Analytics Cloud (SAC) offers 25+ standard chart types, sometimes your business needs require unique visualizations or interactive components that don’t exist out of the box. That’s where custom widgets come in.
This comprehensive guide walks you through the concepts and architecture of building custom SAC widgets, covering:
- Widget SDK fundamentals and architecture
- Development environment setup
- Design patterns and best practices
- Deployment strategies and versioning
- Testing and quality assurance approaches
Whether you’re evaluating custom widget feasibility or planning development, this guide provides the foundational knowledge needed.
When to Build Custom Widgets
✓ Good Use Cases
1. Specialized Visualizations
- Sankey diagrams for flow analysis
- Network graphs for relationship mapping
- Custom gauge charts matching brand guidelines
- 3D visualizations for spatial data
2. Third-Party Integrations
- Embed Google Maps with custom markers
- Real-time stock tickers from financial APIs
- Social media sentiment feeds
- IoT sensor dashboards
3. Advanced Interactivity
- Drag-and-drop planning interfaces
- Custom drill-down animations
- Gamification elements (leaderboards, progress rings)
- Interactive tutorials within dashboards
4. Branding Requirements
- Corporate-specific KPI cards
- Branded loading animations
- Custom icon sets
- Proprietary chart types
❌ When NOT to Build Custom Widgets
- Standard chart types exist that meet 90% of requirements
- Timeline is critical (development takes 2-4 weeks)
- No in-house JavaScript expertise
- Widget would be used in only 1-2 dashboards
Alternative: Consider SAC’s native customization options first:
- Custom styling with CSS
- Scripting for dynamic behavior
- Combined standard charts with overlay techniques
Prerequisites
Technical Skills Required
| Skill | Level | Purpose |
|---|---|---|
| JavaScript (ES6+) | Intermediate | Widget logic and interactivity |
| HTML5/CSS3 | Intermediate | Widget structure and styling |
| D3.js or Chart.js | Basic-Intermediate | Data visualization libraries |
| JSON | Basic | Data handling and configuration |
| Git | Basic | Version control |
| SAC Platform | Intermediate | Dashboard integration |
Development Tools
Required:
- Text editor (VS Code recommended)
- SAC Custom Widget SDK
- Node.js v18+ (for local development server)
- Chrome/Firefox DevTools
Optional:
- Postman (API testing)
- GitHub/GitLab (version control)
- Figma (design mockups)
Widget SDK Architecture
File Structure
my-custom-widget/
├── widget.json # Widget metadata and properties
├── main.js # Main widget logic
├── styling.css # Widget styles
├── icon.png # Widget icon (48x48px)
├── /libs/ # Third-party libraries
│ ├── d3.min.js
│ └── chart.min.js
└── README.md # Documentation
widget.json - Configuration File
{
"id": "com.varnika.sankey_chart",
"version": "1.0.0",
"name": "Sankey Flow Diagram",
"description": "Visualize flow and relationships between entities",
"vendor": "Varnika IT Consulting",
"license": "Apache 2.0",
"icon": "icon.png",
"webcomponents": [
{
"kind": "main",
"tag": "sankey-widget",
"url": "main.js"
},
{
"kind": "styling",
"tag": "sankey-widget-styling",
"url": "styling.js"
},
{
"kind": "builder",
"tag": "sankey-widget-builder",
"url": "builder.js"
}
],
"properties": {
"width": {
"type": "integer",
"default": 800
},
"height": {
"type": "integer",
"default": 600
},
"colorScheme": {
"type": "string",
"description": "Color palette for flows",
"default": "blues",
"enum": ["blues", "greens", "category10"]
},
"nodeWidth": {
"type": "integer",
"description": "Width of node rectangles",
"default": 15,
"minimum": 10,
"maximum": 30
}
},
"dataBindings": {
"nodes": {
"type": "Dimension[]",
"description": "Source and target nodes"
},
"links": {
"type": "Measure[]",
"description": "Flow values between nodes"
}
},
"events": {
"onNodeClick": {
"description": "Triggered when user clicks a node"
}
}
}
Understanding Panel Types
SAC Custom Widgets Support Three Panel Types:
1. Main Panel (Required)
- The widget visualization itself
- Handles data rendering and user interactions
- Implements core business logic
2. Styling Panel (Optional)
- Controls visual appearance only
- Properties: colors, fonts, borders, spacing, themes
- Best Practice: Keep all styling properties here, not in builder
- Users access via “Styling” tab in SAC widget properties
3. Builder Panel (Optional)
- Controls functional configuration
- Properties: data mappings, calculation logic, behavioral settings
- Important SAC Limitation: Cannot use both builder panel AND data bindings
- With data bindings: Use styling panel only
- Without data bindings: Can use both styling and builder panels
- Users access via “Builder” tab in SAC widget properties
Panel Communication Pattern:
// Styling or builder panel communicates with main widget
this.dispatchEvent(new CustomEvent('propertiesChanged', {
detail: {
properties: {
chartType: 'bar',
colorScheme: 'blues'
}
}
}));
// Main widget listens and updates
this.addEventListener('propertiesChanged', (event) => {
const newProps = event.detail.properties;
this._applyProperties(newProps);
this._render();
});
Example 1: Custom Visualization Widget Conceptual Overview
Understanding Widget Architecture
Custom SAC widgets follow the Web Components standard. Here’s the conceptual structure:
High-Level Architecture:
// Widget Structure (Conceptual Overview)
// Best practice: Extend a base class for shared functionality
class CustomWidget extends HTMLElement {
constructor() {
super();
// Initialize shadow DOM
// Set default properties
// Prepare template
}
// SAC Widget Lifecycle Hooks
onCustomWidgetInit() {
// Called once when widget is first initialized
// Set up event listeners, load libraries
}
onCustomWidgetBeforeUpdate(changedProperties) {
// Called before data binding or property updates
// Validate incoming data, prepare for changes
}
onCustomWidgetAfterUpdate(changedProperties) {
// Called after data binding or property updates
// Update visualization with new data
// Access data via this.dataBinding.getDataBinding()
}
onCustomWidgetResize(width, height) {
// Called when widget container is resized
// Adjust layout and re-render
}
onCustomWidgetDestroy() {
// Called when widget is removed from dashboard
// Clean up event listeners, timers, resources
}
set propertyName(value) {
// Handle property changes
// Trigger re-render if needed
}
_render() {
// Core rendering logic
// Use D3.js, Chart.js, or Canvas
// Apply styling and interactions
}
}
Key Implementation Concepts
1. Shadow DOM for Encapsulation:
- Isolates widget styles from parent page
- Prevents CSS conflicts
- Enables reusable components
2. Property Management:
- Define configurable properties (width, height, colors)
- Implement getters and setters for reactivity
- Validate input values
3. Data Binding:
- Accept data from SAC data source
- Transform data into visualization format
- Handle data updates efficiently
4. Event Handling:
- Dispatch custom events for user interactions
- Enable drill-down and filtering
- Communicate with SAC dashboard
5. Visualization Libraries:
- D3.js: Complex, custom visualizations
- Chart.js: Standard charts with customization
- Canvas API: High-performance rendering
Sample Widget Flow
User Action in SAC
↓
Data Binding Updates
↓
onCustomWidgetBeforeUpdate()
↓
Data validation
↓
onCustomWidgetAfterUpdate()
↓
_render() method
↓
Visualization library (D3/Chart.js)
↓
Update DOM/Canvas
↓
Display to user
Data Structure Concepts
Typical Data Format for Custom Widgets:
{
"metadata": {
"dimensions": ["Region", "Product"],
"measures": ["Revenue", "Quantity"]
},
"data": [
{ "region": "North", "product": "A", "revenue": 5000 },
{ "region": "South", "product": "B", "revenue": 3000 }
]
}
Data Transformation Pattern:
// Conceptual data processing with data binding
onCustomWidgetAfterUpdate(changedProperties) {
// 1. Get bound data from SAC
const dataBinding = this.dataBinding.getDataBinding();
// 2. Validate structure
if (!this._isValidData(dataBinding)) {
this._showError("Invalid data format");
return;
}
// 3. Transform for visualization
const processedData = this._transformData(dataBinding);
// 4. Update visualization
this._updateVisualization(processedData);
}
Deployment Strategies
Deployment Options:
Option A: Direct Upload to SAC
- Open SAC → Files → Public → Custom Widgets folder
- Upload widget package (zip containing widget.json, main.js, styling.js, builder.js, icon.png)
- Widget becomes available in chart picker within 5-10 minutes
- Best for: Development and testing
Option B: CDN Hosting (Recommended for Production)
- Host widget files on corporate CDN or cloud storage
- Configure SAC to reference external URL
- Benefits:
- Centralized version management
- Faster updates across all dashboards
- Better performance with CDN caching
- Best for: Production deployments with multiple users
Option C: SAP BTP Deployment (Enterprise)
# Conceptual deployment workflow
1. Package widget with manifest.yml
2. Deploy to SAP BTP Cloud Foundry space
3. Register widget URL in SAC tenant
4. Manage versions via BTP DevOps
Security & Integrity in Production
Subresource Integrity (SRI):
- Generate SHA-384 hash of all widget files
- Include in manifest to prevent tampering
- SAC validates file integrity before loading
// widget.json with integrity hashes
{
"webcomponents": [
{
"kind": "main",
"tag": "my-widget",
"url": "main.js",
"integrity": "sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/ux..."
}
]
}
Production Build Checklist:
- ✓ Code minification and obfuscation
- ✓ Remove console.log and debugging code
- ✓ Generate integrity hashes (never edit manually)
- ✓ Increment version number
- ✓ Test in staging environment first
- ✓ Document breaking changes in changelog
Security Best Practices:
- Validate all user inputs and data bindings
- Sanitize HTML to prevent XSS attacks
- Use CSP-compliant code (no inline scripts/styles in production)
- Implement error boundaries to prevent widget crashes
- Log errors to monitoring system (not console)
Best Practice: Use development SAC tenant for testing before promoting to production.
Example 2: Simple KPI Card Widget - Conceptual Approach
Widget Purpose: Display single metric with trend indicator and mini sparkline
Design Principles
Visual Structure:
┌────────────────────────────┐
│ KPI Label │
│ $1.25M │ ← Main value (large, bold)
│ ▲ 15.3% vs last month │ ← Change indicator
│ ▬▬▬▬▬▬▬▬ │ ← Sparkline trend
└────────────────────────────┘
Implementation Concepts
1. Property Configuration:
label: String (e.g., “Monthly Revenue”)value: Number (e.g., 1250000)change: Number (percentage, e.g., 15.3)history: Array of numbers for sparklinethresholds: Object defining color rules
2. Value Formatting:
// Conceptual formatter
_formatValue(num) {
// Apply business logic:
// - Millions: 1.25M
// - Thousands: 125K
// - Add currency symbol
// - Respect locale settings
}
3. Trend Visualization:
- Use HTML5 Canvas for performance
- Draw simple line chart
- Highlight recent data points
- Apply color based on trend direction
4. Responsive Behavior:
// Conceptual responsive design
_adjustLayout() {
if (containerWidth < 300) {
// Hide sparkline, show only KPI
} else if (containerWidth < 500) {
// Compact layout
} else {
// Full layout with all details
}
}
Integration with SAC
Conceptual Usage Pattern:
// In SAC story scripting
var customWidget = Canvas.getWidgetById("CustomKPI_1");
// Set properties
customWidget.label = "Monthly Revenue";
customWidget.value = 1250000;
customWidget.change = 15.3;
customWidget.history = [980000, 1050000, 1100000, 1150000, 1250000];
// Listen for widget events
customWidget.addEventListener("onValueClick", function(event) {
// Handle user interaction
// Apply filters, navigate to detail page, etc.
console.log("User clicked KPI:", event.detail);
});
Data Binding from SAC Data Source:
// Data binding is configured in SAC Builder panel
// 1. Select widget in canvas
// 2. In Builder panel, click "Add Data"
// 3. Choose model: "SalesModel"
// 4. Map dimensions and measures to widget's dataBindings:
// - Dimensions: ["TimePeriod"] → nodes
// - Measures: ["Revenue"] → links
// 5. Apply filters in Builder: Region = "North America", Year = "2025"
// Access bound data in widget code:
const dataBinding = this.dataBinding.getDataBinding();
const dimensions = dataBinding.dimensions;
const measures = dataBinding.measures;
Best Practices
1. Performance Optimization
✓ Do:
- Debounce resize events (300ms delay)
- Use
requestAnimationFramefor animations - Cache DOM queries
- Lazy load large libraries
- Implement virtual scrolling for large datasets
❌ Don’t:
- Manipulate DOM on every data point
- Load full D3.js for simple charts (use Chart.js or native Canvas)
- Create memory leaks with event listeners
- Block main thread with heavy calculations
2. Error Handling
Conceptual Error Management:
// Validate data structure in lifecycle method
onCustomWidgetAfterUpdate(changedProperties) {
try {
// Get bound data
const dataBinding = this.dataBinding.getDataBinding();
// Validation rules
if (!dataBinding || !dataBinding.data) {
throw new Error("No data bound to widget");
}
if (!this._hasRequiredFields(dataBinding)) {
throw new Error("Missing required dimensions or measures");
}
if (!this._isDataWithinLimits(dataBinding.data)) {
throw new Error("Data exceeds processing limits");
}
// Process valid data
this._processData(dataBinding.data);
this._render();
} catch (error) {
// Log for debugging
console.error("Widget error:", error);
// Show user-friendly message
this._displayErrorState(error.message);
// Report to monitoring system
this._reportError(error);
}
}
// Display error in widget
_displayErrorState(message) {
// Replace widget content with error message
// Provide actionable feedback
// Maintain widget layout structure
}
Error Categories:
- Data validation errors: Invalid format, missing fields
- Runtime errors: Library loading failures, rendering issues
- Configuration errors: Invalid property values
- Network errors: Failed API calls, timeout issues
3. Accessibility
WCAG 2.1 AA Compliance Checklist:
<!-- Semantic HTML structure -->
<div role="img"
aria-label="Revenue trend visualization showing 15% increase">
<!-- Keyboard navigation support -->
<button tabindex="0"
aria-label="View detailed revenue breakdown">
View Details
</button>
<!-- Screen reader descriptions -->
<span class="sr-only">
Monthly revenue is $1.25 million, up 15.3% from last month
</span>
<!-- Focus indicators -->
<style>
:focus-visible {
outline: 2px solid #F4C430;
outline-offset: 2px;
}
</style>
</div>
Key Accessibility Features:
Keyboard Navigation
- Tab through interactive elements
- Enter/Space to activate
- Escape to close overlays
Screen Reader Support
- ARIA labels for all interactive elements
- Live regions for dynamic updates
- Alternative text for visuals
Color Contrast
- Minimum 4.5:1 ratio for text
- 3:1 ratio for UI components
- Don’t rely solely on color to convey information
Focus Management
- Visible focus indicators
- Logical tab order
- Focus trapping in modals
4. Responsive Design
Conceptual Responsive Behavior:
// Monitor container size changes
connectedCallback() {
// Use ResizeObserver API
this._resizeObserver = new ResizeObserver(entries => {
// Debounce to avoid excessive re-renders
this._debounceResize(() => {
const { width, height } = entries[0].contentRect;
this._handleResize(width, height);
});
});
this._resizeObserver.observe(this);
}
// Apply responsive layout rules
_handleResize(width, height) {
if (width < 400) {
// Mobile layout: Stack elements, larger touch targets
this._applyMobileLayout();
} else if (width < 768) {
// Tablet layout: Condensed view
this._applyTabletLayout();
} else {
// Desktop layout: Full features
this._applyDesktopLayout();
}
// Re-render with new dimensions
this._render();
}
// Cleanup on disconnect
disconnectedCallback() {
this._resizeObserver?.disconnect();
}
Responsive Design Patterns:
| Screen Size | Layout Strategy | Touch Target Size |
|---|---|---|
| < 400px | Single column, simplified | 44px minimum |
| 400-768px | Flexible grid, condensed | 36px minimum |
| > 768px | Full features, detailed | 32px minimum |
Testing Your Widget
Development Testing Strategy
1. Local Testing Environment:
Create a minimal HTML test harness:
<!DOCTYPE html>
<html>
<head>
<title>Widget Development Test</title>
<script src="your-widget.js"></script>
</head>
<body>
<h1>Widget Test Environment</h1>
<!-- Your custom widget -->
<your-widget-name
id="testWidget"
style="width: 800px; height: 600px;">
</your-widget-name>
<script>
// Test script
const widget = document.getElementById("testWidget");
// Test with sample data
widget.setData({ /* test data */ });
// Test property changes
widget.propertyName = "newValue";
// Test event listeners
widget.addEventListener("customEvent", (e) => {
console.log("Event fired:", e.detail);
});
</script>
</body>
</html>
2. Run Local Server:
# Option 1: Python
python3 -m http.server 8000
# Option 2: Node.js
npx http-server
# Option 3: VS Code Live Server extension
# Right-click HTML file → "Open with Live Server"
Testing Checklist
Functional Testing:
- ✓ Widget renders with valid data
- ✓ Properties update correctly
- ✓ Events fire when expected
- ✓ Error handling works gracefully
- ✓ Data validation catches invalid inputs
Performance Testing:
- ✓ Renders in < 1 second with typical data
- ✓ Smooth interactions (60fps)
- ✓ Handles large datasets (test with 10,000+ rows)
- ✓ No memory leaks after 100 renders
Compatibility Testing:
- ✓ Chrome, Firefox, Safari, Edge
- ✓ Mobile browsers (iOS Safari, Chrome Mobile)
- ✓ Different screen sizes (320px to 4K)
- ✓ SAC latest version compatibility
Accessibility Testing:
- ✓ Keyboard navigation works
- ✓ Screen reader announces changes
- ✓ Color contrast meets WCAG AA
- ✓ Focus indicators visible
Testing Strategy
Multi-Level Testing Approach
1. Unit Testing (Component Logic)
// Test individual widget methods
describe('CustomWidget', () => {
test('should format currency correctly', () => {
const widget = new CustomWidget();
expect(widget._formatCurrency(1250000)).toBe('$1.25M');
});
test('should validate data structure', () => {
const widget = new CustomWidget();
const validData = { dimensions: ['Region'], measures: ['Revenue'] };
expect(widget._isValidData(validData)).toBe(true);
});
});
2. Integration Testing (SAC Lifecycle)
// Test widget lifecycle and property sync
describe('SAC Integration', () => {
test('should handle property updates from SAC', () => {
const widget = new CustomWidget();
const changes = { chartType: 'bar', theme: 'dark' };
widget.onCustomWidgetAfterUpdate(changes);
expect(widget.chartType).toBe('bar');
expect(widget.theme).toBe('dark');
});
test('should dispatch events correctly', () => {
const widget = new CustomWidget();
let eventFired = false;
widget.addEventListener('onDataPointClick', () => {
eventFired = true;
});
widget._handleDataPointClick({ value: 100 });
expect(eventFired).toBe(true);
});
});
3. Browser Compatibility Testing
- Chrome (latest 2 versions)
- Firefox (latest 2 versions)
- Safari (latest 2 versions)
- Edge (Chromium-based)
- Mobile browsers (iOS Safari, Chrome Mobile)
4. Performance Testing
// Benchmark rendering with large datasets
describe('Performance', () => {
test('should render 10,000 data points under 1 second', () => {
const widget = new CustomWidget();
const largeDataset = generateTestData(10000);
const startTime = performance.now();
widget.onCustomWidgetAfterUpdate({ data: largeDataset });
const endTime = performance.now();
expect(endTime - startTime).toBeLessThan(1000);
});
test('should not leak memory after 100 renders', () => {
const widget = new CustomWidget();
const initialMemory = performance.memory.usedJSHeapSize;
for (let i = 0; i < 100; i++) {
widget._render();
}
const finalMemory = performance.memory.usedJSHeapSize;
const memoryIncrease = finalMemory - initialMemory;
expect(memoryIncrease).toBeLessThan(5000000); // 5MB threshold
});
});
5. Visual Regression Testing
- Take screenshots of widget in different states
- Compare against baseline images
- Detect unintended visual changes
- Tools: Playwright, Puppeteer, Percy
Testing Workflow:
# Run all tests before deployment
npm test # Unit tests
npm run test:integration # Integration tests
npm run test:e2e # End-to-end browser tests
npm run test:visual # Visual regression
Deployment Checklist& Version Management
Critical Manifest Rules
Required Fields (SAC will reject upload without these):
{
"id": "com.yourcompany.widgetname",
"name": "Widget Display Name",
"vendor": "Your Company Name",
"version": "1.0.0",
"newInstancePrefix": "Widget",
"webcomponents": [ /* at least one */ ]
}
Forbidden Fields (cause upload errors):
- ❌
author(usevendorinstead) - ❌
homepage - ❌
enumValues(useenumwithvaluesarray) - ❌
parametersin events (events don’t have parameters)
Property Type Compliance:
// ✓ Correct
{
"chartType": {
"type": "string",
"description": "Chart visualization type",
"enum": ["bar", "line", "pie"],
"default": "bar"
},
"seriesColors": {
"type": "string[]",
"default": ["#FF0000", "#00FF00", "#0000FF"]
}
}
// ❌ Incorrect - don't use 'enumValues'
{
"chartType": {
"type": "string",
"enumValues": ["bar", "line"] // Wrong!
}
}
Semantic Versioning Strategy
Follow semantic versioning: MAJOR.MINOR.PATCH
- MAJOR (1.0.0 → 2.0.0): Breaking changes (removed properties, changed behavior)
- MINOR (1.0.0 → 1.1.0): New features, backward compatible
- PATCH (1.0.0 → 1.0.1): Bug fixes, no new features
When to Increment Version:
- ✓ Any change to widget files (.js, .json, .css)
- ✓ Property additions or modifications
- ✓ Bug fixes or performance improvements
- ✓ Visual design changes
Version Management Example:
// v1.0.0 - Initial release
{
"version": "1.0.0",
"properties": {
"title": { "type": "string" }
}
}
// v1.1.0 - Add new property (backward compatible)
{
"version": "1.1.0",
"properties": {
"title": { "type": "string" },
"subtitle": { "type": "string", "default": "" } // New
}
}
// v2.0.0 - Breaking change (rename property)
{
"version": "2.0.0",
"properties": {
"heading": { "type": "string" } // Renamed from 'title'
},
"migration": {
"v1ToV2": {
"title": "heading" // Migration map
}
}
}
Deployment Checklist
✓ Code Quality
□ No console.log() statements in production
□ All errors caught and handled
□ Code minified for production
□ No hardcoded test data
✓ Performance
□ Loads in < 1 second
□ Smooth animations (60fps)
□ Works with 10,000+ data points
□ No memory leaks after 100 renders
✓ Compatibility
□ Tested in Chrome, Firefox, Safari
□ Works on mobile devices
□ Compatible with SAC's latest version
□ No conflicts with other custom widgets
✓ Documentation
□ widget.json complete and accurate
□ README with usage examples
□ API documentation for all properties/methods
□ Sample data provided
✓ Versioning
□ Version number updated
□ Changelog maintained
□ Breaking changes documented
Conclusion
Custom SAC widgets unlock unlimited possibilities for visualizations and interactivity. While they require JavaScript development skills and careful planning, the investment pays off when you need specialized analytics experiences that drive better business decisions.
Key Takeaways:
- Understand widget lifecycle hooks (Init, BeforeUpdate, AfterUpdate, Resize, Destroy) before writing code
- Master panel architecture: Builder vs Styling panels, and SAC’s data binding limitations
- Use a base class pattern for shared functionality across widgets (event handling, theming, validation)
- Leverage proven libraries (D3.js, Chart.js) rather than building visualization engines from scratch
- Follow semantic versioning and manifest compliance rules to avoid deployment errors
- Implement Subresource Integrity (SRI) for production security
- Test at multiple levels: unit, integration, browser compatibility, performance, visual regression
- Plan for accessibility (WCAG 2.1) and responsive design from the start
- Document widget APIs and version migration paths clearly
- Always test with the latest SAC version (2025.X releases) for compatibility
- Consider total cost of ownership: development (6-12 weeks) + testing + maintenance + security updates
Development Timeline Expectations:
- Standard widgets (KPI cards, simple gauges, filters): 2-3 weeks
- Advanced widgets (custom charts, interactive visualizations, third-party integrations): 6-10 weeks
- Enterprise solutions (complex data processing, multi-widget systems, advanced interactivity): 12-20 weeks
- Add 30-40% time for testing, documentation, deployment, and production hardening
When to Build vs. Buy:
- Build in-house: Unique business requirements, have skilled team, ongoing customization needs
- Partner with experts: Time-critical, complex requirements, lack internal expertise, need production support
Need Custom SAC Widget Development?
Varnika IT Consulting has built 50+ custom widgets for SAC across industries, from advanced visualizations to third-party API integrations. Our team combines SAP platform expertise with modern web development skills to create widgets that enhance your analytics platform and drive user adoption.
Our Widget Development Services:
- ✓ Requirements analysis and feasibility assessment
- ✓ Proof-of-concept development (2-week sprints)
- ✓ Production-grade implementation with full testing
- ✓ SAC integration and deployment support
- ✓ Documentation and knowledge transfer
- ✓ Ongoing maintenance and enhancement
Investment Ranges:
| Widget Type | Timeline | Investment Range |
|---|---|---|
| Standard Widgets KPI cards, filters, simple gauges, basic data displays | 2-3 weeks | $5,000 - $15,000 |
| Advanced Widgets Custom charts, interactive visualizations, API integrations, multi-panel widgets | 6-10 weeks | $15,000 - $35,000 |
| Enterprise Solutions Complex data processing, multi-widget ecosystems, advanced interactivity, real-time integrations | 12-20 weeks | $35,000 - $50,000+ |
Pricing includes: Development, testing, documentation, deployment support, and 90-day warranty. Ongoing maintenance and SLA support available separately.
Typical Project Timeline: 6-20 weeks from requirements to production deployment, depending on complexity
Schedule a Widget Consultation →
Published: November 15, 2024 | Reading Time: 14 minutes