Browse by type
A powerful web-based visual designer for creating MAUI (Microsoft App UI) layouts with drag-and-drop functionality. This Angular application provides an intuitive interface for designing XAML-based user interfaces with real-time preview capabilities.
Live demo: https://gmprakhar.github.io/MAUI-Designer/ (deployed to GitHub Pages from main)
Ctrl+Z / Ctrl+Y)Delete to remove, Ctrl+D to duplicate, arrow keys to nudge, Esc to deselectHorizontalOptions and VerticalOptions on every controlCtrl+A selects every child of the current layoutAppThemeBinding, previewed against the toolbar themeSemanticProperties editing plus live WCAG AA contrast checkingCtrl+] / Ctrl+[, add Shift to jump to either end)Ctrl+C, Ctrl+X, Ctrl+V (containers keep their children, names stay unique)Ctrl + mouse wheel, and Space + drag (or middle-drag) to panAvatarView, Expander, DrawingView, MediaElement, Popup and SemanticOrderView work out of the box.xaml file, or convert an .svg icon into MAUI Path elementsAuto, *, absolute) size the canvas tracks and round-trip through XAML. RowSpan / ColumnSpan stretch the child across those tracks.Progress valueIsRunning flagItemTemplateBefore you begin, ensure you have the following installed:
Clone the repository:
bash
git clone https://github.com/GMPrakhar/MAUI-Designer.git
cd MAUI-Designer
Install dependencies:
bash
npm install
Install Angular CLI globally (if not already installed):
bash
npm install -g @angular/cli
Start the development server:
npm start
# or
ng serve
Navigate to http://localhost:4200/ in your browser. The application will automatically reload when you make changes to the source files.
Build the project for production:
npm run build
# or
ng build
The build artifacts will be stored in the dist/ directory.
Unit tests (Karma + Jasmine):
npm test # watch mode
npm run test:headless # single headless run (CI friendly)
End-to-end tests (Playwright, headless Chromium). The Angular dev server is started automatically on port 4300:
npx playwright install chromium # once
npm run e2e # run the suite
npm run e2e:report # open the last HTML report
To run the suite against an already running instance (for example a production build):
E2E_BASE_URL=http://localhost:4400 npm run e2e
The specs live in e2e/ and cover the shell, toolbox, canvas, hierarchy, properties
panel, grid editing, undo/redo, persistence and the XAML editor.
The app is fully client side, so it can be hosted as a static site. Pushing to main
runs .github/workflows/deploy-pages.yml, which builds with the correct base href,
adds an SPA 404.html fallback and publishes to GitHub Pages.
Enable it once via Settings → Pages → Build and deployment → Source: GitHub Actions.
Build the same bundle locally with:
npm run build:pages
The toolbox is organized into three categories:
The Properties panel allows you to modify:
{Binding Path} instead of the literal valueThe designer does not need to reference your NuGet package — it only needs a description of the controls. Drop a JSON manifest into Toolbox → Custom controls → Import:
{
"id": "syncfusion-inputs",
"package": "Syncfusion.Maui.Inputs",
"xmlns": {
"prefix": "sf",
"uri": "clr-namespace:Syncfusion.Maui.Inputs;assembly=Syncfusion.Maui.Inputs"
},
"controls": [
{
"tag": "SfComboBox",
"displayName": "Combo box",
"icon": "arrow_drop_down_circle",
"defaultWidth": 200,
"defaultHeight": 40,
"isContainer": false,
"preview": { "kind": "box", "label": "{Placeholder}" },
"properties": [
{ "name": "Placeholder", "type": "string", "defaultValue": "Select an item" },
{ "name": "IsEditable", "type": "boolean", "defaultValue": false },
{ "name": "MaxDropDownHeight", "type": "number", "defaultValue": 200 }
]
}
]
}
Manifest reference:
| Field | Meaning |
|---|---|
id |
Stable identifier used for updates and removal |
package |
NuGet package name, shown in the toolbox and properties panel |
xmlns.prefix / xmlns.uri |
XML namespace emitted in the generated XAML |
controls[].tag |
The XAML tag, e.g. SfComboBox |
controls[].isContainer |
true if the control accepts child elements |
controls[].preview |
Canvas rendering: kind of box, text, image, list or slot, plus label, backgroundColor, textColor, borderColor, cornerRadius, icon |
controls[].properties[] |
Editable properties: name, type (string, number, boolean, color, enum), defaultValue, options, bindable |
{Property} placeholders inside preview.label and the colour fields are interpolated from the
element's current values, so the canvas preview updates as you edit.
Registered manifests are stored in localStorage under maui-designer.custom-controls. The
namespace URI is also stored on each element, so pasted XAML keeps working even if the manifest is
removed later.
src/
├── app/
│ ├── components/ # UI Components
│ │ ├── designer-canvas/ # Main design surface
│ │ ├── hierarchy-panel/ # Element tree view
│ │ ├── properties-panel/ # Property editor
│ │ ├── toolbox/ # Element toolbox
│ │ └── xaml-editor/ # XAML code editor
│ ├── models/ # Data models
│ │ ├── maui-element.ts # MAUI element definitions
│ │ └── toolbox.ts # Toolbox item definitions
│ ├── services/ # Business logic services
│ │ ├── drag-drop.ts # Drag-and-drop functionality
│ │ ├── element.ts # Element management
│ │ ├── layout-designer.ts # Layout calculations
│ │ ├── xaml-generator.ts # XAML code generation
│ │ └── xaml-parser.ts # XAML parsing
│ └── app.ts # Main app component
├── styles.scss # Global styles
└── index.html # Main HTML file
e2e/ # Playwright end-to-end tests
├── helpers/designer-page.ts # Page object with data-testid locators
├── app-shell.spec.ts
├── toolbox.spec.ts
├── canvas.spec.ts
├── hierarchy-panel.spec.ts
├── properties-panel.spec.ts
├── undo-redo.spec.ts
├── persistence.spec.ts
└── xaml-editor.spec.ts
UI elements expose
data-testidattributes for the e2e suite. Keep them stable when refactoring templates.
The application follows Angular's standalone components architecture with a service-based approach:
browse all types & interfaces →
$ claude mcp add MAUI-Designer \
-- python -m otcore.mcp_server <graph>