Improving User Experience (UX) in Oracle APEX
From Functional to Fantastic: Tricks with JavaScript, Dynamic Actions, and Page Items to create modern applications.

Search for a command to run...
From Functional to Fantastic: Tricks with JavaScript, Dynamic Actions, and Page Items to create modern applications.

No comments yet. Be the first to comment.
El patrón para producción que ahora despliego en menos de 30 minutos — y por qué finalmente lo empaqueté.

A production pattern I now deploy in under 30 minutes — and why I finally packaged it.
Cómo resolvimos el caos de WhatsApp en las inmobiliarias mexicanas con componentes nativos de APEX y diseño intencional.

How we transformed a complex real estate workflow into a sleek, high-density productivity tool using native APEX components and intentional design.

Por qué el 'los empleados no tienen opción' es la mayor falacia del software empresarial, y cómo construir herramientas que los usuarios realmente amen.

💼 For IT Managers & Decision Makers
Business Problem: High user error rates and clumsy interfaces lead to customer churn and ballooning support costs; a premium, responsive UX improves user retention and speeds up workflow execution.
Operational Efficiency: Relying on standardized APEX JavaScript APIs and Dynamic Actions instead of custom DOM manipulation reduces application maintenance overhead by 45%.
Developer Action: If you are a developer, skip directly to the code below!
You've mastered the basics (Best Practices) and bulletproofed your app (Security). Now, it's time to go for the WOW factor: the User Experience (UX).
In Oracle APEX, great UX isn't just about "looking pretty"; it's about perceived performance, fluid interactions, and that feeling of using world-class software. In this APEX Insights post, we are going to transform a functional app into an exceptional experience.
To ground these concepts, we will follow a real-world scenario. A logistics client asked us to audit their Sales Invoice Creator screen. The legacy implementation was a massive, single-region form with 20 unorganized fields.
Every field change triggered a database round-trip via individual Dynamic Actions. There were no loading indicators, leading to severe layout shifts, double-submissions, and extreme user frustration. Keyboard navigation was entirely broken, forcing users to repeatedly grasp their mouse just to jump across broken sections.
Here is how we refactored this nightmare into a dynamic, blazing-fast, and accessible 3-step reactive experience using the APEX JavaScript API and optimized Dynamic Actions.
Dynamic Actions (DAs) are the declarative bridge between the Frontend (JavaScript) and the Backend (PL/SQL). However, having 20 separate Dynamic Actions executing on a single page is a recipe for maintenance chaos and sluggish performance.
| Scenario | Recommendation | Reason |
|---|---|---|
| Simple Interactions | Dynamic Actions | Less code, faster declarative setup. |
| Complex Biz Logic | PL/SQL (via DA Server-Side) | Business rules must live in the database. |
| DOM Manipulation / Custom UI | Pure JavaScript (Page Level) | Best for event handling and complex browser UI behaviors. |
In the legacy form, almost half of the 20 fields were hidden or disabled by default under specific conditions (e.g., Shipping Address only showing when Custom Delivery is selected).
Having 10 individual Dynamic Actions with Fire on Initialization = True caused the page to load, render the fields, and then abruptly hide them milliseconds later. This created a jarring layout shift.
Fire on Initialization on individual DAs. Instead, we consolidated the page setup rules into a single JavaScript initialization function configured at the page level and executed on Page Load.// A single consolidated Page Load initialization routine
function initInvoiceForm() {
const isCustomDelivery = $v("P1_DELIVERY_TYPE") === "CUSTOM";
// Declaratively show/hide using APEX API rather than DOM styling
if (isCustomDelivery) {
apex.item("P1_SHIPPING_ADDRESS").show();
} else {
apex.item("P1_SHIPPING_ADDRESS").hide();
}
}
// Executed once on Page Load
initInvoiceForm();
Perceived performance is how fast the user feels the application responds. When our invoice screen had to fetch customer details and current price lists from the server, the interface would freeze for 1.5 seconds. Users frequently double-clicked buttons, generating duplicate invoices.
When running a background PL/SQL process via AJAX, always give the user immediate visual feedback.
// Refactored AJAX Call with target-specific Spinner
const invoiceRegion = "#invoice_details_region";
// Show spinner directly over the affected region
const mySpinner = apex.util.showSpinner($(invoiceRegion));
apex.server.process(
"FETCH_CUSTOMER_DEALS",
{ x01: $v("P1_CUSTOMER_ID") },
{
success: function (pData) {
// Populate fields instantly
$s("P1_DISCOUNT_RATE", pData.discount);
calculateInvoiceTotals();
},
complete: function () {
// Remove the spinner upon completion
mySpinner.remove();
},
},
);
After completing a step or successfully submitting the invoice draft, avoid reloading the entire page. Instead, provide clear, asynchronous feedback.
Use apex.message.showPageSuccess('Invoice draft saved successfully!'); to display a native, floating notification.
Use apex.message.clearErrors(); and apex.message.showErrors() for non-intrusive form validation alerts.
Direct DOM manipulation is the fastest route to high technical debt in Oracle APEX.
document.getElementById and jQuery Selectors for Values⚠️ WARNING: Directly manipulating page item DOM nodes (e.g.,
document.getElementById('P1_TOTAL').value = 100) bypasses APEX's state management. APEX won't register the change, leading to session state mismatch and data loss.
Always use the native APEX JavaScript API wrapper apex.item().
| Task | Legacy / Vulnerable Code | Modern APEX API |
|---|---|---|
| Get Value | document.getElementById('P1_QTY').value |
$v('P1_QTY') |
| Set Value | document.getElementById('P1_TOTAL').value = '10' |
$s('P1_TOTAL', '10') |
| Disable | $('#P1_ITEM').prop('disabled', true) |
apex.item('P1_ITEM').disable() |
| Hide Component | $('#P1_ITEM').hide() |
apex.item('P1_ITEM').hide() |
In our redesigned form, the invoice lines recalculate instantly on the client side as the user types, using standard API shortcuts:
function calculateInvoiceTotals() {
const qty = parseFloat($v("P1_QUANTITY")) || 0;
const price = parseFloat($v("P1_UNIT_PRICE")) || 0;
const discount = parseFloat($v("P1_DISCOUNT_RATE")) || 0;
const subtotal = qty * price;
const total = subtotal * (1 - discount / 100);
// Safely update values using the APEX shortcut
$s("P1_SUBTOTAL", subtotal.toFixed(2));
$s("P1_TOTAL", total.toFixed(2));
}
When building highly dynamic client-side experiences, it is easy to leave keyboard and screen-reader users behind. For example, if you hide a section of a form or dynamically switch between steps, a keyboard user navigating via Tab can easily lose their place or get kicked back to the top of the page.
Always ensure that any client-side layout shift (such as displaying/hiding elements or showing validation banners) programmatically guides the keyboard cursor to the most logical next action.
When a user completes a step or activates a dynamic panel, use the APEX JS API to transfer the keyboard focus seamlessly.
// Programmatically shift focus to the shipping zip input
apex.item("P1_SHIPPING_ZIP").setFocus();
If you are displaying dynamic inline errors or success messages via JavaScript, move the focus to the alert container so it is read immediately by screen readers:
// Focus on the alert region so it is registered by assistive tech
$("#apex_layout_alert").focus();
To learn more about keyboard traps, semantic HTML, and ARIA attributes, explore our comprehensive guide on Accessibility (A11y) in Oracle APEX.
The round-trip (network request to the database) is the biggest enemy of speed and UX. While APEX makes it extremely easy to run processes, it's easy to overuse them.
When resetting our invoice line items, the legacy page executed a PL/SQL process calling APEX_UTIL.CLEAR_PAGE_CACHE which made a full server call just to empty some textboxes.
// Loop through and clear items on the client side instantly
["P1_ITEM_ID", "P1_QUANTITY", "P1_UNIT_PRICE", "P1_TOTAL"].forEach(
function (itemName) {
apex.item(itemName).setValue(null);
},
);
Ready to take your frontend skills to the next level? Explore these articles in the series:
Universal Theme Secrets: Advanced Customization — Dive deep into Theme Roller, CSS custom properties, and UI overrides.
Accessibility (A11y) in Oracle APEX: Building Inclusive Enterprise Applications — Learn how to ensure your highly interactive layouts are fully WCAG compliant.
Enterprise UX Patterns for High-Productivity Internal Apps — Study real-world design blueprints for high-frequency internal tools.
The difference between a "good" and an excellent APEX application lies in the details. By moving away from bloated, uncoordinated Dynamic Actions, implementing target-specific visual feedback, utilizing native JavaScript APIs, managing keyboard focus, and keeping calculations client-side, we transformed a painful 20-field form into a fluid, responsive workflow. Your users don't just want functional screens—they want experiences they love to use.
If your users constantly complain that your APEX application feels like an outdated spreadsheet, you don't need to rewrite the entire system; you need a UX intervention.
Stop building static tables. Start building high-performance experiences.
Which of these UX practices has made the biggest difference in your enterprise apps? Share your feedback in the comments!
Is a clunky, sluggish interface slowing down your business workflows or frustrating your users? I help enterprise teams design premium, highly reactive, and accessible Oracle APEX applications.
Productized Service: 🎨 Schedule a 30-minute call. Show me the slowest or most confusing screen in your APEX application and I will deliver a flow redesign (mockup) guaranteeing frictionless navigation.
Direct Alignment: Connect on LinkedIn to discuss frontend and UX best practices.