You just spent three days trying to make a native APEX item do something it clearly was not designed for. You are Googling, checking forums, and every answer ends with “you could use a plugin for that.” But where do you even start?
Most plugin tutorials skip the fundamentals. They throw you into code you do not understand. They assume you already know what a callback function is or how APEX passes parameters to your PL/SQL procedure.
This guide walks you through building your first Oracle APEX plugin from absolute scratch. No assumptions. No skipped steps. You will understand the plugin architecture, learn what each file does, and build a working item plugin that you can actually use in production.
By the end of this article, you will know exactly how to extend APEX when built-in features are not enough.
What Exactly Is an Oracle APEX Plugin?
An Oracle APEX plugin is a packaged extension that adds functionality not available in standard APEX components. Think of it as a self-contained module you build once and reuse everywhere.
Plugins live in Shared Components. You configure them declaratively. You can export them as SQL scripts and share them across applications, workspaces, or even with the entire Oracle community.
Why Plugins Exist (When Built-In Features Are Not Enough)
APEX gives you a solid foundation. Interactive grids, dynamic actions, charts, forms. But sometimes you hit a wall.
Maybe you need a signature capture field. Maybe you need coordinate-aware image zooming. Maybe you need a custom date picker that handles Islamic calendar dates. Native APEX does not support these out of the box.
You have two options. Write page-level JavaScript and PL/SQL that you copy-paste into every application. Or build a plugin once and make it reusable.
According to Jeff Kemp’s plugin guide, plugins offer a write-once approach that makes them atomic, loosely coupled, and easier to document. They integrate directly into the APEX Builder interface with help text, configuration attributes, and version control support.
When I built the BioSig Pro signature plugin, I needed digital signatures in an enterprise app. Native APEX had nothing. I could have written custom JavaScript on every page. Instead, I built a plugin. Now any developer in my organization can drag it onto a page and configure it in 30 seconds.
The 8 Types of APEX Plugins You Can Build
APEX supports eight distinct plugin types. Each one extends a different part of the platform.
Item plugins create custom form fields. These replace or enhance standard items like text fields, select lists, or date pickers. Think sliders, color pickers, or the signature capture fields in BioSig Pro.
Region plugins render entire page sections with custom logic. I built an image zoom plugin with coordinate-aware panning as a region plugin because users needed to interact with an entire image container, not just a single form field.
Dynamic Action plugins extend APEX’s declarative event handling. Instead of writing jQuery on every page, you package the behavior into a reusable plugin that developers configure through the Builder.
Process plugins create reusable code blocks that run during page processing. Think API calls, batch operations, or file uploads that you need across multiple applications.
Authentication Scheme plugins implement custom login mechanisms. LDAP integration, SAML single sign-on, or proprietary authentication systems.
Authorization Scheme plugins control page and component access. You might need role-based access that checks an external API instead of database tables.
REST Data Source plugins let you implement custom REST connectors when APEX’s standard REST integration does not handle your specific API authentication or response format.
Web Source plugins (introduced in APEX 23.1) extend how APEX consumes external data sources beyond basic REST endpoints.
For a comprehensive breakdown of plugin types, the Rittman Mead team covers each one with use cases and architecture considerations.
How Plugins Differ from Page-Level JavaScript
Page-level JavaScript runs every time APEX renders that specific page. You write it in the page attributes. It stays tied to that one page in that one application.
Plugins package your code into a Shared Component. Other developers configure them declaratively. They do not need to understand your JavaScript or PL/SQL. They just set attributes in the Builder.
Page-level code also lacks versioning. If you improve your script, you manually update every page. Plugins version automatically. You update the plugin once, and subscribed pages pull the latest version.
Understanding Plugin Architecture Before You Build
Every Oracle APEX plugin has three core pieces. PL/SQL callback functions. JavaScript files. CSS files. APEX orchestrates how these pieces work together.
The Three Core Components (PL/SQL Callback, JavaScript Files, CSS Files)
PL/SQL callback functions execute on the server during page rendering. APEX calls your render function and expects you to return HTML, set session state, or configure JavaScript callbacks. For item plugins, you might also implement a validation function or a meta_data function.
JavaScript files handle client-side behavior. User interactions. DOM manipulation. AJAX calls back to your PL/SQL. You attach these files to the plugin definition, and APEX automatically includes them when the page loads.
CSS files style your plugin’s visual output. Custom colors, layouts, animations. Again, you attach them to the plugin definition and APEX injects them into the page head automatically.
Not every plugin needs all three. A simple process plugin might only have PL/SQL. A UI-heavy region plugin needs all three working in sync.
How APEX Calls Your Plugin (The Rendering Flow)
When APEX renders a page containing your plugin, it follows a specific sequence.
First, APEX calls your render function during page generation. APEX passes a p_item (for item plugins) or p_region (for region plugins) record containing all the configuration attributes the developer set in the Builder.
Your render function generates HTML. It writes directly to the page buffer using htp.p() or sys.htp.prn(). You can also set a javascript_function attribute that tells APEX to call a specific JavaScript function after the page loads.
Second, APEX includes your attached JavaScript and CSS files in the page head. The browser downloads them and executes your client-side logic.
Third, if the user interacts with your plugin and triggers an AJAX callback, APEX calls your ajax function (if you defined one). This lets you fetch data, validate input, or update session state without a full page refresh.
According to Oracle’s official plugin documentation, understanding this render-ajax cycle is critical. Your PL/SQL and JavaScript need to communicate through the apex_plugin API properly or the plugin breaks in production.
Plugin Attributes vs. Application Items
Plugin attributes are configuration options developers set when they use your plugin. Think of them as parameters.
Standard attributes are defined by APEX. For item plugins, this includes Label, Help Text, Width, and Read Only settings. You get these automatically.
Custom attributes are ones you define. You can add up to 15 application-level attributes (set once per app) and 25 component-level attributes (set per usage). These appear in the Builder interface when developers configure your plugin.
For example, BioSig Pro has custom attributes for pen color, line width, and whether to show a clear button. Developers set these values declaratively without touching code.
Application items are session-level variables that persist across pages. Plugins do not replace them. You might use plugin attributes to configure behavior and application items to store the plugin’s output values.
Building Your First Item Plugin Step by Step
Let’s build a simple item plugin. A Text Length Counter that shows remaining characters as users type. It will display “45 characters remaining” below a text field and update in real-time.
This example is simple enough to understand but complete enough to deploy in production.
Setting Up the Plugin Definition in Shared Components
Navigate to Shared Components in your APEX application. Under User Interface, click Plug-ins. Click Create.
Give your plugin a name: Text Length Counter. Set the internal name to com.yourcompany.text_length_counter. Oracle recommends namespacing internal names with a reverse domain to avoid conflicts when sharing plugins.
Set the Type to Item. This tells APEX you are building a form field plugin.
Under Source, you will define your PL/SQL callback functions. Leave this blank for now. We will write the render function next.
Under Standard Attributes, enable Is Visible and Has Element. These tell APEX your plugin renders a visible HTML element on the page.
Under Custom Attributes, click Add Attribute. Create a component-level attribute called Max Length with the type Integer. Set a default value of 500. This lets developers configure the maximum allowed characters.
Writing the PL/SQL Render Function
In the Callbacks section of your plugin definition, create a function for Render Function Name. Set it to render_text_length_counter.
Now write the actual PL/SQL. You can write it inline in the plugin definition or create a package and reference it. For this example, inline is fine.
function render_text_length_counter (
p_item in apex_plugin.t_item,
p_plugin in apex_plugin.t_plugin,
p_param in apex_plugin.t_item_render_param,
p_result in out nocopy apex_plugin.t_item_render_result
)
return apex_plugin.t_item_render_result
is
l_max_length number := nvl(p_item.attribute_01, 500);
l_element_id varchar2(255) := apex_plugin.get_input_name_for_page_item(p_is_multi_value => false);
begin
-- Render the input field
sys.htp.prn('<input type="text" id="' || l_element_id || '" name="' || l_element_id || '" ');
sys.htp.prn('maxlength="' || l_max_length || '" ');
sys.htp.prn('value="' || apex_escape.html(p_param.value) || '" />');
-- Render the counter display
sys.htp.prn('<div class="text-counter" id="' || l_element_id || '_counter">');
sys.htp.prn(l_max_length || ' characters remaining</div>');
-- Tell APEX to call our JavaScript initialization
p_result.is_navigable := true;
p_result.javascript_function := 'initTextCounter';
return p_result;
end render_text_length_counter;
This code does four things. It reads the custom Max Length attribute from p_item.attribute_01. It generates an HTML input field with the current value. It creates a div to display the character count. And it tells APEX to call a JavaScript function named initTextCounter after the page loads.
One common mistake junior developers make here: not handling NULL values in attributes. If the developer does not set Max Length, p_item.attribute_01 returns NULL and your plugin breaks. Always use nvl() or coalesce() with sensible defaults.
For more on avoiding common PL/SQL mistakes junior developers make, I wrote a detailed breakdown of the errors that cause production bugs.
Adding Client-Side JavaScript for Interactivity
Your PL/SQL generated the HTML. Now you need JavaScript to make the counter update as users type.
In the Files section of your plugin definition, click Upload File. Create a new JavaScript file called text-counter.js and upload it.
function initTextCounter() {
// Find all instances of this plugin on the page
$('.text-counter').each(function() {
var counterId = $(this).attr('id');
var inputId = counterId.replace('_counter', '');
var input = $('#' + inputId);
var counter = $(this);
var maxLength = parseInt(input.attr('maxlength'));
// Update counter on input
input.on('input', function() {
var remaining = maxLength - $(this).val().length;
counter.text(remaining + ' characters remaining');
});
// Set initial count
var remaining = maxLength - input.val().length;
counter.text(remaining + ' characters remaining');
});
}
This JavaScript runs after APEX renders the page. It finds every instance of your plugin, attaches an event listener to the input field, and updates the counter display as users type.
When I built BioSig Pro, the signature capture plugin, I initially tried to handle touch events the same way as mouse events. That broke completely on mobile devices. The fix required detecting touch vs mouse input and normalizing the coordinates differently for each. The lesson: always test your JavaScript on mobile Safari and Chrome, not just desktop browsers.
Defining Custom Attributes Users Can Configure
You already created one custom attribute (Max Length). Let’s add two more to show you how flexible this system is.
Add a component-level attribute called Show Counter with type Yes/No. Default to Yes. This lets developers hide the counter if they only want length validation without the visual display.
Add another attribute called Counter Color with type Color Picker. Default to #666666. This lets developers match the counter text to their application theme.
Now update your render function to use these attributes:
l_show_counter varchar2(1) := nvl(p_item.attribute_02, 'Y');
l_counter_color varchar2(50) := nvl(p_item.attribute_03, '#666666');
-- Only render counter if enabled
if l_show_counter = 'Y' then
sys.htp.prn('<div class="text-counter" id="' || l_element_id || '_counter" ');
sys.htp.prn('style="color: ' || l_counter_color || ';">');
sys.htp.prn(l_max_length || ' characters remaining</div>');
end if;
The Builder automatically generates a configuration UI for these attributes. Developers see labeled fields, color pickers, and yes/no toggles. They never touch your PL/SQL code.
What Are the Most Common Plugin Development Mistakes?
I have built multiple production plugins. BioSig Pro. Image Zoom. Custom validation frameworks. Every time, I hit the same mistakes early in development.
Forgetting to Return the Correct Result Type
Each plugin type expects a specific return type from its callback functions. Item plugins return apex_plugin.t_item_render_result. Region plugins return apex_plugin.t_region_render_result. Dynamic actions return apex_plugin.t_dynamic_action_render_result.
If you return the wrong type, APEX throws a compilation error that is not immediately obvious. The error message says “PL/SQL function returned an incorrect value” without telling you what value it expected.
Check the dynamic action plugin callbacks documentation from Rittman Mead. They break down the exact return structure for each plugin type.
Not Handling NULL Values in Attributes
Custom attributes can be NULL if the developer does not set them. If your PL/SQL assumes p_item.attribute_01 always has a value, your plugin crashes in production.
Always wrap attributes in nvl() or coalesce() with sensible defaults:
l_max_length := nvl(to_number(p_item.attribute_01), 500);
Skipping the Ajax Identifier
If your plugin needs AJAX callbacks (most do), you must set the ajax_identifier in your render result:
p_result.ajax_identifier := apex_plugin.get_ajax_identifier;
Without this, your JavaScript cannot call back to your PL/SQL ajax function. APEX will not know which plugin instance triggered the request.
Hardcoding Values Instead of Using Substitution Strings
Never hardcode application IDs, page numbers, or item names in your plugin code. Use APEX substitution strings like &APP_ID. and &APP_PAGE_ID. or reference items dynamically through the plugin API.
Hardcoded values break when you export the plugin to another application. Substitution strings adapt automatically.
How Do You Test and Debug APEX Plugins?
Testing plugins is harder than testing regular APEX pages because you are working with multiple layers. PL/SQL callbacks. JavaScript execution. CSS rendering. AJAX communication.
Using Browser DevTools to Inspect Plugin Output
Open Chrome DevTools (F12) and inspect the HTML your render function generated. Does it match what you expected? Are the CSS classes applied correctly? Is the JavaScript firing on the right events?
In the Console tab, check for JavaScript errors. A typo in your function name will throw Uncaught ReferenceError: initTextCounter is not defined. Fix it in your uploaded JS file and refresh.
In the Network tab, watch AJAX requests when your plugin calls back to the server. Is APEX receiving the correct parameters? Is your ajax function returning valid JSON?
APEX Debug Mode for PL/SQL Callbacks
Enable Debug mode in the APEX Developer Toolbar. Run your page. Check the debug output for your plugin’s render function.
Look for these lines:
...execute plug-in: item="P1_TEXT_COUNTER" type="com.yourcompany.text_length_counter"
If you see errors about invalid return types or missing attributes, your PL/SQL callback has a bug. The debug log shows the exact line that failed.
For production applications, I use a centralized error logging framework that captures plugin errors with full session context and call stacks. APEX_DEBUG stops working when sessions end. A proper error logger persists errors to database tables you can query later.
Testing Across Different APEX Versions
Plugins built for APEX 21.2 might break in APEX 23.1 if Oracle changed the plugin API. Always test your plugin in the earliest APEX version you plan to support.
If you are sharing the plugin publicly, document the minimum APEX version in the help text. Developers will waste hours debugging compatibility issues if you do not warn them.
Making Your Plugin Production-Ready
You built a working plugin. Now make it maintainable.
Adding Help Text and Documentation
In the plugin definition, every custom attribute has a Help Text field. Fill it in. Explain what the attribute does, what values are valid, and what happens if the developer leaves it blank.
For example:
Max Length – Maximum number of characters allowed in the text field. Defaults to 500 if not specified. Setting this to 0 disables length validation.
Under the main plugin settings, add Help Text that explains how to use the plugin, what dependencies it has, and any known limitations.
Good documentation means other developers can use your plugin without asking you questions. It also means you will remember how it works when you revisit the code six months later.
Versioning Your Plugin Properly
Set a Version number in the plugin definition. Use semantic versioning: 1.0.0 for initial release, 1.1.0 for new features, 2.0.0 for breaking changes.
Update the version every time you modify the plugin. This helps you track which applications are running old versions and need updates.
Exporting and Sharing Your Plugin
In Shared Components, click Export next to your plugin. APEX generates a SQL script containing all the plugin code, attributes, and file attachments.
You can import this script into any other APEX application. You can share it on apex.world, the community plugin repository. You can publish it on GitHub.
Before sharing publicly, review your code for hardcoded credentials, database schema names, or internal business logic. Clean it up. Make variable names generic.
When I open-sourced BioSig Pro, I stripped out all references to our internal schema names and replaced them with configurable attributes. That made the plugin usable by anyone without modifications.
For quality assurance, run the APEX Advisor against your application. According to APEX performance best practices, Advisor catches programming errors, security vulnerabilities, and usability issues automatically. Fix any warnings before you export.
Start Building Your Own Plugins
You now know how plugin architecture works. You understand how APEX calls your PL/SQL render function and passes control to your JavaScript. You know the difference between custom attributes and application items. You have seen a complete working example of an item plugin.
The skills you learned here apply to every plugin type. Region plugins use the same render-ajax pattern. Dynamic action plugins follow the same attribute system. Process plugins still need proper error handling and NULL checks.
The best way to learn is to build. Start simple. Build a plugin that solves one specific problem in your application. Add features incrementally. Test in different browsers. Document your code.
Want to see a real production plugin in action? Check out BioSig Pro, the signature capture plugin I built and open-sourced. Study the code. See how it handles mobile touch events and saves signature data to the database. Use it as a template for your own plugins.
Need a development environment to practice? Set up Oracle Cloud Free Tier APEX environment and start building today. You get a full APEX workspace with no credit card required.
Got questions about plugin development? Drop them in the comments. I read every one.
Hassan Raza
An Oracle ACE Associate and Senior Oracle Application Developer at S&H Software Solution. I am specialized in Oracle APEX, SQL, and PL/SQL and writes about Oracle development at oraclewithhassan.com