Build Mendix Pluggable Widgets with Gleam — no JSX required.
Write React components entirely in Gleam and run them as Mendix Studio Pro widgets. React bindings are provided by redraw/redraw_dom, Mendix API bindings by mendraw, and build tools + JS interop by glendix.
- Static type safety — Gleam's robust type system catches runtime errors at compile time
- Immutable data — Predictable state management
- JavaScript target support —
gleam build --target javascriptoutputs ES modules - glendix + mendraw packages — Type-safe Gleam bindings for React + Mendix API + JS Interop, supporting
EditableValue,ActionValue,ListValueand all other Mendix Pluggable Widget API types
src/
mendix_widget_gleam.gleam # Main widget module
editor_config.gleam # Studio Pro property panel configuration
editor_preview.gleam # Studio Pro design view preview
components/
hello_world.gleam # Shared Hello World component
MendixWidget.xml # Widget property definitions
package.xml # Mendix package manifest
package.json # npm dependencies (React, external libraries, etc.)
gleam.toml # Includes glendix >= 4.0.5 + mendraw dependency
React/Mendix FFI and JS Interop bindings are not included in this project — they are provided by the glendix and mendraw Hex packages.
Widget code (.gleam) + glendix package (Hex)
| gleam run -m glendix/build (Gleam compilation runs automatically)
ES modules (.mjs) — build/dev/javascript/...
| Bridge JS (auto-generated) handles imports
| Rollup (pluggable-widgets-tools)
.mpk widget package — dist/
The Gleam function fn(JsProps) -> Element has an identical signature to a React functional component. React bindings are provided by redraw / redraw_dom, while mendraw provides Mendix runtime type accessors and glendix provides JS interop, so widget projects need only focus on business logic.
// src/mendix_widget_gleam.gleamimportmendraw/mendix.{typeJsProps}importredraw.{typeElement}importredraw/dom/attributeimportredraw/dom/htmlpubfnwidget(props:JsProps)->Element{letsample_text=mendix.get_string_prop(props,"sampleText")html.div([attribute.class("widget-hello-world")],[html.text("Hello "<>sample_text),])}Mendix complex types can also be used in a type-safe manner from Gleam:
importmendraw/mendiximportmendraw/mendix/editable_valueimportmendraw/mendix/actionpubfnwidget(props:JsProps)->Element{// Access EditableValueletname_attr:EditableValue=mendix.get_prop_required(props,"name")letdisplay=editable_value.display_value(name_attr)// Execute ActionValueleton_save:Option(ActionValue)=mendix.get_prop(props,"onSave")action.execute_action(on_save)// ...}- Gleam (v1.0+)
- Node.js (v16+)
- Mendix Studio Pro (for widget testing)
gleam run -m glendix/install # Auto-downloads Gleam deps + installs npm deps + generates binding code (external React packages must be installed manually beforehand)gleam run -m glendix/build # Gleam compilation + widget build (.mpk output)Build artefacts are output as .mpk files in the dist/ directory.
gleam run -m glendix/dev # Gleam compilation + dev server (HMR, port 3000)
gleam run -m glendix/start # Linked development with Mendix test projectAll commands are unified under gleam. gleam run -m automatically compiles Gleam before running the script.
| Command | Description |
|---|---|
gleam run -m glendix/install | Install dependencies (Gleam + npm) + generate binding code |
gleam run -m glendix/build | Production build (.mpk output) |
gleam run -m glendix/dev | Development server (HMR, port 3000) |
gleam run -m glendix/start | Linked development with Mendix test project |
gleam run -m glendix/lint | Run ESLint |
gleam run -m glendix/lint_fix | ESLint auto-fix |
gleam run -m glendix/release | Release build |
gleam run -m mendraw/marketplace | Search/download Mendix Marketplace widgets |
gleam run -m glendix/define | Widget property definition TUI editor |
gleam build --target javascript | Gleam to JS compilation only |
gleam test | Run Gleam tests |
gleam format | Format Gleam code |
React component libraries distributed as npm packages can be used from pure Gleam without writing any .mjs FFI files.
npm install rechartsAdd a [tools.glendix.bindings] section to your gleam.toml:
[tools.glendix.bindings.recharts]
components = ["PieChart", "Pie", "Cell", "Tooltip", "ResponsiveContainer"]gleam run -m glendix/installA binding_ffi.mjs file is generated automatically. It is also regenerated on subsequent builds via gleam run -m glendix/build.
importglendix/bindingimportmendraw/interopimportredraw.{typeElement}importredraw/dom/attribute.{typeAttribute}fnm(){binding.module("recharts")}pubfnpie_chart(attrs:List(Attribute),children:List(Element))->Element{interop.component_el(binding.resolve(m(),"PieChart"),attrs,children)}pubfntooltip(attrs:List(Attribute))->Element{interop.void_component_el(binding.resolve(m(),"Tooltip"),attrs)}External React components follow the same calling pattern as html.div.
- Gleam — Widget logic and UI (compiled to JavaScript target)
- glendix — Build tools + JS Interop Gleam bindings (Hex package)
- mendraw — Mendix API Gleam bindings (Hex package)
- React 19 — Mendix Pluggable Widget runtime
- Rollup — Bundling via
@mendix/pluggable-widgets-tools
- The Gleam to JS to Mendix Widget pipeline is not an officially supported combination; build configuration customisation may be required
- JSX files are not used — all React logic is implemented via Gleam + glendix
- React bindings use redraw/redraw_dom via glendix — other Gleam React libraries are not used
- FFI files are not written directly in the widget project — React/Mendix FFI is provided by glendix
Apache License 2.0 — see LICENSE