Has this ever happened to you?

You’ve built up a beautiful set of pages, taking your user through a flow that the business mapped out for you after really thinking everything through. Just kidding, most likely they handed over a napkin with some scribbles on it, and said: This is what we want.

You go away, put your blood, sweat and tears into it, and finally present your masterpiece, complete with lovely APEX Wizard Progress List. They love it.

They love it, but have one small request. One. Tiny. Thing.

Let’s please move the 4th page over to the 6th, 6th becomes 4th. And actually, page 5? I don’t think we actually need it anymore. And come to think of it, move this page here and this one here. It’s just switching up the order a bit, that’s a quick fix, right?

And your mind immediately goes to all the NEXT and PREVIOUS buttons you’ve set up in your flow, your page branches, that you now have to go basically re-do entirely. Not to mention re-ordering the Wizard List you’ve created for your flow.

Let’s just say this happening to me once on a project a few years ago made me think THERE MUST BE A BETTER WAY. Because you know me, I am all about doing things faster.

So ever since then, I never go in to an APEX wizard development without making sure we are set up to make this fully flexible and dynamic.

Don’t fret! You’ll be able to download our demo code and app at the bottom of this post

Global Application Items

The first thing you’ll need to do is create 3 global application items: G_NEXT_PAGE, G_PREVIOUS_PAGE and G_WIZARD_CODE

Create Global Application Items

Table to support dynamic APEX Wizard

This is where the magic happens. Because we’ll store our navigation logic in a database table, we’ll be able to modify our navigation on the fly!

WIZARD_CODE: This is simply an identifier for your wizard. We include this because eventually, your application might include multiple wizards, and we need to know which pages belong to each wizard or workflow.

SEQ_NO and APP_PAGE_ID: These columns help us identify what position (sequence) each page occupies in the flow.

LABEL: This will be the label or identifier of this particular step in the process. It will appear in your Wizard List.

Dynamic Navigation Table

Initial data might look this, for a wizard that is supposed to take the user from page 1, to 2, to 3, to 4, to 5 and finally 6.

Sample data for dynamic navigation

Please note that I have learned from experience to allow ‘padding’ in my numbering, hence the gap of 10 each time. You just know that the stakeholders will not only want to change the order around, but also add and remove pages. Trust me on this one.

Package to support dynamic navigation

We now need to create a package to support our dynamic navigation. At it’s simplest, we need functions to return both NEXT_PAGE and PREVIOUS_PAGE every time our users are on a page included in a workflow.

Our package spec, in its simplest form, looks like this:


FUNCTION get_next_page (p_app_page_id in number default v(‘APP_PAGE_ID’),
p_app_id in number default v(‘APP_ID’),
p_wizard in varchar2)

FUNCTION get_previous_page (p_app_page_id in number default v(‘APP_PAGE_ID’),
p_app_id in number default v(‘APP_ID’),
p_wizard in varchar2)


Basically, when passing in an app_page_id and wizard_code to my functions, I can determine both next and previous pages by using the SEQ_NO in my table.

Application Process

The next step is to create an application process that will populate our global application items G_NEXT_PAGE and G_PREVIOUS_PAGE.

We create the following Before Header Application Process:

Application Process

We also include a condition on this, because we do not want it to execute for every page, but rather for any pages that, according to our WIZARD_NAVIGATION table, are part of the wizard:

Application Process Condition

Page Branches and Buttons

Every one of our pages involved in this flow should include NEXT and PREVIOUS buttons, that submit our page and then redirect the user to either G_NEXT_PAGE or G_PREVIOUS_PAGE, accordingly. Here, as an example, is our ‘NEXT PAGE’ branch.

Page Branches

By using our global applications items :G_NEXT_PAGE and :G_PREVIOUS_PAGE, that get recalculated on every page load, we are significantly reducing time spent, and making our APEX Wizard nice and flexible.

Don’t forget to put conditions on your NEXT and PREVIOUS buttons. Our 2 functions are designed to return -1 if a PREVIOUS or NEXT page is not found (ie: if we are sitting on the first or last page in the wizard).

We therefore simply use: nvl(:G_NEXT_PAGE,-1)!=-1 or nvl(:G_PREVIOUS_PAGE,-1)!=-1 as conditions on each of our buttons.

Dynamic List and List Region

We also need to create a Dynamic List in Shared Components. We simply called ours Dynamic Wizard, and we can use it in any of the APEX Wizards we create in our application.

It looks like this:

Dynamic List Query

Super simple, right?

Add the wizard list to your pages

Finally, we need to add our Dynamic Wizard list as a Wizard Progress region to each of the pages in our wizard.

Wizard ContainerWizard Progress

Don’t forget you can use the ‘Copy to Other Page’ functionality in Page Designer to do this faster!

One last missing piece is actually setting the value of :G_WIZARD_CODE. We did this simply in a before header page process of our first page, but you can use your own logic or set things through a link, etc.

Ok, now you’re ready to have some fun! Run your page, and you should already be in pretty good shape at this point:

Ok, pretty cool, right? You show your boss how you pretty quickly managed to set up her flow of 4 pages, and she’s WELL impressed.

But BOOM, she asks you to add another page and swap a couple of others around.

Now, far be it from me to lead you down the naughty path, but personally I might be tempted to tell her you need a couple of hours to do this, and go check out the latest episode of Game of Thrones. Winter is coming, people. Because in reality, now that we’ve set things up so beautifully, this is all you need to do:

How cool is that? Did you notice how the sequence changed in the wizard, our Step 4 now has has a NEXT button (when it did not before), and we have a new Step 6? Without a NEXT button? We changed nothing in our pages (except for actually creating the new PAGE 6, of course), but all we did was changed the settings in our table. Magic!

In my next post, I’ll talk about how we’ve made this even more dynamic to accommodate different conditions.

Happy APEX’ing, friends!


Download the sample app and code from this post here.

People photo created by yanalya – www.freepik.com