In many Visual Builder Studio projects, custom AppUIs are developed to extend Oracle Fusion Cloud applications. Each custom AppUI typically lives in its own Git repository within the Visual Builder workspace. While Oracle provides a rich set of built-in actions and components, developers frequently need custom JavaScript logic — and the same helper functions often get duplicated across multiple AppUIs.
One of the powerful (and underutilized) features of Visual Builder is the ability to create shared JavaScript functions at the resource level and expose them for reuse across different AppUIs via dependencies. This eliminates code duplication, improves maintainability, reduces bugs from inconsistent implementations, and makes future updates much easier.
Business Challenge
A very common example is the need to export data to CSV from various screens. Especially when using the Redwood Collection component, which includes a built-in export button — developers must provide custom code to generate and trigger the file download. Without a shared approach, the same download logic (handling headers, data formatting, Blob creation, etc.) gets copied into every AppUI that needs it.
Other typical candidates for sharing include:
- Date/time formatting helpers
- Currency / number formatting utilities
- Common validation routines
- API response parsing helpers
Solution: Create a Shared JavaScript Module as a Standalone AppUI
The recommended pattern is to create a dedicated “Shared” AppUI that contains only JavaScript functions (no pages/UI), deploy it as an extension, and then add it as a dependency in any consuming AppUI.
Step-by-step guide
- Create a new workspace and Git repository named “Shared” (or “CommonUtils”, “SharedFunctions”, etc.)
In Visual Builder Studio → Development tab → create new workspace → new AppUI project → name it Shared.
- Create the JavaScript function file
Go to Resources → Functions section.
Click “+” to import / create a new file, for example:export.js
- Implement your shared function(s)
Example implementation for CSV download:
define([
'ojs/ojlogger',
], (
Logger
) => {
'use strict';
function exportToCsv (params) {
try {
const {
headerLabels,
rows,
fileNamePrefix,
} = params;
const reportRows = [
headerLabels,
...rows,
];
const csvContent = "data:text/csv;charset=utf-8,\uFEFF" + reportRows.map(e => e.join(",")).join("\n");
const encodedUri = encodeURI(csvContent);
const link = document.createElement("a");
link.setAttribute("href", encodedUri);
link.setAttribute("download", `${fileNamePrefix} - ${getDateTimeString()}.csv`);
document.body.appendChild(link);
link.click();
} catch (e) {
Logger.error(e);
throw new Error("Failed to export to csv.", {
cause: e,
});
}
}
return {
exportToCsv
};
// Helpers --------------------------------------------------------------------------------------
function validateRequiredFields({ headerLabels, rows, fileNamePrefix }) {
const errors = [];
// Validate headerLabels
if (!Array.isArray(headerLabels)) {
errors.push('headerLabels must be a array');
} else if (headerLabels.length) {
errors.push('headerLabels cannot be empty');
}
// Validate rows
if (!Array.isArray(rows)) {
errors.push('rows must be a array');
} else if (rows.length) {
errors.push('rows cannot be empty');
}
// Validate fileNamePrefix
if (typeof fileNamePrefix !== 'string') {
errors.push('fileNamePrefix must be a string');
} else if (!fileNamePrefix.trim()) {
errors.push('fileNamePrefix cannot be empty');
}
if (errors.length > 0) {
throw new Error(`Validation failed: ${errors.join('; ')}.`);
}
}
function getDateTimeString() {
const date = new Date();
const year = date.getFullYear();
const month = `${date.getMonth() + 1}`.padStart(2, '0');
const seconds = date.getSeconds();
const minutes = date.getMinutes();
const hour = date.getHours();
const day =`${date.getDate()}`.padStart(2, '0');
return `${month}.${day}.${year}-${hour}:${minutes}:${seconds}`;
}
});
- Expose the functions for application extensions
Open the generatedfunctions.jsonfile (it appears after creating functions).
Set “Access for Application Extensions” to Enabled.
- Define which functions are exported/public
In the samefunctions.json(or via UI if available in your VB version), ensure the desired functions are listed under modules and marked as accessible.
You can add multiple files/modules as needed (e.g.,format.js,validate.js). - Deploy the Shared AppUI
Deploy it as you would any other AppUI/extension (to Development / Stage / Production VB instance). - Consume the shared function in another AppUI
Open the target AppUI project.
Go to Dependencies → Add Dependency → select your deployed Shared AppUI.
- Use it in your page / Redwood component
For example, in a Redwood Collection’s export button:
Bind the onAction → call your action chain that invokes the sharedexportCSVfunction, passing headers and data from variables.
Benefits
- Single source of truth — fix/improve the function once
- Consistent behavior across all AppUIs
- Smaller AppUI repositories (less duplicated code)
- Easier unit testing of shared logic
This pattern scales very well for medium/large Oracle Fusion extension projects with multiple AppUIs. Start small with 2-3 common utilities and expand the Shared module over time.
Have you already implemented shared JavaScript in Visual Builder? What kind of helpers do you find most useful to share?