English | 한국어 | 日本語

glendix

Hello! This is glendix and it’s ever so brilliant! It’s a Gleam library that talks to React 19 and Mendix Pluggable Widgets.

You can write proper Mendix widgets using only Gleam — no JSX needed at all, how lovely is that!

What’s New in v2.0

Right, so v2.0 is loads better now! We had a good look at this clever project called redraw and learnt quite a lot from it. redraw is a really proper React binding library for Gleam with lovely type safety and tidy modules. But since glendix is specially made for Mendix Pluggable Widgets, we didn’t copy all of redraw’s fancy SPA bits (like bootstrap/compose and jsx-runtime) — we just took the helpful parts!

What’s Changed Then

How to Put It In Your Project

Pop this into your gleam.toml:

# gleam.toml
[dependencies]
glendix = { path = "../glendix" }

You’ve got to use a local path for now — it’s not on Hex yet, sorry!

Peer Dependencies

Your widget project’s package.json needs these as well:

{
  "dependencies": {
    "react": "^19.0.0",
    "big.js": "^6.0.0"
  }
}

Let’s Get Started!

Here’s a dead simple widget — look how short it is!

import glendix/mendix
import glendix/react.{type JsProps, type ReactElement}
import glendix/react/attribute
import glendix/react/html

pub fn widget(props: JsProps) -> ReactElement {
  let name = mendix.get_string_prop(props, "sampleText")
  html.div([attribute.class("my-widget")], [
    react.text("Hello " <> name),
  ])
}

fn(JsProps) -> ReactElement — that’s literally all a Mendix Pluggable Widget needs. Easy peasy!

All the Modules

React Bits

ModuleWhat It Does
glendix/reactThe main important bits — types like ReactElement, JsProps, Component, Promise, plus element, fragment, keyed, text, none, when, when_some, Context stuff, define_component, memo (uses Gleam’s own equality checking, which is dead clever), flush_sync
glendix/react/attributeAttribute type + 108+ HTML attribute functions — class, id, style, popover, fetch_priority, enter_key_hint, microdata, Shadow DOM, and loads more
glendix/react/hook40 React Hooks! — use_state, use_effect, use_layout_effect, use_insertion_effect, use_memo, use_callback, use_ref, use_reducer, use_context, use_id, use_transition, use_async_transition, use_deferred_value, use_optimistic/use_optimistic_, use_imperative_handle, use_lazy_state, use_sync_external_store, use_debug_value, use_promise (that’s React.use!), use_form_status
glendix/react/refRef helpers — current and assign (kept separate from hooks so it’s tidy)
glendix/react/event16 event types + 154+ handlers (including capture phase and transition events!) + 82+ accessors
glendix/react/html85+ HTML tags — div, span, input, details, dialog, video, ruby, kbd, search, meta, script, object, and so on (pure Gleam, no FFI!)
glendix/react/svg58 SVG elements — svg, path, circle, 16 filter primitives, discard, and more (pure Gleam, no FFI!)
glendix/react/svg_attribute97+ SVG attribute functions — view_box, fill, stroke, markers, filter bits, etc. (pure Gleam, no FFI!)
glendix/bindingFor using other people’s React components — just write bindings.json and you’re sorted, no .mjs needed!
glendix/widgetFor using .mpk widgets from the widgets/ folder as React components — brilliant!
glendix/classicClassic (Dojo) widget wrapper — classic.render(widget_id, properties) — for the older widgets
glendix/marketplaceSearch and download widgets from the Mendix Marketplace — gleam run -m glendix/marketplace

Mendix Bits

ModuleWhat It Does
glendix/mendixThe core Mendix types (ValueStatus, ObjectItem) + how to get things from JsProps (get_prop, get_string_prop)
glendix/mendix/editable_valueFor values you can change — value, set_value, set_text_value, display_value
glendix/mendix/actionFor doing actions — can_execute, execute, execute_if_can
glendix/mendix/dynamic_valueFor read-only values (expression attributes and that)
glendix/mendix/list_valueLists of data — items, set_filter, set_sort_order, reload
glendix/mendix/list_attributeTypes that go with lists — ListAttributeValue, ListActionValue, ListWidgetValue
glendix/mendix/selectionFor picking one thing or lots of things
glendix/mendix/referenceSingle association (ReferenceValue) — like pointing to one friend
glendix/mendix/reference_setMultiple associations (ReferenceSetValue) — like pointing to a whole group of friends!
glendix/mendix/dateA wrapper for JS Date (months go from 1 in Gleam to 0 in JS automatically — clever!)
glendix/mendix/bigBig.js wrapper for really precise numbers (compare gives you a proper gleam/order.Order)
glendix/mendix/fileFileValue and WebImage
glendix/mendix/iconWebIcon — Glyph, Image, IconFont
glendix/mendix/formatterValueFormatterformat and parse
glendix/mendix/filterFilterCondition builder — and_, or_, equals, contains, attribute, literal
glendix/editor_configEditor helpers — hiding attributes, making tabs, reordering things (works with Jint!)

Examples

Attribute Lists

This is how you make a button with attributes — it’s like a shopping list!

import glendix/react/attribute
import glendix/react/event
import glendix/react/html

html.button(
  [
    attribute.class("btn btn-primary"),
    attribute.type_("submit"),
    attribute.disabled(False),
    event.on_click(fn(_event) { Nil }),
  ],
  [react.text("Submit")],
)

And if you only want an attribute sometimes, use attribute.none() — it’s like saying “actually, never mind”:

html.input([
  attribute.class("input"),
  case is_error {
    True -> attribute.class("input-error")
    False -> attribute.none()
  },
])

useState + useEffect

Here’s a counter! Every time you press the button, the number goes up by one — magic!

import gleam/int
import glendix/react
import glendix/react/attribute
import glendix/react/event
import glendix/react/hook
import glendix/react/html

pub fn counter(_props) -> react.ReactElement {
  let #(count, set_count) = hook.use_state(0)

  hook.use_effect_once(fn() {
    // Runs once when it first appears
    Nil
  })

  html.div_([
    html.button(
      [event.on_click(fn(_) { set_count(count + 1) })],
      [react.text("Count: " <> int.to_string(count))],
    ),
  ])
}

useLayoutEffect (Measuring the Page)

This one runs right after the page changes but before you can see it — it’s dead quick!

import glendix/react/hook

let ref = hook.use_ref(0.0)

hook.use_layout_effect_cleanup(
  fn() {
    // Measure things here
    fn() { Nil }  // tidy up after yourself
  },
  [some_dep],
)

Reading and Writing Mendix Values

Here’s how you get values out of Mendix and do things with them:

import gleam/option.{None, Some}
import glendix/mendix
import glendix/mendix/editable_value as ev

pub fn render_input(props: react.JsProps) -> react.ReactElement {
  case mendix.get_prop(props, "myAttribute") {
    Some(attr) -> {
      let display = ev.display_value(attr)
      let editable = ev.is_editable(attr)
      // ...
    }
    None -> react.none()
  }
}

Showing Things Sometimes

Sometimes you want to show something only when a condition is true — here’s how!

import glendix/react
import glendix/react/html

// When something is True
react.when(is_visible, fn() {
  html.div_([react.text("Visible!")])
})

// When you've got a Some value
react.when_some(maybe_user, fn(user) {
  html.span_([react.text(user.name)])
})

Using Other People’s React Components (Bindings)

You can use React libraries from npm without writing any .mjs files yourself — isn’t that ace!

1. Make a bindings.json file:

{
  "recharts": {
    "components": ["PieChart", "Pie", "Cell", "Tooltip", "Legend"]
  }
}

2. Install the package — whatever’s in bindings.json needs to be in node_modules:

npm install recharts

3. Run gleam run -m glendix/install (it makes the bindings for you!)

4. Write a nice Gleam wrapper (works just like html.gleam does):

// src/chart/recharts.gleam
import glendix/binding
import glendix/react.{type ReactElement}
import glendix/react/attribute.{type Attribute}

fn m() { binding.module("recharts") }

pub fn pie_chart(attrs: List(Attribute), children: List(ReactElement)) -> ReactElement {
  react.component_el(binding.resolve(m(), "PieChart"), attrs, children)
}

pub fn pie(attrs: List(Attribute), children: List(ReactElement)) -> ReactElement {
  react.component_el(binding.resolve(m(), "Pie"), attrs, children)
}

5. Use it in your widget:

import chart/recharts
import glendix/react/attribute

pub fn my_chart(data) -> react.ReactElement {
  recharts.pie_chart(
    [attribute.attribute("width", 400), attribute.attribute("height", 300)],
    [
      recharts.pie(
        [attribute.attribute("data", data), attribute.attribute("dataKey", "value")],
        [],
      ),
    ],
  )
}

Using .mpk Widgets

You can put .mpk files in the widgets/ folder and use them like React components — how cool is that!

1. Pop your .mpk files into the widgets/ folder

2. Run gleam run -m glendix/install (it sorts out all the bindings for you!)

Two things happen automatically — isn’t that nice:

3. Have a look at the auto-generated src/widgets/*.gleam files:

// src/widgets/switch.gleam (made automatically!)
import glendix/mendix
import glendix/react.{type JsProps, type ReactElement}
import glendix/react/attribute
import glendix/widget

/// Renders the Switch widget — reads the attributes from props and passes them along
pub fn render(props: JsProps) -> ReactElement {
  let boolean_attribute = mendix.get_prop_required(props, "booleanAttribute")
  let action = mendix.get_prop_required(props, "action")

  let comp = widget.component("Switch")
  react.component_el(
    comp,
    [
      attribute.attribute("booleanAttribute", boolean_attribute),
      attribute.attribute("action", action),
    ],
    [],
  )
}

It works out which attributes are required and which are optional all by itself! You can change the files afterwards if you like.

4. Use it in your widget:

import widgets/switch

// Inside a component
switch.render(props)

Downloading Widgets from the Marketplace

You can search for widgets on the Mendix Marketplace and download them right from the terminal — it’s dead handy!

1. Put your Mendix PAT in .env:

MENDIX_PAT=your_personal_access_token

You can get a PAT from Mendix Developer Settings — click New Token under Personal Access Tokens. You’ll need the mx:marketplace-content:read permission.

2. Run this:

gleam run -m glendix/marketplace

3. Use the lovely interactive menu:

  ── Page 1/5+ ──

  [0] Star Rating (54611) v3.2.2 — Mendix
  [1] Switch (50324) v4.0.0 — Mendix
  ...

  Number: download | Search term: filter by name | n: next | p: previous | r: reset | q: quit

> 0              ← type a number to download it
> star           ← type a word to search
> 0,1,3          ← use commas to pick several at once

When you choose one, it shows you all the versions and tells you if it’s Pluggable or Classic. The .mpk files go into widgets/ and the binding code gets made in src/widgets/ — all automatic!

It uses Playwright (Chromium) to check versions. You’ll need to log in the first time, but after that it remembers you in .marketplace-cache/session.json.

Build Scripts

glendix comes with built-in scripts — no extra files needed! Just use gleam run -m and off you go!

CommandWhat It Does
gleam run -m glendix/installInstalls everything + makes bindings + generates widget files (works out your package manager by itself!)
gleam run -m glendix/marketplaceSearches and downloads widgets from the Marketplace (interactive!)
gleam run -m glendix/buildMakes a production build (.mpk file)
gleam run -m glendix/devStarts a dev server (with HMR on port 3000 — changes show up instantly!)
gleam run -m glendix/startConnects to a Mendix test project
gleam run -m glendix/lintChecks your code with ESLint
gleam run -m glendix/lint_fixFixes ESLint problems automatically
gleam run -m glendix/releaseMakes a release build

It works out which package manager you’re using all by itself:

How It’s All Put Together

Here’s what’s inside — it’s quite organised actually!

glendix/
  react.gleam              ← The main important bits — createElement, Context, keyed, components, flushSync
  react_ffi.mjs            ← The JS helper for elements, Fragment, Context, and clever memo stuff
  react/
    attribute.gleam         ← Attribute type + 108+ HTML attribute functions
    attribute_ffi.mjs       ← Turns attributes into React props
    hook.gleam              ← 40 React Hooks (including use_promise and use_form_status!)
    hook_ffi.mjs            ← Hooks JS helper
    ref.gleam               ← Ref helpers (current and assign)
    event.gleam             ← 16 event types + 154+ handlers + 82+ accessors
    event_ffi.mjs           ← Event accessor JS helper
    html.gleam              ← 85+ HTML tags (pure Gleam — no JS!)
    svg.gleam               ← 58 SVG elements (pure Gleam — no JS!)
    svg_attribute.gleam     ← 97+ SVG attributes (pure Gleam — no JS!)
  mendix.gleam              ← Core Mendix types + Props accessors
  mendix_ffi.mjs            ← Mendix runtime type helper
  mendix/
    editable_value.gleam    ← EditableValue
    action.gleam            ← ActionValue
    dynamic_value.gleam     ← DynamicValue
    list_value.gleam        ← ListValue + Sort + Filter
    list_attribute.gleam    ← List-linked types
    selection.gleam         ← Selection
    reference.gleam         ← ReferenceValue (single association)
    reference_set.gleam     ← ReferenceSetValue (multiple associations)
    date.gleam              ← JS Date wrapper
    big.gleam               ← Big.js wrapper
    file.gleam              ← File / Image
    icon.gleam              ← Icon
    formatter.gleam         ← ValueFormatter
    filter.gleam            ← FilterCondition builder
  editor_config.gleam       ← Editor helpers (Jint compatible — no Lists!)
  editor_config_ffi.mjs     ← @mendix/pluggable-widgets-tools wrapper
  binding.gleam             ← External React component binding API
  binding_ffi.mjs           ← Binding JS helper (gets remade on install)
  widget.gleam              ← .mpk widget component binding API
  widget_ffi.mjs            ← Widget JS helper (gets remade on install)
  classic.gleam             ← Classic (Dojo) widget wrapper
  classic_ffi.mjs           ← Classic widget JS helper (gets remade on install)
  marketplace.gleam         ← Marketplace search and download
  marketplace_ffi.mjs       ← Content API + Playwright + S3 download helper
  cmd.gleam                 ← Shell commands + PM detection + binding generation
  cmd_ffi.mjs               ← Node.js child_process + fs + ZIP + binding + widget .gleam generation
  build.gleam               ← Build script
  dev.gleam                 ← Dev server script
  start.gleam               ← Mendix integration script
  install.gleam             ← Install + binding generation script
  release.gleam             ← Release build script
  lint.gleam                ← ESLint script
  lint_fix.gleam            ← ESLint auto-fix script

Why We Made It This Way

Thank You

The React bindings in v2.0 were inspired by the lovely redraw project. We learnt a lot about how to split up FFI modules, hook patterns, and event systems from them. Cheers, redraw!

Licence

Blue Oak Model Licence 1.0.0

Search Document