--- name: alter-page description: "Modify an existing page or snippet's widget tree in place with ALTER PAGE / ALTER SNIPPET — SET, INSERT, DROP, REPLACE and SET Layout. Use when changing a caption, style or property, adding or removing a widget, or reordering a form, instead of rewriting the whole page with CREATE OR REPLACE. The only safe way to change a page or snippet authored in Studio Pro." --- # ALTER PAGE / ALTER SNIPPET - Modify Existing Pages and Snippets ## Overview ALTER PAGE and ALTER SNIPPET modify an existing page or snippet's widget tree **in-place** without requiring a full `create or modify`. Operations work directly on the raw BSON tree, preserving widget types and properties that MDL doesn't explicitly model. ## When to Use | Scenario | Use | |----------|-----| | Change a button caption, label, or style | `alter page` with `set` | | Add a field to an existing form | `alter page` with `insert` | | Remove unused widgets | `alter page` with `drop` | | Replace a footer or section | `alter page` with `replace` | | Several related changes on the same page | `alter page` with multiple operations in one block | | Same property across many pages (e.g., add `Class` to every Container) | `update widgets` — see `bulk-widget-updates` | | Rebuild entire page from scratch (pages your scripts own only) | `create or modify page` | | Create a new page | `create page` | **Rule of thumb:** - `alter page` — targeted edits to one page. Combine multiple ops in one block when they belong together. - `update widgets` — cross-page bulk updates with `WHERE` filtering and `DRY RUN`. - `create or modify page` — redefining the full page structure of a page your MDL scripts own. **Who owns the page decides** ([choose-edit-mode](../choose-edit-mode/SKILL.md)). A page or snippet authored or edited in Studio Pro is changed with `alter`, however large the change. `describe` → edit → `create or modify` is only for pages your MDL scripts created and nobody has changed in Studio Pro since: on a Studio Pro page that round trip has dropped translations and filled an empty English caption from another language, and on a snippet it has dropped the snippet's type. ## Syntax ```sql alter page Module.PageName { operation1; operation2; ... }; alter snippet Module.SnippetName { operation1; operation2; ... }; ``` This is the **generic ALTER** — the same four verbs for every document type (`alter layout` too): ```sql alter page Module.PageName { set (Key: value, …) on ; -- no `on`: the page itself insert before|after|into { } replace with { } drop , ; }; ``` A `` on a page is a widget name, a DataGrid 2 column `grid column(Attr)` (or `grid column('Caption')`), or a layout region `layoutContainer.top`. Properties go in parentheses with `:`, exactly as in `create page`. The older spellings `set Key = value on w`, `set Key: value` (no parentheses) and `drop widget w` still run, and warn with MDL-DEPR101, MDL-DEPR102 and MDL-DEPR103 — write the form above. Multiple operations can be combined in a single ALTER statement. They are applied sequentially; later operations see the page state produced by earlier ones, so you can `set` on a widget you just `insert`ed. ```sql mdl 1; -- Rename a column, add a sibling, drop an obsolete one — all in one block. alter page MyMod.Product_Overview { set (caption: 'Product Name') on dgProducts column(Name); insert after dgProducts column(Lifecycle) { column (attribute: Sku, caption: 'SKU') }; drop dgProducts column('Old') }; ``` For changes that should be applied across **many pages** (e.g., "add `Class='card'` to every Container in `MyMod`"), use `UPDATE WIDGETS` instead — see `bulk-widget-updates`. ## Operations ### List View Specialization Templates A List View template has no name, so it cannot be reached by a widget ref like every other target. Adding one reuses `INSERT INTO` with the same `template for` block `create page` uses — a template has one spelling everywhere. Removing one has its own form: ```sql mdl 1; alter page Pages.Vehicle_Overview { insert into vehicleListView { template for Pages.Motorcycle { dynamictext mcLabel (content: 'Motorcycle {1}', contentparams: ({1} = Brand)) } }; drop template for Pages.SUV in vehicleListView }; ``` Naming the list view in the `drop` is required, not optional: one page can hold two list views with a template for the same entity. Most template edits need none of this. The widgets **inside** a template are ordinary named widgets, so `set (content: '…') on busLabel` and `insert after busLabel { … }` already work and land in the right template. To replace a whole template, `drop` it and `insert` the new one in the same block — operations apply in order. Refused, each naming the problem: - `insert before` / `insert after` a template — templates are not siblings of the widgets in the list view's body, so only `insert into` makes sense. - mixing `template for …` blocks with ordinary widgets in one `insert` — they go to different places (the Templates array and the default body). Use two inserts. - a template for an entity that is not the list view's entity or a specialization of it — it could never match an object the list view shows. - a second template for an entity that already has one. - `drop template for` an entity with no template — the error names the ones that are there, because dropping nothing and reporting success is how a typo becomes a silent no-op. ### SET - Modify Widget Properties ```sql -- Single property set (caption: 'New Caption') on widgetName -- Multiple properties set (caption: 'Save & Close', buttonstyle: success) on btnSave -- Page-level property (no ON clause). Page-level property names are -- case-sensitive and must match the Mendix property exactly. set (Title: 'New Page Title') -- Pop-up dimensions (apply when the page is opened in a pop-up) set (PopupWidth: 800) set (PopupHeight: 480) set (PopupResizable: true) set (Documentation: 'What this page is for.') -- Retarget a button's on-click action. Any form `create page` accepts works -- here, including the combined ones. set (Action: call microflow Module.ACT_Other) on btnSave set (Action: SAVE CHANGES CLOSE PAGE) on btnSave set (Action: SHOW PAGE Module.DetailPage) on btnEdit -- Retarget ONE named action slot of a pluggable widget, by the widget's own -- property key (the same key `create page` takes: `createFileAction: …`). set ('createFileAction': call microflow Module.ACT_CreateFile) on fileUploader1 set ('onSelectionChange': show page Module.Detail) on dgOrders -- Rebind a data-bound widget set (DataSource: $OrderParam) on dvOrder set (DataSource: microflow Module.MF_Get) on dvOrder ``` **Prefer `set Action` over `replace` when only the action changes.** `replace` rebuilds the widget from what the statement says, so any property you do not restate — `ButtonStyle`, `Class`, design properties, tooltip — is dropped. `set` edits the one property and leaves the rest of the widget alone. The exception is a **pluggable widget replaced by one of the same kind** (a combo box by a combo box): there `replace` keeps every stored property the statement does not change — a translated placeholder, `readOnlyStyle`, anything MDL has no word for — and writes only what differs from the widget as `describe` prints it. So a sort can be added to a combo box's options by restating its `describe` line with `sort by` appended. `set Action` is refused on a widget that has no action (a plain container, say), rather than writing a property the widget type does not define — Studio Pro refuses to open a document with an unknown property while MxBuild tolerates it, so a silent write would build cleanly and then fail to open. **Supported SET properties:** | Property | Widget Types | Value Type | Example | |----------|-------------|------------|---------| | `Action` | Widgets with an on-click action (ACTIONBUTTON, LINKBUTTON, clickable containers) | Any `create page` action expression | `set (Action: call microflow M.ACT_Go) on btnSave` | | `''` | Pluggable widgets — any **action-typed** property (File Uploader `createFileAction`, DataGrid 2 `onSelectionChange`, …) | Any `create page` action expression | `set ('createFileAction': call microflow M.ACT_Create) on fileUploader1` — refused, naming the widget's action slots, if the key is not action-typed | | `caption` | ACTIONBUTTON, LINKBUTTON | String | `set (caption: 'Submit') on btnSave` | | `content` | DYNAMICTEXT | String | `set (content: 'New Heading') on txtTitle` | | `label` | TEXTBOX, TEXTAREA, DATEPICKER, COMBOBOX, CHECKBOX, RADIOBUTTONS | String | `set (label: 'full Name') on txtName` | | `buttonstyle` | ACTIONBUTTON, LINKBUTTON | Primary, Default, Success, Danger, Warning, Info | `set (buttonstyle: danger) on btnDelete` | | `class` | Any widget | CSS class string | `set (class: 'card mx-2') on container1` | | `style` | Any widget (see warning below) | Inline CSS string | `set (style: 'padding: 16px;') on container1` | | `editable` | Input widgets | String | `set (editable: 'Never') on txtReadOnly` | | `visible` | Any widget | String or Boolean | `set (visible: false) on txtHidden` | | `Name` | Any widget | String | `set (Name: 'newName') on oldName` | | `Title` | Page-level only (case-sensitive) | String | `set (Title: 'Edit Customer')` | | `Documentation` | Page-level only (case-sensitive) | String (`''` clears) | `set (Documentation: 'Coordinator triage step.')` | | `layout` | Page-level only | Qualified name | `set layout = Atlas_Core.Atlas_Default` | | `PopupWidth` | Page-level only (case-sensitive) | Positive integer (pixels) | `set (PopupWidth: 800)` | | `PopupHeight` | Page-level only (case-sensitive) | Positive integer (pixels) | `set (PopupHeight: 480)` | | `PopupResizable` | Page-level only (case-sensitive) | Boolean | `set (PopupResizable: true)` | | `Class` | Page-level (case-sensitive, no ON) | CSS class string | `set (Class: 'container-fluid bg-light')` | | `Style` | Page-level (case-sensitive, no ON) | Inline CSS string | `set (Style: 'min-height: 100vh')` | | `Visible` (conditional) | Any widget | expression | `set (Visible: $currentObject/Name != '') on ctnDetails` | | `Editable` (conditional) | Input widgets | expression | `set (Editable: $currentObject/Active) on txtName` | | `'quotedProp'` | Pluggable widgets | String, Boolean, Number | `set ('showLabel': false) on cbStatus` | > **Conditional visibility/editability** — `set (Visible: ) on widget` (and > `Editable`) attach a per-object client expression, stored as written: name > attributes as `$currentObject/Name`. The bracketed `set (Visible: [Name != ''])`, > which roots a bare attribute for you, still works and warns MDL-DEPR081. Setting > `Editable` on a non-input widget is rejected. This mirrors CREATE PAGE's > `visible:` — see the create-page skill for enum-value rules. **Pluggable widget properties** use quoted names to set values in the widget's `Object.Properties[]`. Boolean values are stored as `"yes"`/`"no"` in BSON. **Column property names are case-insensitive** in MDL — `set (caption: …)` and `set (Caption: …)` both work. The internal BSON keys are dictated by the widget schema and stay case-sensitive on the storage side. > **Warning: Style on DYNAMICTEXT** — Setting `style` directly on a DYNAMICTEXT widget crashes MxBuild with a NullReferenceException. Wrap the DYNAMICTEXT in a CONTAINER and apply styling to the container instead: > ```sql > -- Wrong: crashes MxBuild > SET (Style: 'color: red;') ON txtHeading > > -- Correct: style the container > REPLACE txtHeading WITH { > CONTAINER ctnHeading (Style: 'color: red;') { > DYNAMICTEXT txtHeading (Content: 'Heading', RenderMode: H2) > } > } > ``` #### Changing a widget's DataSource `SET DataSource` retypes a data source in place — including across shapes, e.g. from a microflow to a page parameter: ```sql mdl 1; ALTER PAGE MyModule.OrderPage { SET (DataSource: $Order) ON dvOrder; -- page/snippet parameter SET (DataSource: microflow MyModule.MF_Get) ON dvOrder; -- microflow SET (DataSource: nanoflow MyModule.NF_Get) ON dvOrder; -- nanoflow SET (DataSource: selection dgOrders) ON dvDetail; -- listen to widget }; ``` The parameter must exist on the page (or snippet) being altered — its entity is read from the container's own parameter list, and an unknown name is refused rather than written as an unresolved reference. `association` and `database` sources are **not** supported by SET. Use REPLACE for those, which rebuilds the widget through the CREATE PAGE path and handles every datasource type; the error message says so. A `database` source has no single stored shape — the widget holding it decides which element Mendix writes (a list view, a data grid and a pluggable widget each store a different one), and SET writes the property directly rather than rebuilding the widget, so it has nothing to choose from. This used to be accepted and half-applied: the widget was left with a source that DESCRIBE read back as absent and mxbuild rejected as **CE7007**, on a page `exec` had just reported as altered (mendixlabs/mxcli#1032). A **data view** is the one case REPLACE does not rescue: it binds to a single object, so Mendix gives it no database form at all and the CREATE PAGE path refuses one too. Point it at a context parameter, a microflow, a nanoflow or `selection `, and use a list view or a data grid to show a query. The refusal says which of the two situations you are in. ### INSERT - Add Widgets ```sql -- Insert after a widget insert after txtName { textbox txtMiddleName (label: 'Middle Name', attribute: MiddleName) } -- Insert before a widget insert before btnSave { actionbutton btnPreview (caption: 'Preview', action: call microflow Module.ACT_Preview) } -- Insert INTO a container — append as its last child (works on an EMPTY container) insert into ctnToolbar { actionbutton btnNew (caption: 'New', action: nothing, buttonstyle: primary) } ``` Inserted widgets use the same syntax as `create page`. Multiple widgets can be inserted in a single block. `insert into ` appends as the last child of the named container — the only way to fill an **empty** container, and handy for adding to a container/dataview without needing a sibling to anchor to. Widgets inserted into a dataview take that dataview's entity as their context. Supported on simple containers (container, dataview, groupbox, tab page, scroll-container region); for a layout grid, insert relative to a widget inside the target column instead. **Adding a tab:** `insert into { tabpage … }` appends a tab page, and `insert after|before { tabpage … }` places it next to that sibling. A tab page cannot go next to an ordinary widget or inside another tab page, and one insert cannot mix tab pages and widgets. Dropping or reordering tab pages still needs `create or modify page`. **The context comes from the nearest enclosing data source, whatever kind it is** — a database or association source, a microflow/nanoflow source (the entity is the flow's return type), or `datasource: selection `, which takes the entity of the list it listens to. A bare attribute in the inserted or replaced widget resolves against that entity, exactly as it would in `create page`. When no enclosing source can be resolved, the binding is written unset rather than guessed at — `describe page` then prints ``, and mxbuild reports `CE0402 "No value specified."`, so re-describe the page after an ALTER that moves data-bound widgets. ### DROP - Remove Widgets ```sql -- Drop a single widget drop txtUnused -- Drop multiple widgets drop txtOldField, lblOldLabel, container2 ``` Removes widgets and their entire subtree from the page. ### REPLACE - Replace Widget Subtree ```sql mdl 1; -- Replace a data view's footer (it has no name: address it by its data view) alter page MyModule.Customer_Edit { replace dvMain.footer with { footer { actionbutton btnSave (caption: 'Save', action: save changes, buttonstyle: primary) actionbutton btnCancel (caption: 'Cancel', action: cancel changes) } } }; ``` Replaces the target widget with one or more new widgets. The new widgets use the same syntax as `create page`, and may reuse the names of the widgets the replace removes. `insert into dvMain.footer { … }` appends to a footer and `drop dvMain.footer` empties it. ### DataGrid Column Operations A DataGrid 2 column is addressed by what `describe page` prints in it: `grid column(Attr)` for its `attribute:`, `grid column('Caption')` for its `caption:`. See "DataGrid 2 columns: how to address them" below. ```sql -- SET a column property set (caption: 'Product SKU') on dgProducts column(Code) -- DROP a column drop dgProducts column('Old column') -- INSERT a column after an existing one insert after dgProducts column(Price) { column (attribute: Margin, caption: 'Margin') } -- REPLACE a column replace dgProducts column(Description) with { column (attribute: Notes, caption: 'Notes') } -- Two columns over one attribute: pick one with @n drop dgProducts column(Name)@2 ``` The older dotted form `gridName.columnName` still works; it matches a name mxcli derives (the short attribute name, else the sanitized caption, else `colN`). ### ADD Variables - Add a Page Variable ```sql add variables $showStockColumn: boolean = 'true' ``` Adds a new page variable (`Forms$LocalVariable`) to the page/snippet. DataType can be `boolean`, `string`, `integer`, `decimal`, `datetime`, or an entity type. Default value is a Mendix expression in single quotes. ### DROP Variables - Remove a Page Variable ```sql drop variables $showStockColumn ``` Removes a page variable by name. ### SET Layout - Change Page Layout ```sql -- Auto-map placeholders by name (most common case) set layout = Atlas_Core.Atlas_Default -- Explicit mapping when placeholder names differ set layout = Atlas_Core.Atlas_SideBar map (Main as content, Extra as Sidebar) ``` Changes the page's layout without rebuilding the widget tree. Only rewrites the `FormCall.Form` and `FormCall.Arguments[].Parameter` BSON fields — all widget content is preserved. Not supported for snippets. When placeholders have the same names in both layouts (e.g., both have `Main`), auto-mapping works. Use `map` when placeholder names differ between the old and new layout. ## Examples ### Change button text and style ```sql mdl 1; alter page MyModule.Customer_Edit { set (caption: 'Save & Close', buttonstyle: success) on btnSave }; ``` ### Add a field to a form ```sql mdl 1; alter page MyModule.Customer_Edit { insert after txtEmail { textbox txtPhone (label: 'Phone', attribute: Phone) } }; ``` ### Add a page variable for column visibility ```sql mdl 1; alter page MyModule.ProductOverview { add variables $showStockColumn: boolean = 'if (3 < 4) then true else false' }; ``` ### Remove unused fields and update title ```sql mdl 1; alter page MyModule.Customer_Edit { set (title: 'Edit Customer Details'); drop txtLegacyField, lblOldNote; set (label: 'Email Address') on txtEmail }; ``` ### Replace a footer section ```sql mdl 1; alter page MyModule.Customer_Edit { replace dvMain.footer with { footer { actionbutton btnSave (caption: 'Save', action: save changes, buttonstyle: success) actionbutton btnDelete (caption: 'Delete', action: delete, buttonstyle: danger) actionbutton btnCancel (caption: 'Cancel', action: cancel changes) } } }; ``` ### Modify a snippet ```sql mdl 1; alter snippet MyModule.NavigationMenu { set (caption: 'Dashboard') on btnHome; insert after btnHome { actionbutton btnReports (caption: 'Reports', action: show page MyModule.Reports_Overview) } }; ``` ### Set pluggable widget properties ```sql mdl 1; alter page MyModule.Customer_Edit { set ('showLabel': false) on cbStatus; set ('labelWidth': 4) on cbCategory }; ``` ## DataGrid 2 columns: how to address them, and what you can set **Mendix stores no column name.** A DataGrid 2 column's schema has no name or identifier key — the only human-facing label is its caption — so a column is written without one, and `describe page` prints none: ```mdl create or modify page Mod.P (...) { datagrid dg1 (datasource: database Mod.Item) { column (attribute: Label, caption: 'The Label') column (caption: 'Actions', ShowContentAs: customContent) { … } } }; ``` A name written there anyway (`column colLabel (…)`, the old describe output) is dropped and reported as **MDL-DEPR005**; `mxcli fmt --upgrade` removes it. `ALTER PAGE` addresses a column by **what describe prints in it** — its `attribute:` value, or its `caption:`: ```mdl mdl 1; alter page Mod.P { set (Caption: 'Renamed') on dg1 column(Label) }; -- the column bound to Label alter page Mod.P { drop dg1 column('Actions') }; -- the column captioned Actions alter page Mod.P { set (Sortable: false) on dg1 column(Owner/Name) }; -- over an association, as describe writes it ``` Two columns over the same attribute (or with the same caption) share the address. ALTER refuses it rather than picking one, and the error lists the matches — add `@n` to choose: `drop dg1 column(FullName)@2`. The older `dg1.Label` form still works: it addresses a column by a name mxcli derives (the attribute's short name, else the sanitized caption, else `colN` by position). Prefer `column(…)`, which says what it matches. ### Setting column properties Property names resolve against the keys the installed widget declares, so both the schema key and mxcli's MDL alias work (`DynamicCellClass` and `ColumnClass` both reach `columnClass`). An unknown name lists what *is* settable on that grid. **`DynamicCellClass` (and a widget's `DynamicClasses`) take a Mendix expression, written as-is.** A quoted value is a Mendix string, so a literal CSS class is just the quoted class name, and a computed one is the expression itself: ```mdl mdl 1; -- a literal class: the string 'highlight' alter page Mod.P { SET (DynamicCellClass: 'highlight') ON dg1 column(Label) }; -- a computed class alter page Mod.P { SET (DynamicCellClass: if $currentObject/Price > 100 then 'highlight' else '') ON dg1 column(Label) }; ``` ```text -- WRONG: a bare name is an identifier, not a string — mxbuild reports CE0117 alter page Mod.P { SET (DynamicCellClass: highlight) ON dg1 column(Label) }; ``` The old spelling — the expression's text in quotes, `'if … then ''a'' else '''''` — is refused under `mdl 1;` as MDL-WIDGET33, because there it stores that text as a class name. A script without the header keeps its old meaning and warns MDL-V1-QUOTEDEXPR; `mxcli fmt --upgrade` writes it bare. This applies equally to `create page`; the two paths behave identically. A column's pluggable `Visible` expression is not converted yet: there a quoted value is still the expression's text, so a literal needs the doubled quotes. Properties holding a **structured** value — `attribute`, `filter`, `content`, actions — cannot be set by ALTER at all. It refuses them and points at `create or modify page`, rather than writing a string where Mendix expects a reference. **Widget property names are matched case-insensitively**, pluggable ones included, so a spelling `CREATE PAGE` accepts is a spelling `ALTER PAGE` accepts — `set (PageSize: 10) on dgProducts` and `set (pageSize: 10) on dgProducts` are the same statement. This is what makes DESCRIBE output re-executable: `describe page` prints the capitalised `PageSize:`, while the widget template stores `pageSize` (mendixlabs/mxcli#1069). A property the widget does not declare is still an error — and `mxcli check … --references` reports it **before** the script runs, so a typo no longer lands halfway through. The pre-flight resolves the name against the stored document rather than a list, so it is right about whatever widget package this project has installed; the error names the widget's own property keys. `ON` a widget the page does not have is caught the same way. Two things it deliberately stays quiet about, because it cannot answer them: a page the script itself creates (nothing is stored yet — the widgets there are checked where they are written), and a widget an `INSERT` in the same script adds. Both still fail at exec if they are genuinely wrong. ## Common Mistakes | Mistake | Fix | |---------|-----| | Missing `on widgetName` for widget SET | Add `on widgetName` (only page-level properties — `Title`, `Documentation`, `PopupWidth`, `PopupHeight`, `PopupResizable`, `Class`, `Style` — omit ON) | | `unsupported page-level property: title` | Page-level property names are case-sensitive — use `Title`, `PopupWidth`, `PopupHeight`, `PopupResizable`, `Class`, `Style` | | Using unquoted pluggable property names | Quote pluggable props: `set ('showLabel': false) on cb` | | `pluggable property "X" not found` | The widget does not declare it — casing is not the problem (any casing resolves). The error lists the keys it does declare; `describe widget type ` or `describe page` shows them in context. Run `mxcli check … --references` to get this before the script runs | | Wrong widget name | Use `describe page Module.Name` to see widget names | | SET on non-existent widget | Widget names are case-sensitive; check with DESCRIBE | | Missing semicolons between operations | Each operation inside `{ }` ends with `;` | ## Limitations — prefer binding at page creation (ledger finding #45) `ALTER PAGE` is best for *content* edits (add/remove/retitle widgets). When you hit the limit below, define the referenced microflows **before** the page and bind the buttons at creation time instead of rewiring afterwards: 1. **`SET` cannot rewire a button's action.** `set` accepts a fixed property list (`caption`, `class`, `visible`, …) — `action` is not on it, so `set Action = call microflow … on btnSave` is a parse error. Set the button's action when the button is created (or `REPLACE` the button subtree). **A data view's footer has no name.** Its widgets are stored in the data view, and the footer itself is not, so a name written on it is never kept (MDL-DEPR005) and `describe` prints `footer { … }`. Address it by its data view: `replace dvMain.footer with { footer { … } }`, `insert into dvMain.footer { … }`, `drop dvMain.footer`. **Recommended pattern**: put save/reset microflows in a file that runs *before* the page definition, and bind the popup/footer buttons to them at creation. The apparent "page needs microflow, microflow needs page" cycle usually exists only between *different* microflows, not within the page itself. ## Validation Checklist 1. **Get widget names first**: Run `describe page Module.PageName` to see all widget names 2. **Check syntax**: `mxcli check script.mdl` 3. **Check references**: `mxcli check script.mdl -p app.mpr --references` 4. **Verify result**: Run `describe page Module.PageName` after ALTER to confirm changes 5. **Validate project**: `mxcli docker check -p app.mpr` (or `mxcli docker check -p app.mpr`) ## Related Commands - `describe page Module.PageName` - View current page structure (get widget names) - `describe snippet Module.SnippetName` - View current snippet structure - `create [or replace] page` - Create or fully rebuild a page - `create [or replace] snippet` - Create or fully rebuild a snippet - `update widgets set ... where ...` - Bulk update widget properties across pages - `drop page Module.PageName` - Delete a page - `drop snippet Module.SnippetName` - Delete a snippet ## Related Skills - [Create Page](../create-page/SKILL.md) - Full page creation syntax - [Overview Pages](../overview-pages/SKILL.md) - CRUD page patterns - [Master-Detail Pages](../master-detail-pages/SKILL.md) - Selection binding pattern