Control Add-in Object in Business Central

What Is a Control Add-in in Business Central?

A control add-in is an AL object you use to embed a custom web-based UI component inside the Business Central client. Think of it as a bridge between the Business Central page framework and HTML/JavaScript/CSS running in the browser client. The client hosts the add-in on a page (typically rendered in an iframe) and loads the JavaScript and CSS packaged with your extension.

The key concept is that Business Central renders the add-in in the client, and you communicate between AL and JavaScript using events (JS → AL) and procedures (AL → JS).

I’ve been having a lot of fun building control add-ins and vibe-coded something fun for the holiday.

You can find the full code for the example on GitHub.

How Control Add-ins Work

When a page that contains a usercontrol is opened, the Business Central web client loads the add-in resources packaged in your extension (JavaScript, CSS, images). The add-in renders into a host container in the page.

From there, the integration is two-way:

  • JavaScript raises events back to AL using Microsoft.Dynamics.NAV.InvokeExtensibilityMethod('EventName', [args]).
  • AL calls JavaScript functions by invoking procedures declared on the controladdin object (which must exist in your JS runtime).

You can think of it as:

  • Events: “JavaScript is telling AL something happened.”
  • Procedures: “AL is telling JavaScript to update the UI.”

Creating a Control Add-in Object

To get started, you’ll typically:

  1. Create a controladdin object in AL.
  2. Add your JS/CSS files to the extension (often under an addin/ or controladdin/ folder).
  3. Reference those files from Scripts, StartupScript, and StyleSheets.
  4. Define events (JS → AL) and procedures (AL → JS).
  5. Place it on a page using a usercontrol.

Here’s the control add-in definition for my holiday example:

controladdin DVLPRControlAddIn
{
    HorizontalShrink = true;
    HorizontalStretch = true;
    MaximumHeight = 300;
    MaximumWidth = 700;
    MinimumHeight = 300;
    MinimumWidth = 700;
    RequestedHeight = 300;
    RequestedWidth = 700;
    Scripts = 'controladdin/scripts.js';
    StartupScript = 'controladdin/start.js';
    StyleSheets = 'controladdin/style.css';
    VerticalShrink = true;
    VerticalStretch = true;

    procedure Animate()
    procedure Render(html: Text);
    event OnControlAddInReady();
    event ShowError(ErrorTxt: Text);
}

A few notes on those properties:

  • StartupScript is typically used to bootstrap the control and indicate the initial trigger to invoke on the page that contains the add-in.
  • Scripts is where you put the bulk of your implementation (functions that AL procedures call, helpers, etc.).
  • StyleSheets is optional, but recommended for maintainability.
  • Sizing properties (RequestedHeight, MinimumHeight, VerticalStretch, etc.) help your add-in behave predictably in pages.

Using the Control Add-in on a Page

Once the controladdin exists, you host it on a page via usercontrol. Below is a simple Card page example that:

  • Receives a JavaScript event when the control is loaded and ready (JS → AL event).
  • Calls JavaScript procedures to render HTML and start an animation (AL → JS procedures).
page 50100 "DVLPR Christmas Tree Page"
{
    ApplicationArea = All;
    Caption = 'Christmas Tree';
    UsageCategory = Lists;

    layout
    {
        area(Content)
        {
            group(controls)
            {
                Caption = 'Merry Christmas!';
                usercontrol(PageControlAddIn; DVLPRControlAddIn)
                {
                    trigger OnControlAddInReady()
                    begin
                        CurrPage.PageControlAddIn.Render(@'
                        <div id="scrolltext">Merry Christmas!</div>
                        <div class="tree">
                            <div class="lights">
                                <div class="light"></div>
                                <div class="light"></div>
                                <div class="light"></div>
                                <div class="light"></div>
                                <div class="light"></div>
                                <div class="stump"></div>
                            </div>
                        </div>');
                        CurrPage.PageControlAddIn.Animate();
                    end;

                    trigger ShowError(ErrorTxt: Text)
                    begin
                        Error(ErrorTxt);
                    end;
                }
            }
        }
    }
}

OnControlAddInReady() is your “safe moment” to start calling procedures into JavaScript, because the client has loaded the resources and the JavaScript runtime is initialized.

Note: In Business Central 2025 Wave 1 and later, you can also use the new UserControlHost page type to host control add-ins in a full-page experience.

Learn more about that here.

JavaScript: Rendering UI and Calling Back into AL

Now for the JavaScript side. The easiest pattern is:

  • In start.js: signal that the add-in is ready.
  • When something happens: call Microsoft.Dynamics.NAV.InvokeExtensibilityMethod(...) with the event name defined in AL. The Business Central client will route that to your AL event handler.

controladdin/start.js

Microsoft.Dynamics.NAV.InvokeExtensibilityMethod('OnControlAddInReady', []);

That InvokeExtensibilityMethod call maps directly to the AL event:

trigger OnControlAddInReady()
begin
end;

So the Business Central client will invoke the trigger OnControlAddInReady() block inside your usercontrol.

JavaScript: Implementing AL Procedures (AL → JS)

If you declare a procedure in the controladdin object, you must implement a matching function in JavaScript so AL can call it.

From the AL object:

    procedure Render(html: Text);

Implement it in a JS file you included under Scripts (for example scripts.js):

controladdin/scripts.js

function Render(html) {
    try {
        document.getElementById('controlAddIn').innerHTML = html;
    }
    catch (e) {
        Microsoft.Dynamics.NAV.InvokeExtensibilityMethod('ShowError', [e.toString()]);
    }
}

Now this AL call will work (it passes HTML to render):

CurrPage.PageControlAddIn.Render(@'
                        <div id="scrolltext">Merry Christmas!</div>
                        <div class="tree">
                            <div class="lights">
                                <div class="light"></div>
                                <div class="light"></div>
                                <div class="light"></div>
                                <div class="light"></div>
                                <div class="light"></div>
                                <div class="stump"></div>
                            </div>
                        </div>');

CSS: Keep It Simple and Contained

A small stylesheet helps keep the markup readable:

controladdin/style.css

body {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    background-color: #000;
    color: #fff;
    font-family: Arial, sans-serif;
}

#scrolltext {
    position: absolute;
    top: 50px;
    left: 50px;
    font-size: 24px;
    overflow-x: hidden;
    white-space: nowrap;
}

.tree {
    position: absolute;
    top: 180px;
    left: 320px;
    width: 40;
    height: 0;
    border-left: 50px solid transparent;
    border-right: 50px solid transparent;
    border-bottom: 100px solid green;
    margin-bottom: -30px;
}

.tree:before {
    content: '';
    position: absolute;
    top: -50px;
    left: -25px;
    width: 0;
    height: 0;
    border-left: 25px solid transparent;
    border-right: 25px solid transparent;
    border-bottom: 50px solid green;
}

.tree:after {
    content: '';
    position: absolute;
    top: -80px;
    left: -15px;
    width: 0;
    height: 0;
    border-left: 15px solid transparent;
    border-right: 15px solid transparent;
    border-bottom: 30px solid green;
}

.stump {
    position: absolute;
    top: 190px;
    left: 5px;
    width: 20px;
    height: 20px;
    background-color: brown;
}

.lights {
    position: absolute;
    top: -90px;
    left: -15px;
    width: 30px;
    height: 170px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    align-items: center;
}

.light {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background-color: red;
    animation: blink 1s infinite;
}

.light:nth-child(2) {
    background-color: yellow;
    animation-delay: 0.2s;
}

.light:nth-child(3) {
    background-color: blue;
    animation-delay: 0.4s;
}

.light:nth-child(4) {
    background-color: white;
    animation-delay: 0.6s;
}

.light:nth-child(5) {
    background-color: orange;
    animation-delay: 0.8s;
}

@keyframes blink {

    0%,
    100% {
        opacity: 1;
    }

    50% {
        opacity: 0.5;
    }
}

(Keep your CSS scoped to your own classes so you don’t accidentally affect the surrounding Business Central page.)

Common Pitfalls (That Everyone Hits Once)

  • Calling procedures before the control is ready: use OnControlAddInReady() for initialization calls.
  • Event name mismatches: the string you pass to InvokeExtensibilityMethod('OnControlAddInReady', ...) (or ShowError) must match the AL event name exactly.
  • Trying to do server work in the add-in: treat it as UI; keep business logic in AL/codeunits.

Wrapping Up

Control add-ins are helpful when you need a richer client experience than standard AL page controls can provide. Once you learn the basic rhythm—declare events/procedures in AL, implement the UI in JavaScript, and connect them with InvokeExtensibilityMethod—you can build surprisingly powerful UI integrations (I’ve even created a few games within Business Central—more on that later) while keeping business logic in AL.

Learn more about the control add-in object here.

You can find the full code for the example on GitHub.

Note: The code and information discussed in this article are for informational and demonstration purposes only. This content was written referencing Microsoft Dynamics 365 Business Central 2025 Wave 2 online.

Permanent link to this article: https://www.dvlprlife.com/2025/12/control-add-in-object-in-business-central/

Leave a Reply

Your email address will not be published.