Acumatica · Modernui

Acumatica Modern UI — Adding Custom Screens

A step-by-step walk through adding a new custom screen to the Acumatica Modern UI — DAC, graph, screen editor, workspace placement, and the Mobile Framework details.

John Kihiu12 min read

Adding a custom screen used to mean generating an ASPX page, wrestling the classic screen editor, and shipping the page file in your customization project. In the Modern UI (the Aurelia-based front end Acumatica has been rolling out since 2023 R1), a custom screen is a TypeScript class plus an HTML template, compiled against the client controls library. The server side — DACs, graph, attributes — is completely unchanged. If you know how to build a graph, you already know 80% of building a Modern UI screen; the remaining 20% is a front-end toolchain that deserves a clear-eyed walkthrough.

The toolchain

Modern UI screens are developed against the FrontendSources folder shipped with the Acumatica site (from roughly 2023 R1 onward). Inside it, the screen workspace contains the client controls packages and a build setup (npm + webpack). One-time setup: install Node.js (match the version the release notes specify), run npm ci in the screen workspace, and you get a dev build with watch mode that recompiles your screen as you save. The compiled output is served by the site; for deployment, the build artifacts ship in the customization project.

The server side is unchanged

Start exactly as you always did — a DAC and a graph:

C#
public class RMAEntry : PXGraph<RMAEntry, KNRMA>
{
    public SelectFrom<KNRMA>.View Document;
    public SelectFrom<KNRMALine>
        .Where<KNRMALine.rmaNbr.IsEqual<KNRMA.rmaNbr.FromCurrent>>
        .View Lines;

    public PXAction<KNRMA> approve;
    [PXButton(CommitChanges = true)]
    [PXUIField(DisplayName = "Approve")]
    protected virtual IEnumerable Approve(PXAdapter adapter)
    {
        // business logic here
        return adapter.Get();
    }
}

Attributes still drive metadata: PXUIField display names, selectors, defaults, and workflow all behave identically. The Modern UI reads this metadata the same way ASPX did — which is why the front-end files end up so thin.

The TypeScript screen class

A screen is a class decorated with graphInfo, declaring views as typed properties:

TypeScript
import {
  PXScreen, PXView, PXFieldState, PXActionState,
  createSingle, createCollection, graphInfo, gridConfig
} from 'client-controls';

@graphInfo({ graphType: 'KN.RMA.RMAEntry', primaryView: 'Document' })
export class KN301000 extends PXScreen {
  Approve: PXActionState;

  Document = createSingle(RMA);
  Lines = createCollection(RMALine);
}

export class RMA extends PXView {
  RMANbr: PXFieldState;
  CustomerID: PXFieldState;
  Status: PXFieldState;
  Description: PXFieldState;
}

@gridConfig({ syncPosition: true, initNewRow: true })
export class RMALine extends PXView {
  InventoryID: PXFieldState;
  Qty: PXFieldState;
  ReasonCode: PXFieldState;
}

Everything here mirrors the graph: view names match, field properties match DAC fields. You're declaring which parts of the server contract this screen uses, not re-describing behavior.

The HTML template

The template places views into layout primitives — qp-fieldset for forms, qp-grid for grids, template containers for the classic form-plus-tab arrangement:

HTML
<template>
  <qp-fieldset view.bind="Document">
    <field name="RMANbr"></field>
    <field name="CustomerID"></field>
    <field name="Status"></field>
    <field name="Description"></field>
  </qp-fieldset>
  <qp-grid view.bind="Lines" style="height: 100%"></qp-grid>
</template>

Screen ID conventions still apply — the files live under a folder named for the screen ID (KN301000 here), and the site map entry points at it. After a build, register the screen in the site map and it renders with the standard toolbar, the Approve action appearing automatically because the graph declares it.

Let the server do the thinking

The strongest habit from classic development carries over: visibility, enabling, defaulting and validation belong in event handlers and attributes on the server. The TypeScript layer should stay declarative. Every piece of logic you sneak into the client is logic the mobile app and API won't enforce.

Honest gotchas

Wrapping up

Custom Modern UI screens split cleanly: the server side is the Acumatica you already know, and the client side is a thin, declarative TypeScript/HTML pair compiled by a standard front-end toolchain. Keep logic on the server, treat the build artifacts as first-class deployment items, and start with a simple master-detail screen to bed in the toolchain before attempting anything clever. The learning curve is front-loaded in tooling, not concepts.

John Kihiu
Acumatica ERP Developer · Laravel Engineer

Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.