Matlab Build App
MathWorksOfficialCreated by MathWorks
- Skill
- Workflow
- Software development
- BSD-2-Clause
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured approach to developing MATLAB applications, guiding users from initial requirements to a fully implemented codebase. It helps determine the optimal architecture (UIFigure or UIHTML) and layout archetype (e.g., Dashboard, Explorer, Wizard) based on the user's specific needs, team expertise, and maintenance requirements. The skill enforces a rigorous planning phase, requiring an implementation plan before any code is written, and supports multiple serialization formats for UIFigure apps, including App Designer (.mlapp) and plain-text (.m + .xml) formats.
What it does
- Conducts discovery to identify app purpose, maintenance needs, and visual requirements.
- Recommends the best architecture (UIFigure or UIHTML) and layout archetype.
- Produces a detailed implementation plan and writes it to a markdown file.
- Supports building UIFigure apps with optional App Designer serialization.
- Ensures structural integrity by enforcing grid-based layouts and proper background task handling.
- Provides a clear, step-by-step build sequence based on internal documentation.
When to use it
- When you need to build a new MATLAB GUI or interactive tool.
- When you are unsure whether to use UIFigure or UIHTML for a specific project.
- When you need to define a clear development plan for a complex MATLAB application.
- When you want to create an App Designer app using either binary or plain-text formats.
Advantages
- Ensures consistent, maintainable application architecture.
- Prevents common pitfalls by enforcing proven layout and background task patterns.
- Provides clear guidance on choosing between App Designer and programmatic approaches.
- Facilitates collaboration by requiring a documented implementation plan before coding.
Usage examples
- “I need to build a MATLAB dashboard to monitor sensor data. What approach should I take?”
- The agent will ask discovery questions about the app's lifespan, team skills, and visual needs, then recommend an architecture and layout archetype before creating a plan.
- “Build me a UIHTML app for visualizing experimental results.”
- The agent will skip the architecture recommendation, ask for any missing requirements, and proceed to generate an implementation plan for a UIHTML-based app.
Install
5 of its files are not mirrored here, so install it from its source:
https://github.com/matlab/matlab-agentic-toolkit/tree/2a7f215e965597acf6ed495beb9e7b8aff45a3be/skills-catalog/matlab-app-building/matlab-build-app
Documentation by MathWorks
MATLAB Build App
Determine the best architecture for a MATLAB application, produce an implementation plan grounded in internal references, and execute the build. For UIFigure apps, optionally serialize into an App Designer format.
When to Use This Skill
Use this skill when:
User wants to build a MATLAB app, GUI, or interactive tool
User asks "how should I build this app?" or "which approach should I use?"
User describes an application — with or without specifying an implementation path
User mentions: MATLAB app, GUI, uifigure, uihtml, interactive tool, dashboard, visualization app, App Designer, .mlapp, plain-text app
User describes spatial layout needs: dashboard, control panel, sidebar, tabs, wizard, stepper, canvas, workspace
User has already chosen a path (e.g., "build me a uihtml app") — handle directly
When Not to Use
The request is purely about MATLAB computation with no UI component
The user is asking about an existing app they want to modify → read
references/editing-guide.mdThe user wants to convert between App Designer formats (e.g., .mlapp to plain-text or vice versa) → tell them to use File > Save As in App Designer. Do NOT attempt the conversion programmatically.
Critical Rules
MUST ask discovery questions before recommending a path — unless the user already specified one
MUST confirm the path choice with the user before producing the implementation plan
MUST produce an implementation plan grounded in internal references before writing any code
MUST write the plan to a file (
<app-name>-plan.md) in the working directoryNEVER recommend a path without understanding the user's constraints (unless path was pre-specified)
NEVER apply dark mode, custom colors, or visual themes unless the user explicitly requests them
ALWAYS present the recommendation as guidance, not a mandate — the user decides
MUST choose archetype based on the user's primary task, not aesthetics
NEVER treat two archetypes as equals within one app — one is always the primary container
UIFigure app: MUST use
uigridlayoutfor all structural layout — neverPosition-based sizingUIHTML/web app: MUST use CSS Grid or Flexbox for chrome — no absolute positioning for structural panels
The chrome (header, sidebar, tabs, step indicator) MUST remain spatially stable
Background execution (any operation taking more than ~1 second) requires a class-structured app and MUST be read up first in
references/background-tasks.md— do NOT hand-writeparfeval/backgroundPool/DataQueueplumbing from memory. For an App Designer app (.mlappor plain text) you MUST use theaddBackgroundTaskverb and NEVER hand-roll the infrastructure or reuse the standalone-programmatic manual wiring. For a standalone programmatic app, use the MVVM structure per that doc. A flat nested-function app that needs background work MUST escalate to one of these.App Designer serialization: MUST read
references/app-designer/agent-guide-shared.md(covers ownership models, editing discipline, property rules, quoting) plus the format-specific guide (agent-guide-mlapp.mdoragent-guide-plaintext.md) before building. Those docs are the single source of truth; do not attempt to edit app files without reading them first.
Workflow
User request arrives
│
├── Path NOT specified → Full Discovery
│ │
│ ▼
│ Ask Q1-Q4 (purpose, lifespan, polish, team skills)
│ │
│ ▼
│ Identify layout archetype
│ │
│ ▼
│ Recommend UIFigure vs UIHTML based on Q1-Q4
│ │
│ ▼
│ *** STOP: Present recommendation and WAIT for user confirmation ***
│ (Do NOT produce a plan or read references until user says yes)
│ │
│ ▼
│ Ask Q5 (FINAL): Serialization format? (UIFigure apps only)
│ │
│ ├── App Designer → release check, present format choice,
│ │ continue with UIFigure references + add serialization step
│ │
│ └── Standalone programmatic → no extra step
│ │
│ ▼
│ Produce Implementation Plan → write to <app-name>-plan.md
│ │
│ ▼
│ User reviews plan → confirms or adjusts
│ │
│ ▼
│ Begin building (read references per the plan)
│
└── Path IS specified
│
├── Serialization explicit (e.g., ".mlapp app", "plain-text app")
│ → Skip Q5, proceed with stated format
│ → Ask only missing requirements + archetype if unclear
│ → Produce Implementation Plan
│
├── Architecture explicit but serialization not stated
│ (e.g., "build me a uifigure app")
│ → Still ask Q5 (serialization) before planning
│ → Ask missing requirements + archetype if unclear
│ → Produce Implementation Plan
│
└── UIHTML or standalone programmatic explicit
→ Skip Q5 entirely (not applicable)
→ Ask missing requirements + archetype if unclear
→ Produce Implementation PlanDiscovery Questions
Ask these conversationally — not as a rigid checklist. Gather requirements and layout intent first; ask the serialization question last, once you understand what the app needs to do.
Core Questions
1. What does the app do and who is it for?
What will this app do? Who will use it?
Listen for archetype signals: "dashboard", "control panel", "step-by-step", "workspace".
2. Is this a quick tool or something you'll maintain over time?
Is this meant to be maintained and evolved, or is it more of a quick, proof-of-concept tool?
Maintained → proceed to remaining questions; team skills matter
Ephemeral → favor UIHTML app path; can skip team question
3. How polished does the UI need to look?
Are standard MATLAB buttons, sliders, tables, and plots enough? Or do you need custom visuals — branded, animated, or visually richer?
Standard controls sufficient → UIFigure app signal
Custom visuals needed → UIHTML app signal
4. Who will work on this app going forward?
Will this be maintained by people comfortable only with MATLAB, or by people also comfortable with web technologies?
Only ask for maintained apps.
MATLAB-only team → UIFigure app
Web-comfortable team → UIHTML app
5. (FINAL) How should this app be saved?
Do you want this app to open and edit inside App Designer, or do you prefer a standalone programmatic file (no App Designer dependency)?
This is a serialization question, not an architecture question. The app's structure (components, layout, callbacks) is designed using UIFigure knowledge regardless of the answer. Q5 only determines how that design is persisted to disk.
App Designer →
.mlappor plain-text.m+.xml. Check release (plain-text needs R2026b+). If Q3 indicated custom visuals, note that App Designer constrains visual customization vs UIHTML and confirm.Standalone programmatic → programmatic
.mcode, no App Designer dependency. MVVM may warrant multiple files; that is decided at build time.
Follow-up Probes (only if path isn't clear)
Interaction style: Real-time feedback needed? → slight UIFigure lean
Existing work: Existing MATLAB UI code? → UIFigure. Existing web assets? → UIHTML
Distribution: May need to work outside MATLAB? → UIHTML
Long computations: Will any operation take more than a second? → use a background task (read
references/background-tasks.md). Note this requires a class-structured app: App Designer serialization, or MVVM if standalone programmatic. Heavy, long, or one-shot work is ALWAYS a background task, never a timer; a timer fits only scheduled, cheap-per-tick work that must update the UI on the main thread (readreferences/timers.md).
Path Decision Logic
Two stages: first determine architecture (Q1-Q4), then determine serialization format (Q5).
Stage A — Architecture (from Q1-Q4):
Ephemeral app? → Strong default to UIHTML app
Maintained app? → Weigh signals:
Strong UIFigure signals:
✓ Maintained + MATLAB-only team
✓ Standard UI components sufficient
✓ Tight real-time interaction
✓ Existing MATLAB UI code
Strong UIHTML signals:
✓ Ephemeral (regardless of team)
✓ Maintained + web-skilled team
✓ Custom visuals, branded look, animations
✓ Existing web assets
✓ Future portability outside MATLAB
UIFigure + uihtml accent:
Mostly standard app but needs one custom visualization panel.
Stage B — Serialization format (Q5, UIFigure apps only):
Q5 = Standalone → programmatic code (no App Designer). Done.
Q5 = App Designer → adds a serialization layer ON TOP of Stage A.
UIFigure references still apply in full. Serialization is IN ADDITION TO,
not instead of, the normal UIFigure build.
→ Check release, present format choice (plain text vs .mlapp).
→ If Stage A indicated UIHTML for custom visuals, surface the trade-off:
App Designer constrains custom visuals — confirm before committing.Weighting: 1. Architecture (Q1-Q4) 2. Serialization (Q5, applies only to UIFigure)
Paths at a Glance
| Architecture | Key strength |
|---|---|
| UIFigure app | Single language, any MATLAB dev can maintain |
| UIHTML app | Full visual control, rich interactivity |
| UIFigure + accent | Best of both when you need one custom visual |
For UIFigure apps, three serialization formats:
| Format | Trade-off |
|---|---|
Programmatic .m | Standalone, no App Designer dependency |
App Designer .mlapp | Binary, works on any release, opens in App Designer |
App Designer plain text .m + .xml | Source-controllable, AI-editable, R2026b+ |
Layout Archetype
Pick based on the user's primary task:
| Archetype | Primary user task | Structure |
|---|---|---|
| Dashboard | Monitor and compare at a glance | KPI cards + charts + table; read-only |
| Explorer | Adjust parameters, observe live results | Sidebar controls + live display |
| Tabbed | Navigate between independent sections | Tab bar + content panels |
| Wizard | Complete a sequential workflow | Step indicator + one-step-at-a-time |
| Canvas | Create, inspect, or edit spatial artifacts | Central workspace + tools |
If unclear, ask: "Will users mostly be watching results (Dashboard), tweaking controls (Explorer), switching sections (Tabbed), going through steps (Wizard), or working on a central figure (Canvas)?"
Implementation Plan
After confirming path and archetype, produce a plan and write it to <app-name>-plan.md in the working directory. Present a concise summary inline with the approval request.
Plan Template
**Implementation Plan: [App Name]**
**Architecture:** [UIFigure app / UIHTML app / UIFigure + accent]
**Serialization:** [Programmatic .m / App Designer .mlapp / App Designer plain text]
**Layout:** [Archetype] — [one sentence describing spatial structure]
**Structure:**
- [Major panel/area — spatial description]
- [e.g., "Left sidebar (220px) with date range picker, category filter, refresh button"]
- [e.g., "Main area with line chart showing filtered results"]
**Key behaviors:**
- [User-visible interaction effects]
**Internal references that will be used:**
| Reference | Role in this app |
|-----------|-----------------|
| `references/path/file.md` | [what it provides] |
| ... | ... |
**External skills:**
- `matlab-build-chart` — [role, if applicable]
- `matlab-apply-theme` — [role, if applicable]
**File organization:**
[app directory tree]
**Implementation sequence:**
1. [First reference read] — [what it establishes]
2. [Next reference] — [what it adds]
3. [...]Each reference pointer carries provenance: [src: references/uifigure/grid-layout.md §Spanning]
Plan Quality Checks
Before presenting:
[ ] Every reference listed has a concrete role
[ ] Structure uses spatial terms the user can visualize
[ ] Behaviors describe user-visible effects
[ ] Implementation sequence shows a logical build order
[ ] Plan is specific enough for "yes, that's what I want" or "change X"
Reference Router
After plan approval, read the relevant internal references to execute the build.
UIFigure Path
| When building... | Read |
|---|---|
| Grid layout, sizing, components | references/uifigure/guide.md |
| Grid details (spanning, collapse) | references/uifigure/grid-layout.md |
| Panels, tab groups, nested grids | references/uifigure/containers.md |
| Component reference (controls, display) | references/uifigure/components.md |
| Callback patterns, data sharing | references/uifigure/callbacks.md |
| Layout recipes (sidebar, form, split) | references/uifigure/layout-patterns.md |
| MVVM architecture (complex apps) | references/uifigure/mvvm-guide.md |
| View binding patterns | references/uifigure/mvvm-view-binding.md |
| ViewModel testing | references/uifigure/mvvm-testing.md |
| Background execution — concepts + routing (read first) | references/background-tasks.md |
| Background execution — standalone programmatic (MVVM) wiring | references/uifigure/mvvm-background-tasks.md |
| Timers — periodic main-thread work (NOT for offloading heavy work) | references/timers.md |
UIHTML Path
| When building... | Read |
|---|---|
| MATLAB-JS bridge, setup(), events | references/uihtml/bridge-guide.md |
| Communication patterns (4 patterns) | references/uihtml/communication-patterns.md |
| Data type conversion | references/uihtml/data-types.md |
| Platform constraints | references/uihtml/platform-limitations.md |
| uihtml creation, hybrid layouts | references/uihtml/setup.md |
| Error handling (both sides) | references/uihtml/error-handling.md |
| JS MVVM architecture | references/uihtml/mvvm-guide.md |
| Observable/Computed classes | references/uihtml/mvvm-observable-classes.md |
| JS View binding | references/uihtml/mvvm-view-binding.md |
| JS coding patterns, modules | references/uihtml/js-coding-guide.md |
| JS testing & debugging | references/uihtml/js-testing-debugging.md |
| Chart.js setup & patterns | references/uihtml/charting-guide.md |
| Chart type selection | references/uihtml/chart-type-selection.md |
| Chart.js initialization | references/uihtml/chartjs-setup.md |
| Line & bar charts | references/uihtml/line-bar-charts.md |
| Doughnut & scatter charts | references/uihtml/doughnut-scatter-charts.md |
| Chart data updates | references/uihtml/chart-updates.md |
| Chart ↔ bridge integration | references/uihtml/chart-bridge-integration.md |
| Chart performance | references/uihtml/chart-performance.md |
| CSS styling & tokens | references/uihtml/styling-guide.md |
| Component CSS (buttons, inputs) | references/uihtml/component-styles.md |
| CSS layout patterns | references/uihtml/css-layout-patterns.md |
| Brand design tokens | references/uihtml/brand-design-tokens.md |
| Dark mode | references/uihtml/dark-mode.md |
| External color schemes | references/uihtml/external-color-schemes.md |
App Designer Serialization (UIFigure apps only)
Important: App Designer serialization is an additional step AFTER the normal UIFigure build. You MUST also read the UIFigure path references above (components, grid-layout, callbacks, containers, layout-patterns) to design the app's structure. The serialization references below cover only how to persist that design to disk.
Both formats use the same AppDesignerAgentInterface API via the bundled scripts/ tool; only the file extension passed to create()/open() differs.
Read first (both formats):
| When building... | Read |
|---|---|
Verbs, build sequence, editing, save()/validate()/finalize(), inspect() | references/app-designer/agent-guide-shared.md |
| Background execution — concepts + routing (read first) | references/background-tasks.md |
Background execution — addBackgroundTask verb reference | references/app-designer/background-tasks.md |
Plain-text (.m + .xml):
| When building... | Read |
|---|---|
Ownership model, .xml authoring, flow, gotchas | references/app-designer/agent-guide-plaintext.md |
| classdef structure & block ordering rules | references/app-designer/plaintext/classdef-template.md |
| XML structure and which properties to set | references/app-designer/plaintext/xml-template.md |
| Per-component property names, types, defaults | references/app-designer/plaintext/property-reference.md |
| Authoritative XML schema | references/app-designer/plaintext/matlabAppSchema.xsd |
Binary .mlapp:
| When building... | Read |
|---|---|
Build flow, native property values, save()/finalize() | references/app-designer/agent-guide-mlapp.md |
Editing an Existing App
| When editing... | Read |
|---|---|
| Any existing app (determines format, routes to approach) | references/editing-guide.md |
Archetype References
| Archetype | File |
|---|---|
| Dashboard | references/archetypes/dashboard.md |
| Explorer | references/archetypes/explorer.md |
| Tabbed | references/archetypes/tabbed.md |
| Wizard | references/archetypes/wizard.md |
| Canvas | references/archetypes/canvas.md |
External Skills (invoked, not read)
matlab-build-chart— when the app includes MATLAB plots (UIFigure path or UIFigure+accent)matlab-apply-theme— when the app needs dark mode, brand colors, or custom palettes (UIFigure path)
After Plan Approval
All UIFigure apps (regardless of serialization format)
Read the archetype reference for the spatial skeleton
If MVVM warranted (Explorer, Wizard, Canvas, or complex apps): read the architecture reference
Read the UIFigure guide to establish layout (grid, components, containers)
Layer in path-specific references per the implementation sequence
For visual polish: invoke
matlab-apply-theme(UIFigure) or read styling references (UIHTML accent)For charts: invoke
matlab-build-chart(UIFigure) or read charting references (UIHTML accent)If App Designer serialization: read the app-designer reference docs (shared guide + format-specific guide), then use the
AppDesignerAgentInterfaceAPI to persist the app. The docs contain the complete build flow, property rules, and editing discipline.
UIHTML path
Read the archetype reference for the spatial skeleton
If MVVM warranted: read the JS MVVM architecture reference
Read the bridge guide to establish the MATLAB-JS connection
Layer in path-specific references per the implementation sequence
For visual polish: read styling/CSS references
For charts: read charting references
Format Fallback
If the plain text path produces errors that cannot be resolved (e.g., version mismatch, unsupported component), fall back to the .mlapp path — the same verbs drive both formats; re-create the handle with a .mlapp extension. Only fall back to fully programmatic UIFigure code as a last resort.
Recommendation Template
Recommended architecture: [UIFigure app / UIHTML app / UIFigure + accent]
Based on what you've described:
[Key signal 1]
[Key signal 2]
[Key signal 3, if applicable]
What this means: [1-2 sentences on development experience]
Trade-off: [Main thing they'd give up vs the other path]
Serialization (if App Designer): I'd recommend [.mlapp / plain text] because [reason]. This doesn't change the app's structure, just how it's saved.
Want to proceed with this approach, or would you prefer the other path?
Copyright 2026 The MathWorks, Inc.
Files
- ATTRIBUTION.mdAdded by AutoGineer1.0 KiB
- LICENSELicence file added by AutoGineer1.4 KiB
- SKILL.md20.9 KiB
- assets/colors_and_type.css12.8 KiB
- assets/minimal-bridge.html1.3 KiB
- assets/minimal-bridge.m1.2 KiB
- assets/standalone-chart-template.html11.5 KiB
- manifest.yaml672 B
- references/app-designer/agent-guide-mlapp.md15.7 KiB
- references/app-designer/agent-guide-plaintext.md14.7 KiB
- references/app-designer/agent-guide-shared.md27.1 KiB
- references/app-designer/background-tasks.md4.8 KiB
- references/app-designer/plaintext/classdef-template.md11.3 KiB
- references/app-designer/plaintext/matlabAppSchema.xsd16.8 KiB
- references/app-designer/plaintext/property-reference.md45.1 KiB
- references/app-designer/plaintext/xml-template.md12.3 KiB
- references/archetypes/canvas.md8.0 KiB
- references/archetypes/dashboard.md6.4 KiB
- references/archetypes/explorer.md5.7 KiB
- references/archetypes/tabbed.md6.6 KiB
- references/archetypes/wizard.md9.7 KiB
- references/background-tasks.md6.9 KiB
- references/editing-guide.md3.3 KiB
- references/timers.md2.1 KiB
- references/uifigure/callbacks.md3.0 KiB
- references/uifigure/components.md1.7 KiB
- references/uifigure/containers.md1.3 KiB
- references/uifigure/grid-layout.md4.9 KiB
- references/uifigure/guide.md5.3 KiB
- references/uifigure/layout-patterns.md1.5 KiB
- references/uifigure/mvvm-background-tasks.md3.9 KiB
- references/uifigure/mvvm-guide.md11.1 KiB
- references/uifigure/mvvm-testing.md2.8 KiB
- references/uifigure/mvvm-view-binding.md5.4 KiB
- references/uihtml/brand-design-tokens.md5.6 KiB
- references/uihtml/bridge-guide.md7.0 KiB
- references/uihtml/chart-bridge-integration.md7.5 KiB
- references/uihtml/chart-performance.md5.0 KiB
- references/uihtml/chart-type-selection.md3.1 KiB
- references/uihtml/chart-updates.md4.7 KiB
- references/uihtml/charting-guide.md3.9 KiB
- references/uihtml/chartjs-setup.md4.0 KiB
- references/uihtml/communication-patterns.md4.2 KiB
- references/uihtml/component-styles.md4.3 KiB
- references/uihtml/css-layout-patterns.md5.0 KiB
- references/uihtml/dark-mode.md3.8 KiB
- references/uihtml/data-types.md2.7 KiB
- references/uihtml/doughnut-scatter-charts.md6.0 KiB
- references/uihtml/error-handling.md4.6 KiB
- references/uihtml/external-color-schemes.md2.6 KiB
- references/uihtml/js-coding-guide.md11.2 KiB
- references/uihtml/js-testing-debugging.md2.9 KiB
- references/uihtml/line-bar-charts.md5.2 KiB
- references/uihtml/mvvm-guide.md11.0 KiB
- references/uihtml/mvvm-observable-classes.md2.9 KiB
- references/uihtml/mvvm-view-binding.md5.3 KiB
- references/uihtml/platform-limitations.md2.5 KiB
- references/uihtml/setup.md3.9 KiB
- references/uihtml/styling-guide.md8.1 KiB
- scripts/+module/+backgroundtask/BackgroundTaskModule.p(opens on GitHub)Not mirrored: opens on GitHub2.5 KiB
- scripts/+module/+backgroundtask/templates/cancelBackground.m357 B
- scripts/+module/+backgroundtask/templates/handleBackgroundComplete.m1.4 KiB
- scripts/+module/+backgroundtask/templates/safeProgress.m734 B
- scripts/+module/+backgroundtask/templates/startBackground.m1.2 KiB
- scripts/+module/AppBuilderModule.p(opens on GitHub)Not mirrored: opens on GitHub242 B
- scripts/AppDesignerAgentInterface.p(opens on GitHub)Not mirrored: opens on GitHub12.6 KiB
- scripts/MlappBackend.p(opens on GitHub)Not mirrored: opens on GitHub9.4 KiB
- scripts/PlainTextBackend.p(opens on GitHub)Not mirrored: opens on GitHub10.2 KiB
references/app-designer/agent-guide-shared.md
AppDesignerAgentInterface: Shared Guide (read this first)
About this API.
AppDesignerAgentInterfaceis an internal API, for agentic use only. It exists to be driven by an AI agent (you) throughAppDesignerAgentInterface; it is not a supported, user-facing API. Its surface (verbs, arguments, behavior) may change between MATLAB releases without notice. Treat this guide, for the release you are running against, as the single source of truth. Do not assume any part of the API is permanent or carry assumptions about it across releases.
AppDesignerAgentInterface is a stateful MATLAB API that serializes UIFigure apps into
App Designer formats. The app's structure (components, layout, callbacks) is designed
using UIFigure knowledge; this tool handles how that design is persisted to disk.
You call verbs that mutate an in-memory model, then save() serializes once.
This file covers the paradigms that are the same for both formats. Then read the
format-specific guide:
.mlappapps: readagent-guide-mlapp.md.plain-text (
.m+.xml) apps: readagent-guide-plaintext.md.
The two formats have different ownership models, so do not assume a verb behaves the same in both. The single most important rule: the tool does exactly what it was told, nothing more, and tells the truth about what it did. If something is wrong it throws a descriptive error rather than silently guessing or "fixing" it.
How you interact with it
Everything runs in MATLAB. A session looks like:
addpath('.../matlab-build-app/scripts'); % put the API on the path
appBuilder = AppDesignerAgentInterface.create("C:/path/App.mlapp"); % or .open(...)
appBuilder.setProperty("UIFigure", "Name", "My App"); % ... verbs ...
appBuilder.save(); % serialize + gateappBuilder is a handle object. All the instance verbs mutate the model it holds. The
factory methods (create, open, finalize) are static (AppDesignerAgentInterface.create(...)).
Format is decided by the file extension you pass to create/open:
.mlapp -> mlapp path, .m -> plaintext path. ClassName is derived from the
filename stem, so name the file what you want the app class to be called.
Converting between formats: If the user asks to convert an existing app from
.mlapp to plain text (or vice versa), do NOT rebuild it from scratch. Tell the
user to open the app in App Designer and use Save As, changing the file
extension to .mlapp or .m. App Designer handles the conversion natively and
preserves all components, callbacks, and properties. Rebuilding wastes tokens and
risks losing fidelity.
Calling conventions (keep a clean workspace)
Every eval you run lands in the base workspace. Follow these rules to avoid polluting it with temporaries:
Always semicolon-terminate. Every statement must end with
;. Without it, MATLAB echoes the return value to the console (noisy) and assignsans.Inline structs into verb calls. Do not create intermediate variables for property structs. Pass them directly:
appBuilder.addComponent("Button", "Btn", "UIFigure", struct('Text','Go','FontSize',14));Not:
props = struct('Text','Go','FontSize',14); % BAD — leaves `props` in workspace appBuilder.addComponent("Button", "Btn", "UIFigure", props);Only
appBuildershould persist. It is the one handle you need across calls. If a struct is genuinely too complex to inline readably, use it and thenclearit in the same eval:clear props;
save() vs finalize() — what each one is
These are two different things and conflating them leads to invented workarounds. Get the model straight before the when-to-call rules below.
save()persists and validates. It serializes the in-memory model to disk and runs the real product loader as a gate. A file thatsave()accepted is complete, valid, and openable — nothing else is required to make it a real app. This is the workhorse: call it whenever you need the model on disk.finalize()presents the finished result. It round-trips the saved file through the live product (mlapp: bakes the full runnable classdef; plaintext: canonicalizes the.xml) and leaves the app open so the user sees it rendered. It is a presentational, optional, once-at-completion step — not part of correctness. Asave()d app is already done in every load-bearing sense;finalize()just makes it look finished on screen.
finalize() is reversible — it is not a one-way door. You can always open() the
file after finalizing and keep editing (free it from AD first — open() for code,
release() for plaintext .xml, per the iterate-to-preview rule below). Never avoid
finalize() to "preserve editability" — editability is never lost, so that is not a
real trade-off.
"finalize" is internal vocabulary — never say it to the user. To the user this is
just "opening the app in App Designer so they can see it." When you offer it, ask in
their terms ("Want me to open it in App Designer so you can take a look?"), not "should
I finalize it?" — the word sounds like a committing, irreversible step, and it is
neither. Keep finalize() as the mechanism you invoke, not a term you expose.
How to drive it: chunked inline calls, not a script
Run the verbs as inline calls in a persistent MATLAB session, grouped into a
few logical chunks. Do not write the build out to a temp
.m script and run that. Two reasons:
The model is a persistent handle.
appBuilderlives in the session between calls, so each verb builds on the last. This only works against a persistent MATLAB session (what the connector / MCP server gives you) — the handle has to survive from one call to the next. A temp script throws that away and hides the operations from the user.Failures stay cheap and local. Verbs validate before they mutate, so a verb that throws leaves the model untouched — you fix that one call and continue. Because the earlier verbs already applied to the live model, you never re-emit the whole build to recover from one bad call. A monolithic script forces exactly that re-run.
Chunk by logical unit — e.g. all of one panel's components in one call, then its callbacks in the next. Not one verb per round-trip (you pay latency for nothing) and not one giant block (a failure anywhere makes you hunt). A handful of coherent chunks gives you failure-pinpointing without the overhead.
Many cheap verbs, one save(). save() is the expensive step (it runs the real
product loader as a gate). Do not save() between chunks to "check progress" —
Layer-1 already validated each verb as you called it, and if save() does reject
something it names the offending component, which you fix with a single setProperty
and re-save. No rebuild.
Finalize once, when the work is done (both formats). When you have finished a build
or an edit — not after every intermediate save() — make AppDesignerAgentInterface.finalize(filepath)
your last action. finalize() launches App Designer and leaves the app open so the
user sees the finished result with its theme rendered. What it accomplishes differs by
format (mlapp bakes the full runnable classdef; plaintext canonicalizes the .xml) —
see each format guide. Do NOT finalize between chunks or after each save() in a
multi-step build — that relaunches App Designer every time (~4s each) and churns the UI
the user is watching. Save as many times as the build needs; finalize exactly once at
completion.
One exception — iterate-to-preview. If you finalize, then need a small adjustment,
you must free the file from AD before editing it (finalize left it open; editing a file
AD holds triggers a wedging reload dialog). Editing code → open() first; editing
plaintext components (the .xml) → release() first. Then edit, save(), and
finalize() again. See the plaintext guide's "Editing after finalize" section.
Recommended build sequence
There are only a few HARD ordering rules; most of what looks ordered is not. In order of how much they matter:
create()first. Seeds the rootUIFigure.Parent before child.
addComponentrequires the parent to exist, so build the tree top-down: containers (Panel,GridLayout,TabGroup) before their contents. This is the one real constraint inside the "components" step.A target must exist before you reference it.
setPropertyandaddCallbackneed their component present. (Naturally satisfied if you build top-down.)save()last.
Everything else is FREE — do not over-sequence:
Properties are not a separate phase. Set a component's properties inline in the
addComponentpropsstruct. Fewer calls, and coupled-order pairs (likeDropDownItems-before-Value) are reordered for you (see the mlapp guide). ReservesetPropertyfor edits to an already-built app, not fresh construction. For a child of aGridLayout, place it in a cell with'Layout', struct('Row', r, 'Column', c), a struct, never a[row col]vector (see the mlapp guide, "Placing a component into a GridLayout cell").Code is order-free. Callback bodies, methods, and
addPropertyreferenceapp.<X>at runtime, not when you call the verb. A callback body may mention a component or property you have not declared yet. The only code-side ordering rule is thataddCallback(wiring an event slot) needs the component to exist. WireStartupFcnlast so it can reference everything.
So a natural mlapp build reads: create + figure props → component tree top-down
(properties inline) → app-state addProperty → behavior (addCallback + bodies +
methods, startup last) → save() once. Plaintext is the mirror image — components
live in the hand-authored .xml, so the sequence is author .xml tree → code verbs
→ save() (see the plaintext guide).
The verbs (full public surface)
Factories (static)
| Call | Returns | Notes |
|---|---|---|
AppDesignerAgentInterface.create(filepath) | handle | New empty app. Seeds a root UIFigure. For plaintext also seeds a boilerplate .xml (see plaintext guide). Releases the file from AD first (posture A). |
AppDesignerAgentInterface.open(filepath) | handle | Load an existing app into a model. Releases the file from AD first (posture A). |
AppDesignerAgentInterface.finalize(filepath [, deadlineSeconds]) | (void) | Both formats. Opens in AD, saves through the product, leaves it open for viewing. mlapp bakes runnable code; plaintext canonicalizes the .xml. See each format guide. |
AppDesignerAgentInterface.release(filepath) | (void) | Free a file from App Designer before hand-editing its .xml (posture A: closes a clean copy, rejects an unsaved one). The escape hatch for editing plaintext components after finalize(). See plaintext guide. |
Component verbs (mutate the UI tree) — mlapp only
These edit components. On a plaintext model they HARD-ERROR
(AppDesignerAgentInterface:plaintextComponentsInXml) because you author plaintext components
in the .xml by hand. See the plaintext guide.
| Verb | Signature |
|---|---|
addComponent | appBuilder.addComponent(type, codeName, parentCodeName, props) — props optional struct |
setProperty | appBuilder.setProperty(codeName, propName, value) — supports dot-path for sub-object properties (e.g. "Title.String") |
removeComponent | appBuilder.removeComponent(codeName) |
moveComponent | appBuilder.moveComponent(codeName, newParentCodeName) |
addCallback | appBuilder.addCallback(codeName, eventName, callbackName) — wires an event slot to a function name |
Code verbs (mutate the .m code side) — both formats
| Verb | Signature |
|---|---|
setCallbackCode | appBuilder.setCallbackCode(callbackName, bodyCode) — the callback body text (dedented; no function line) |
addProperty | appBuilder.addProperty(propName, value, access) — access = "public" (default) / "private" / "protected" |
addMethod | appBuilder.addMethod(name, bodyCode, access) — bodyCode is the FULL function ... end text |
removeCallback | appBuilder.removeCallback(callbackName) — fully remove a callback: its body AND (mlapp) every component event slot wired to it. Throws AppDesignerAgentInterface:noSuchCallback only if the name has neither a body nor any wiring. |
removeMethod | appBuilder.removeMethod(name) — remove a method. Throws AppDesignerAgentInterface:noSuchMethod if absent. |
removeProperty | appBuilder.removeProperty(propName) — remove a property. Throws AppDesignerAgentInterface:noSuchProperty if absent. |
set/add code verbs REPLACE, they do not append. setCallbackCode,
addProperty, and addMethod each key on the name. If that name already exists,
the verb overwrites the whole entry and emits a warning
(AppDesignerAgentInterface:overwriteCallback / overwriteProperty / overwriteMethod) naming
the key. Nothing is merged. To modify rather than replace, use the read-modify-write
pattern below.
Inspection / lifecycle — both formats
| Verb | Returns | Notes |
|---|---|---|
inspect() | struct | Full model projection (see shape below). Read-only. |
validate() | struct array | Dry-run check. Non-throwing, writes nothing. Always a struct array with fields code, message, one element per problem. A clean result is a 0x0 struct (still has the two fields) — test it with isempty(v), which is true exactly when there are no problems. |
save() | (void) | Serialize + loadability gate. Throws on failure. |
All string arguments are MATLAB string ("...") or char ('...'); both work.
inspect() output shape
s = appBuilder.inspect() returns a struct:
s.ClassName string
s.Format "mlapp" | "plaintext"
s.StartupFcn string (name of the startup callback, or "")
s.Nodes cell array of node structs, in insertion order
s.Callbacks struct: fieldname = callback name, value = body text
s.Methods struct: fieldname = method name, value = struct with .Body (full function text) and .Access ("public"|"private")
s.Properties struct: fieldname = property name, value = default valueEach node struct in s.Nodes:
node.CodeName string (the unique identifier for the component)
node.Type string (component type, e.g. "Button")
node.ParentCodeName string
node.Props struct (property name -> native value) << field is "Props", NOT "Properties"
node.Layout struct (Row / Column for grid children)
node.Callbacks struct (eventName -> wired callback name)
node.Children string array of child codeNamesGotcha: the node property bag is node.Props, not node.Properties. To read a
component's Color: figNode.Props.Color.
Editing an existing app (read this before you change anything)
The API is stateful and additive. You never rebuild an app to change it, and you
never re-run create() on a file that already exists. The rule is:
To edit, open() the app and apply the smallest set of verbs that make the change.
Do NOT re-create the app or re-drive the whole build.
create() is for a brand-new file only. Everything else — adding a component,
changing a property, editing code, removing something — is open() + a few surgical
verbs + save(). The model you open already contains everything the app has; your
verbs mutate it in place.
The disciplined edit loop is inspect first, then mutate:
appBuilder = AppDesignerAgentInterface.open("C:/path/MyApp.mlapp");
s = appBuilder.inspect(); % <-- see the CURRENT state before you touch it
% ... decide the minimal change from what you actually see ...
appBuilder.setProperty("GoButton", "Text", "Start");
appBuilder.save();Why inspect() first matters:
Placement/layout: before adding a component, read existing node
Position(orLayoutfor grid children) so the new one fits, instead of guessing coordinates. Sees.Nodes{k}.Props.Position/.Layout.Avoiding accidental overwrite: before
setCallbackCode/addProperty/addMethod, check whether the name already exists ins.Callbacks/s.Properties/s.Methods. If it does, the verb REPLACES it (with a warning). That is the mechanism behind "it overwrote the callback I already wrote."
Modifying code (not replacing it): read-modify-write
There is no "append to a callback" verb. The code verbs replace by name. To add to existing code, read the current body, build the new full text yourself, and set it back:
s = appBuilder.inspect();
old = s.Callbacks.onGo; % current body ("" if unset)
newBody = old + newline + "app.Count = app.Count + 1;";
appBuilder.setCallbackCode("onGo", newBody); % now holds old + newIf you skip the read and just call setCallbackCode("onGo", "app.Count = ..."), you
lose the previous body and get an AppDesignerAgentInterface:overwriteCallback warning. The
warning is your signal that you replaced something — if you did not intend to, you
skipped the read step.
Removing code
Use the removal verbs (removeCallback / removeMethod / removeProperty). Each
throws if the target does not exist, so a clean call means it was really there and is
now gone. There is no verb to partially edit a body down — to shrink a method,
read-modify-write the full replacement text, or remove and re-add.
Removing a component is removeComponent (mlapp only; plaintext edits the .xml).
Removing a component does NOT remove any callback bodies it was wired to — those are
code entries; remove them separately with removeCallback if they are now orphaned.
Component types and events are validated from the product (Layer-1)
When you add a component, set a property, or wire a callback, AppDesignerAgentInterface validates
against the real App Designer component registry — nothing is hardcoded. An
invalid type/property/event throws immediately and mutates nothing.
There are 54 component types. Use the short name (last segment of the class),
e.g. "Button", "Label", "NumericEditField", "Slider", "DropDown":
AirspeedIndicator, Altimeter, ArtificialHorizon, Button, ButtonGroup, CheckBox,
CheckBoxTree, ClimbIndicator, ColorPicker, ContextMenu, DatePicker, DiscreteKnob,
DropDown, EGTIndicator, EditField, Gauge, GridLayout, HTML, HeadingIndicator,
Hyperlink, Image, Knob, Label, Lamp, LinearGauge, ListBox, Menu, NinetyDegreeGauge,
NumericEditField, Panel, PushTool, RPMIndicator, RadioButton, RangeSlider,
RockerSwitch, SemicircularGauge, Slider, Spinner, StateButton, Switch, Tab,
TabGroup, Table, TextArea, TimeScope, ToggleButton, ToggleSwitch, ToggleTool,
Toolbar, Tree, TreeNode, TurnCoordinator, UIAxes, UIFigureEvent names are per-type. Examples:
Button->ButtonPushedFcnSlider->ValueChangedFcn,ValueChangingFcnDropDown->ValueChangedFcn,DropDownOpeningFcn,ClickedFcn
The app root is always UIFigure (seeded by create). The app-level startup
callback is wired on UIFigure with event name StartupFcn.
Property order is handled for you
You can pass a component's properties in any order. Some components have coupled
properties that the product requires be applied in a specific order (e.g. a
DropDown's Items before its Value), but AppDesignerAgentInterface reorders each property bag
into App Designer's own canonical order before applying it, so you never have to
sequence them yourself. This is product-sourced, not a hardcoded list. See the mlapp
guide ("Property order does not matter") for the full set of affected components.
The startup callback
Wire it on the figure, then set its body:
appBuilder.addCallback("UIFigure", "StartupFcn", "startupFcn"); % mlapp only; plaintext wires it in .xml
appBuilder.setCallbackCode("startupFcn", "app.CurrentPlayer = ""X"";");StartupFcn is always a valid event on UIFigure. Use startup to initialize app
state properties (declare them bare via addProperty(name, "", "private"), then
assign real values in startup) so the app opens in a known state.
Callback bodies vs. method bodies (easy to get wrong)
setCallbackCode(name, body)takes just the body, dedented, nofunctionline, noend.AppDesignerAgentInterfacegenerates thefunction name(app, event)wrapper. Inside, you haveappandeventin scope.addMethod(name, body, access)takes the FULLfunction ... endtext, including the signature line. Write the whole thing:
appBuilder.addMethod("checkWin", strjoin([ ...
"function tf = checkWin(app)" ...
" tf = false;" ...
" % ..." ...
"end" ], newline), "private");Reach components from a callback/method via app.<CodeName> (e.g.
app.StatusLabel.Text = "..."). Reach app-level properties via app.<PropName>.
Which verb: is it wired to an event, or called from code? This is the
distinction that decides setCallbackCode vs addMethod:
A function that fires from a component event (a button push, a value change, startup) is a callback: wire it with
addCallback, then give it a body withsetCallbackCode. App Designer only emits a callback that is wired to an event slot — asetCallbackCodebody that no component wires to is silently dropped at save (it never appears in the generated app).validate()on an mlapp flags this asAppDesignerAgentInterface:orphanCallbackBodyso you catch it before save.A helper that other code calls by name (including anything you invoke via
createCallbackFcn(app, @thing, true), or a plainapp.thing()from another callback) is a method: add it withaddMethod. Do not create it withsetCallbackCode— an unwired body is dropped, so the call would reference a function that does not exist.
A name cannot be both a callback and a method. If the same name is wired as a
callback body and added as a method, it would be generated as two functions
with the same name (a broken classdef). AppDesignerAgentInterface rejects the collision with
AppDesignerAgentInterface:nameCollision from whichever verb is called second. To convert a
callback into a method (e.g. you wired it but really want a callable helper),
removeCallback(name) first — that removes the body and unwires the event
slot — then addMethod(name, ...).
Body text is treated as opaque code — it is stored and emitted verbatim, not
interpreted or reformatted. Literal %, $, quotes, and sprintf/fprintf format
strings pass through unchanged (e.g. a body containing sprintf("$%.2f", total)
round-trips exactly). You do not need to escape anything for AppDesignerAgentInterface's sake; write
the body as the MATLAB code you want to appear in the file. (The usual MATLAB rule
still applies to the string you hand the verb: a " inside a double-quoted MATLAB
string literal must be doubled "", but that is your call's syntax, not an
AppDesignerAgentInterface transformation of the body.)
Quoting patterns for bodies containing single quotes (transpose ' or char
literals 'text'):
% Option 1: use a double-quoted string — single quotes pass through unescaped
appBuilder.setCallbackCode("onPress", "x = [1 2 3]';" + newline + "app.Btn.Text = 'done';");
% Option 2: use sprintf with a char vector — escape ' as ''
appBuilder.setCallbackCode("onPress", sprintf('x = [1 2 3]'';\napp.Btn.Text = ''done'';'));Both produce identical stored bodies. Prefer double-quoted strings (option 1) since single quotes inside them require no escaping.
Shared callbacks (one function, many components)
Wire multiple components to the SAME callback name, then define the body once.
Distinguish which fired using event.Source:
appBuilder.addCallback("RockButton", "ButtonPushedFcn", "PlayMove");
appBuilder.addCallback("PaperButton", "ButtonPushedFcn", "PlayMove");
appBuilder.setCallbackCode("PlayMove", "move = string(event.Source.Text); ...");save() gates on real loadability — trust the error, don't fight it
save() does not just write bytes. It runs the app through the real product loader
(or, for mlapp, a round-trip re-read plus the loader). If the result would not load
cleanly in App Designer, save() throws with the loader's own error. Two failure
shapes to know:
Parse/config error — the file structure is bad (e.g. malformed
.xml).Errored components — the app parses but a component fails to construct (usually a bad property value the lexical checks let through, like an out-of-range enum). The error names the offending component codeName.
When save() throws, read the message: it tells you exactly what the product
rejected. Fix the model/.xml and save again. Do not assume a throw means the
AppDesignerAgentInterface is broken — it usually means the app really would not load.
Not yet supported
Two feature areas are out of scope for AppDesignerAgentInterface today:
Simulink apps
Apps that embed or bind to a Simulink model (signal viewers, dashboard blocks, model
callbacks) are not supported. The plain text format has no representation for Simulink
bindings at all. The mlapp format can encode them, but AppDesignerAgentInterface
does not expose verbs for model association, signal routing, or variable binding. Do not attempt to
wire Simulink connections through raw property manipulation; it will not produce a
loadable app.
User Authored Components (UAC)
UACs (custom component classes authored by the user) cannot be added through
addComponent. The plain text format does not support
UACs. For mlapp, the workaround is to instantiate the UAC programmatically in the
app's StartupFcn callback:
appBuilder.setCallbackCode("startupFcn", ...
"app.MyCustomGauge = MyGaugeComponent(app.UIFigure);" + newline + ...
"app.MyCustomGauge.Layout.Row = 2;" + newline + ...
"app.MyCustomGauge.Layout.Column = 1;");This is a valid App Designer pattern. The UAC will function at runtime but will not
appear in Design View. Alternatively, suggest the user add the UAC in Design View
manually; AppDesignerAgentInterface can then access it via setProperty/getProperty on
subsequent open() calls.
Working-session gotchas (for whoever drives MATLAB, incl. me)
After editing source (
AppDesignerAgentInterface.m/MlappBackend.m/PlainTextBackend.m), runclear classes; rehash toolboxcachebefore the next call, or MATLAB keeps the stale class definition. Not needed for test/script edits..mlappis a zip. To read the generated code, useappdesigner.internal.serialization.FileReader(path).readMATLABCodeText()— NOTfileread(reads raw zip bytes) and there is nomatlabCodeTextstruct field.String property values in the plaintext
.xmlmust be quoted ('text') — they are parsed as native MATLAB literals. See the plaintext guide. (mlapp props are passed as native MATLAB values directly through the verbs, so this only bites plaintext.)Bounded any App-Designer-touching run with a timeout; a wedged AD can block on an unbounded wait.
Copyright 2026 The MathWorks, Inc.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Declares allowed tools
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- MathWorks
- Publisher
- MathWorks (Official)
- Source
- matlab/matlab-agentic-toolkit · skills-catalog/matlab-app-building/matlab-build-app · commit 2a7f215
- Licence
- BSD 2-Clause License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE.md)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by MathWorks or MathWorks.
Get it from the source
5 of this skill’s files are not mirrored here, so there is no ZIP. Get the complete skill from its source: matlab/matlab-agentic-toolkit