VS Code Extension
Developing an abap2UI5 app the plain way means three windows: the editor with the class, a browser with the app, and the activation step between them. Change a line, save, activate, switch to the browser, reload, look, switch back.
The abap2UI5 extension for VS Code collapses that into one window. F9 launches the app next to the source, Ctrl+F3 activates the class and reloads the preview, and the whole linter runs in the editor while you type. It works with any system running abap2UI5, on-premise or cloud — the only thing tying it to a system is the launch URL you configure once.
Installing
Install abap2UI5 from the VS Code Marketplace: Extensions panel (Ctrl/Cmd + Shift + X) → search for abap2UI5 → Install. From the terminal:
code --install-extension abap2ui5.abap2ui5It is on Open VSX as well, for VSCodium, Eclipse Theia and SAP Business Application Studio. Without Marketplace access, every release carries the .vsix for Install from VSIX….
For the parts that talk to a system you also need an ABAP connection — the ABAP remote filesystem extension is what opens ABAP objects from the system and what the activation step hands over to.
Running the app: F9
Put the cursor in a class implementing z2ui5_if_app and press F9. The app opens in an embedded browser beside the source, and the cursor goes back to where it was — even when the loading app tries to grab focus. In a class that is not an abap2UI5 app, F9 keeps its usual meaning (toggle breakpoint), so you do not lose the key.
On the first F9 the extension asks for the launch URL, with {class} as the placeholder:
https://host:44300/sap/bc/z2ui5?app_start={class}&sap-client=100Working against several systems is the normal case, so name them instead:
"abap2ui5.systems": [
{ "name": "DEV", "url": "https://dev:44300/sap/bc/z2ui5?app_start={class}&sap-client=100" },
{ "name": "Sandbox", "url": "https://box:44300/sap/bc/z2ui5?app_start={class}" }
]"abap2UI5: Select System" switches between them and can add one, so you never have to find the JSON. The active system is remembered per window — two windows can work against two systems at once — and credentials are stored per host, so switching back and forth does not ask again.
Where the app opens is abap2ui5.openMode: tab (default, an editor tab next to the code), panel (down with Terminal and Output) or external (your normal browser, reusing your existing SAP session). Two commands move a running app between tab and panel without restarting it.
Why there is a local proxy
An embedded iframe has no SAP session, so a direct call would end in a 401. In tab and panel mode the extension therefore starts a small auth proxy on 127.0.0.1: it asks once for your SAP user and password (the same ones you use in ADT, kept in VS Code's SecretStorage), attaches them to every request, and rewrites Origin and Referer so origin-validating CSRF checks accept the app's POSTs. To make embedding possible at all it strips X-Frame-Options and the frame-ancestors CSP directive from the responses, and it accepts self-signed certificates.
Basic auth required
The system must accept basic auth. Pure SSO/SAML without a basic-auth fallback is not supported — use external mode there. "abap2UI5: Clear Stored SAP Credentials" forgets the password again.
Reload on activation, not on save
Saving an ABAP class does not change what the server runs — only activation does. So the preview reloads on activation:
abap2ui5.reloadOn | Behaviour |
|---|---|
activation (default) | Ctrl+F3 saves, activates through your ABAP tooling and reloads. Activations done any other way are noticed on the server and reload too. A plain save only marks the preview not activated |
save | Reload on every save — for setups where saving already publishes the change |
never | Only F9, the reload button or the status bar |
Activating from somewhere else works as well: while the preview shows the not activated badge, the extension watches the class's ADT metadata on the server and reloads as soon as it is active again — whether you used Ctrl+F3, the ABAP remote filesystem's own button, or even Eclipse.
What the preview gives you beyond a browser tab
- Device widths, theme and language — desktop, tablet (834px) and phone (414px), UI5 themes and logon languages, to check a responsive app without leaving the editor.
- Runtime errors land in the editor. A thrown error, a failed assertion or a rejected promise inside an iframe is normally visible only in browser devtools — exactly the context switch the preview exists to avoid. They are forwarded to the abap2UI5 output channel and counted in a toolbar badge. When the text names a binding path or identifier that appears in the class, the log adds the file and line under it.
- Inspect (🎯) — a one-shot element picker like the one in devtools: the hovered control is outlined, and a click jumps to the
ele( )/tag( )call in the class that wrote it. A row inside a bound list lands on its template; anidwritten in the class settles the match outright. - Model (
{ }) — the running app's JSON model as a document beside the code: live values next to the statically known shape. - Traffic log with roundtrip timings — the proxy sees every request, which makes it a free network tab. Every abap2UI5 event is one POST, and the toolbar shows the last one's duration as a badge. "Is the backend slow or the UI?" stops being a devtools trip.
- Take App Screenshot (📷) — the running app as a PNG for a bug report, rendered headless through the same proxy.
- Stateful reload (📌) — a reload is normally a fresh start, and the three clicks that reproduced the bug have to be clicked again on every activation. With the pin on, the model is captured before the reload and the class's own paths are restored into the fresh page.
Checking views while you type
The extension runs the abap2UI5 linter in the editor, so the findings land in the Problems panel before the app ever reaches a system:
- The property gate is bundled, needs no setup and runs while you type, shortly after each pause. It resolves every control and property against the UI5 metadata snapshot: a control that does not exist is an error, anything newer than your floor (
abap2ui5.viewCheck.minUi5, default 1.71) or deprecated is a warning. - The abap2UI5 rules come with it — the defects that stay silent at runtime, from a hand-written binding path the model does not have to an ABAP boolean written straight into the view.
- The render gate is the expensive one and stays on save and on demand. Install it once with "abap2UI5: Install Render Gate": the command downloads a self-contained checker bundle and Chromium into the extension's storage and runs everything with VS Code's own runtime — no node, npm or PATH setup on the machine.
- The system can answer the settings. After the first F9 the extension reads the system's
sap-ui-version.jsonand offers, once per system, to alignviewCheck.minUi5and.distributionwith what the system actually runs. The detected version stays visible in the status bar.
Quick fixes. Every finding whose correction is mechanical carries it, and the lightbulb offers it — plus fix all in this file, as a command, as a CodeLens above the class, as Fix All View Findings in the Workspace in one undo step, and as source.fixAll.abap2ui5 for editor.codeActionsOnSave. The other quick fix on any finding is suppress on this line, which writes the linter's own directive — so waiving something here waives it in CI too, and a line waived in CI no longer squiggles here.
The repository config wins. An abap2ui5lint.jsonc in the repo is what the CLI and the Action check against, so it overrides the VS Code settings wherever it says something; the settings fill in the rest. The output channel names the file the current values came from — the first place to look when the editor and CI disagree. The config file itself is schema-checked as you edit it, and a baseline it names is applied here as well, so the Problems panel shows only what is new.
The findings view. The Problems panel answers what is wrong in this file. The abap2UI5 Findings view in the Explorer answers the other question — what is wrong in this repository — by grouping the same findings under the rule that produced them, worst first. Twelve unknown-binding-path across three classes are one decision; per file they look like twelve unrelated problems.
Writing the view
The extension knows the whole UI5 API and the model your class derives, so it can help while the chain is being written rather than after it:
- Completion for control names in
ele( )/tag( )(resolved through the namespace in play), for the members of exactly that control in thea( )chained to it, and for the values an enum property accepts. - Binding paths complete too. Typing
{offers the paths the derived model actually has — the same model theunknown-binding-pathrule checks against, so what is offered is exactly what will not squiggle afterwards. Inside an aggregation template the fields of the bound row come first. client->completes and explains itself — everyz2ui5_if_clientmethod, with the full ABAP signature and documentation on hover.- Hover adds a member's type, the UI5 version it appeared in, its deprecation and a link to the UI5 API reference. On a binding path it says what the model resolves it to — a field, a structure, a table, or missing.
- Format Document repairs a builder chain: a child one step under its parent, an attribute one step under its element, an
end( )in the column of theele( )it closes. Deliberately conservative — only builder-verb lines inside a chain are touched. - Emmet for chains —
Page>content>Button*3, then "Expand Abbreviation to a Chain", and the chain that builds it is written in the house layout.#id,[attr=value]and{text}work as in Emmet. - Extract to View Method — a real screen is one very long statement. Put the cursor on the
)->ele( )that should start a new method, name it, and the tail becomes a helper method taking the builder handle, with the declaration written into the class and the call left behind. It extracts a tail and refuses rather than guesses. - Convert XML View to Builder Chain — paste a UI5 demo kit sample (or any view XML) and get the builder chain out, in the corpus style. What the builder cannot express is listed as
TODOcomments rather than dropped silently. Porting a sample starts with paste instead of transcription. - Snippets for the everyday shapes:
z2ui5app,z2ui5main,z2ui5ele,z2ui5button,z2ui5table,z2ui5event,z2ui5popup,z2ui5navtoand more. - Inline annotations — the finding at the end of its line, the UI5
@sinceof what you are writing (warned when it is above your floor), and what aPUBLICattribute costs per roundtrip. Each can be switched off.
Seeing the view and the structure
- Show Reconstructed XML View opens the XML the builder calls actually produce, live beside the class and following the editor the way the Markdown preview does. The findings are mirrored onto the XML lines they concern, and Go to Definition on any line jumps back to the builder call that produced it.
- Preview View (No System) renders the view and shows the picture — no system, no activation, no launch URL. It is the render gate turned around: the view is kept standing and photographed instead of thrown away. A
<class>.mock.jsonnext to the source fills the tables aSELECTwould fill, and the caption always says which model was used. A comma-separated viewport list renders a device matrix in one browser session, and Compare with HEAD puts the committed version beside the working tree — answering what no linter can: did my change do what I meant to the view? - Outline and navigation — the
ele( )/tag( )hierarchy as a tree in the Outline pane; Go to Definition betweenclient->_event( 'GO' )and theWHEN 'GO'that handles it, in both directions; and on a binding path, to theTYPESfield that declares it. - F2 renames the strings an app is wired together with. An abap2UI5 app ties its two halves together with literals and nothing connects the ends, so renaming one is normally a grep and missing one is silent. F2 takes an event, a control id (with every wire addressing it) or a bound attribute (
mv_titleand{/MV_TITLE}together) all at once. Position decides what a literal is, never its text. - App navigation map — every
z2ui5_if_appclass in the workspace and eachnav_app_call( )between them, as a clickable graph. - The apps of this workspace — the Explorer view listing every app class with run, preview and check on it: the list that says which thirty apps a repository has.
- Show Examples for this Control — put the cursor on an
ele( )call and the sample catalogues are searched for working uses of that control, richest first, opening at the line. It reads the catalogues fromabap2ui5.mcp.reposRoot, so it needs those checkouts.
Starting from a template
"New App from Template" is a gallery rather than one skeleton — empty view, list, form, master & detail, popup — and every template ships linter-clean. "New Project from Template" puts the same into an empty folder together with everything that makes it a project: app-template's abaplint.jsonc and abap2ui5lint.jsonc, its AGENTS.md, the CI workflow running both gates, and the abapGit files. See Working Off-Stack for what that project then looks like.
For AI agents
The extension registers the abap2UI5 MCP server for every MCP client in the window — Copilot agent mode, Claude Code, anything else speaking MCP — so an agent working in your editor has the systemless dev loop without any separate configuration. Point abap2ui5.mcp.reposRoot at the folder holding the checkouts and the extension passes the paths through.
It adds a second server of its own for the half the MCP server deliberately does not have: your configured systems.
| MCP tool | What the agent gets |
|---|---|
list_systems | The configured launch systems and which one is active |
search_apps | Class names on the system, via the ADT quick search |
run_app_on_system | The app rendered on the real system, headless, as a screenshot |
Every prompt — system pick, credentials — stays an ordinary VS Code dialog the agent never sees. The names keep the two apart: the abap2UI5 server's run_app builds and boots the transpiled sandbox, this one runs a class on a system.
In the browser
The extension ships a web bundle, so it also runs in vscode.dev, github.dev and browser-based SAP Business Application Studio. Everything that needs no process and no socket works there: completion and hover, the property gate live while typing, the reconstructed XML view, the outline and event navigation, Convert XML View to Builder Chain, the snippets and the whole template gallery. The repository's abap2ui5lint.jsonc is honoured there too, so vscode.dev agrees with CI.
Desktop-only, and hidden from the palette on the web: the embedded preview with its proxy, traffic log, screenshot and pin; Ctrl+F3 activation and the ADT integration; the render gate; the workspace-wide check and quick fixes; the navigation map; the Control Properties view; and the MCP servers.
The settings worth knowing
| Setting | Default | Meaning |
|---|---|---|
abap2ui5.launchUrlTemplate | – | URL template used to launch an app, {class} as the placeholder |
abap2ui5.systems | [] | Named launch profiles, for more than one system |
abap2ui5.openMode | tab | tab, panel or external |
abap2ui5.reloadOn | activation | When the preview reloads: activation, save or never |
abap2ui5.viewCheck.minUi5 | 1.71 | The UI5 version your system runs |
abap2ui5.viewCheck.distribution | sapui5 | Which distribution the system serves |
abap2ui5.viewCheck.live | true | Run the property gate while typing |
abap2ui5.viewCheck.render | false | Also run the headless render gate |
abap2ui5.viewPreview.theme | sap_horizon | Theme the systemless preview renders in |
abap2ui5.viewPreview.viewport | 1280x900 | Viewport(s); a comma-separated list is a device matrix |
abap2ui5.inlineFindings | problems | The finding at the end of its line: problems, all or off |
abap2ui5.mcp.reposRoot | – | Folder holding the abap2UI5 / samples-controls / linter / mcp-server checkouts |
Every command is in the Command Palette (Ctrl/Cmd + Shift + P) under abap2UI5; the full settings and command tables are in the repository README.
Next Steps
- abap2UI5 linter — the gates behind the editor diagnostics
- MCP Server — the systemless loop the extension registers for agents
- Working Off-Stack — the project the template gallery writes
