Mendix Fragments Skill

SkillDev tools

Lets your agent define reusable widget groups once and place them on multiple pages instead of rebuilding them each time.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Mendix Fragments Skill skill

About this capability

Define reusable widget groups with DEFINE FRAGMENT and place them with USE FRAGMENT. Use when the same widget pattern repeats across pages and should be written once.

What this skill tells your AI

The instructions your AI receives, as published by mendixlabs/mxcli in .claude/skills/mendix/fragments/SKILL.md and read by ahel’s review.

When to Use This Skill

Use this skill when:

  • Defining reusable widget groups with define fragment
  • Inserting fragments into pages or snippets with use fragment
  • Listing or inspecting fragments with show fragments / describe fragment
  • Building multiple pages that share common widget patterns (footers, form fields, buttons)
  • Avoiding copy-paste of repeated widget structures across pages

What Are Fragments?

Fragments are script-scoped, transient widget groups:

  • Defined once, reused in multiple pages/snippets within the same script
  • Not persisted in the MPR file — they exist only during script execution
  • Widgets are deep-cloned on expansion (each USE gets independent copies)
  • Optional prefix support to avoid name conflicts when using the same fragment multiple times

Syntax Reference

DEFINE FRAGMENT

define fragment SaveCancelFooter as {
  footer footer1 {
    actionbutton btnSave (caption: 'Save', action: save_changes, buttonstyle: primary)
    actionbutton btnCancel (caption: 'Cancel', action: cancel_changes)
  }
};

Multiple top-level widgets:

define fragment CustomerFields as {
  textbox txtName (label: 'Name', attribute: Name)
  textbox txtEmail (label: 'Email', attribute: Email)
  textbox txtPhone (label: 'Phone', attribute: Phone)
};

USE FRAGMENT

Inside a page or snippet body:

create page Module.CustomerEdit
(
  params: { $Customer: Module.Customer },
  title: 'Edit Customer',
  layout: Atlas_Core.PopupLayout
)
{
  dataview dvCustomer (datasource: $Customer) {
    use fragment CustomerFields
    use fragment SaveCancelFooter
  }
};

With prefix (avoids name conflicts):

use fragment SaveCancelFooter as order_
-- Creates: order_footer1, order_btnSave, order_btnCancel

Content Slots — wrap arbitrary content

A plain fragment substitutes a fixed widget group. A content slot lets a fragment wrap arbitrary caller-supplied content — a reusable shell (a card, panel, or section) whose body varies per use. Declare a slot where the caller's widgets should land, then fill it with the use fragment X { … } payload form:

define fragment Card as {
  container cardWrap (class: 'card', designproperties: ['Card style': on]) {
    container cardBody (class: 'card-body') {
      slot content            -- caller's widgets are spliced in here
    }
  }
};

create page Module.Dashboard (title: 'Dashboard', layout: Atlas_Core.Atlas_Default) {
  use fragment Card {
    dynamictext cardHeading (content: 'Welcome', rendermode: H2)
    dynamictext cardText (content: 'Any widgets can go inside the reusable Card shell')
  }
};

Rules (v1):

  • The slot name is optional and defaults to content; a fragment supports one slot.
  • Using a slotted fragment with no payload (use fragment Card) expands the slot to nothing — a valid empty shell.
  • Supplying a payload to a fragment that declares no slot is an error.
  • The payload is deep-cloned; as prefix_ still renames the fragment's own widgets (not the caller's payload).
  • The slot resolves at expansion — describe page shows the fully-expanded tree (no slot marker), and there are no BSON/round-trip surprises.

For varying a leaf value (a label or attribute name) rather than wrapping a subtree, scalar params (define fragment F($label, $attr) as …) are a planned v1.1 follow-up; today use a slot plus a one-line value fill.

Parameter bindings — datasource & action (experimental)

The content slot varies structure (which widgets). Typed parameters vary data and behavior: a fragment can declare a datasource and/or an action parameter, reference it with $name in a datasource/action position, and receive its value at the use site. This turns a shell into a real reusable component — one panel bound to a different entity and a different handler per use.

define fragment DataPanel($data: datasource, $onEdit: action) as {
  container panelWrap (class: 'card') {
    listview lvItems (datasource: $data) {
      slot content
      actionbutton btnEdit (caption: 'Edit', action: $onEdit, buttonstyle: primary)
    }
  }
};

create page Module.Orders (title: 'Orders', layout: Atlas_Core.Atlas_Default) {
  use fragment DataPanel ($data: database Module.Order, $onEdit: microflow Module.EditOrder) {
    dynamictext panelHeading (content: 'Orders', rendermode: H4)
  }
};

Rules:

  • Param kinds are datasource ($var / database E / $currentObject/Assoc / microflow M) and action (a microflow / nanoflow / save_changes / show_page / …).
  • Every declared parameter must be supplied; unknown args and type mismatches are errors.
  • Values substitute at expansion — describe page shows the concrete datasource/action, no $param.

Building blocks can't declare params (they're authored in Studio Pro), but a use building block accepts rebind overrides that rewrite the block's outermost datasource and/or its first button after the copy:

use building block Atlas_Web_Content.List_Cards
  (datasource: database Module.Order, action: microflow Module.OpenOrder) as orders_;

Binding-point rule (prototype): datasource → the first widget carrying a datasource; action → the first button widget. For anything more specific, copy the block in with as prefix_ and use alter page … set … on prefix_widget.

SHOW FRAGMENTS

show fragments;
-- Lists all defined fragments with widget counts

DESCRIBE FRAGMENT

describe fragment SaveCancelFooter;
-- Outputs the full MDL definition

Common Patterns

Pattern 1: Standard CRUD Footer

define fragment CrudFooter as {
  footer footer1 {
    actionbutton btnSave (caption: 'Save', action: save_changes, buttonstyle: primary)
    actionbutton btnCancel (caption: 'Cancel', action: cancel_changes)
  }
};

-- Use in every edit page
create page Module.Customer_Edit (...) {
  dataview dv (datasource: $Customer) {
    textbox txtName (label: 'Name', attribute: Name)
    use fragment CrudFooter
  }
};

create page Module.Order_Edit (...) {
  dataview dv (datasource: $Order) {
    textbox txtNumber (label: 'Order #', attribute: Number)
    use fragment CrudFooter
  }
};

Pattern 2: Form Field Groups

define fragment AddressFields as {
  textbox txtStreet (label: 'Street', attribute: Street)
  textbox txtCity (label: 'City', attribute: City)
  textbox txtZip (label: 'Zip Code', attribute: ZipCode)
  textbox txtCountry (label: 'Country', attribute: Country)
};

-- Reuse in customer and supplier pages
create page Module.Customer_Edit (...) {
  dataview dv (datasource: $Customer) {
    textbox txtName (label: 'Name', attribute: Name)
    use fragment AddressFields
    use fragment CrudFooter
  }
};

Pattern 3: Same Fragment with Prefix

define fragment ActionButtons as {
  actionbutton btnApprove (caption: 'Approve', action: save_changes, buttonstyle: success)
  actionbutton btnReject (caption: 'Reject', action: cancel_changes, buttonstyle: danger)
};

create page Module.DualPanel (...) {
  layoutgrid lg {
    row row1 {
      column col1 (desktopwidth: 6) {
        use fragment ActionButtons as left_
      }
      column col2 (desktopwidth: 6) {
        use fragment ActionButtons as right_
      }
    }
  }
};

Common Mistakes

Duplicate Fragment Names

-- WRONG: Defining the same fragment name twice causes an error
define fragment footer as { ... };
define fragment footer as { ... };  -- Error: fragment "Footer" already defined

Missing Fragment

-- WRONG: Using a fragment that hasn't been defined
create page Module.MyPage (...) {
  use fragment NonExistent   -- Error: fragment "NonExistent" not found
};

Name Conflicts Without Prefix

-- WRONG: Using same fragment twice without prefix creates duplicate widget names
use fragment footer
use fragment footer   -- Widget name "footer1" already exists!

-- CORRECT: Use prefix for uniqueness
use fragment footer as first_
use fragment footer as second_

Fragment Order

-- WRONG: Using a fragment before defining it
create page Module.MyPage (...) {
  use fragment footer   -- Error: fragment "Footer" not found
};
define fragment footer as { ... };

-- CORRECT: Define before use
define fragment footer as { ... };
create page Module.MyPage (...) {
  use fragment footer   -- OK
};

Validation Checklist

  • All define fragment statements appear before their use fragment references
  • No duplicate fragment names in the script
  • Prefix used when the same fragment appears multiple times on one page
  • Fragment widget names don't conflict with other widgets on the page
  • All widgets inside fragments use valid syntax (same as page bodies)
  • A use fragment X { … } payload is only supplied when fragment X declares a slot
  • A slotted fragment has exactly one slot (v1 supports a single slot)

Related Documentation

  • mxcli syntax fragment — CLI help topic
  • create-page — Page/widget syntax reference
  • overview-pages — CRUD page patterns
  • Proposal: docs/11-proposals/proposal_page_composition.md

Signals

GitHub stars
122
Forks
49
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
fragments
Source
github.com/mendixlabs/mxcli