Mount compiled Elm apps into WordPress admin pages, shortcodes and other surfaces. Handles the enqueue, the localized flags blob, and the REST nonce. Bundling your Elm is your job — this package takes the compiled .js and wires it into WordPress.
Part of a three-package set:
| Role | Package |
|---|---|
| PHP mount helper (this package) | pinkcrab/elm-mount |
| JS bootstrap | @pinkcrab/elm-wp-bootstrap |
| Elm package | Pink-Crab/elm-wp |
composer require pinkcrab/elm-mountBuild an Elm_App once with your script + flags, then call render() (echoes) or parse() (returns a string) from any WordPress callback that produces output — admin page, shortcode, meta box, widget, REST handler, template part. The same $app works for all of them.
use PinkCrab\ElmMount\Elm_App;
$app = Elm_App::create( 'my_settings' )
->script( plugin_dir_url( __FILE__ ) . 'build/main.js' )
->flags( [
'pageTitle' => __( 'My Settings', 'td' ),
'canEdit' => current_user_can( 'manage_options' ),
] );
// Admin page / meta box — echo
add_submenu_page(
'options-general.php',
'My Settings',
'My Settings',
'manage_options',
'my-settings',
fn() => $app->render()
);
// Shortcode — return string
add_shortcode( 'my_app', fn() => $app->parse() );Both render() and parse() enqueue the script, localize the flags blob on window.my_settings, and produce <div id="my_settings-root"></div> for Elm to attach to. The mount node id defaults to {handle}-root; override with ->mount_node( 'custom-id' ) if needed.
Your compiled Elm bundle reads window.my_settings and mounts into #my_settings-root — @pinkcrab/elm-wp-bootstrap handles that plumbing automatically.
This section is the authoritative spec shared by all three packages. Any change here is a contract bump and must be mirrored in the other two repos in lockstep.
Emitted via wp_localize_script( $handle, $handle, $blob ). The JavaScript side reads it from window.<handle> and hands it to Elm as flags.
{
"restRoot": "https://example.test/wp-json/",
"restNonce": "abc123...",
"restNamespace":"wp/v2",
"ajaxUrl": "https://example.test/wp-admin/admin-ajax.php",
"ajaxNonce": "def456...",
"mountNode": "my_settings-root",
"locale": "en_GB",
"currentUser": {
"id": 1,
"displayName": "Glynn Quelch",
"roles": ["administrator"],
"capabilities":["manage_options", "edit_posts"]
},
"pluginData": {
"pageTitle": "My Settings",
"canEdit": true
}
}Notes:
restNonceis minted from thewp_restaction and is whatwp.apiFetchneeds.ajaxNonceis minted from a package-specific action (one per handle) for the legacyadmin-ajax.phppath.pluginDatais the only free-form section — user-supplied flags via->flags( [...] ).capabilitiesis a UI hint for Elm to disable buttons etc; never trust it for authorisation (server-side checks are the real gate).
The JS bootstrap and the Elm package must agree on these names. Changing any is a contract break.
| Direction | Port name | Purpose |
|---|---|---|
| Elm → JS | wpApiFetch |
Outbound REST call via wp.apiFetch. Payload: { id, method, path, body? }. |
| JS → Elm | wpApiFetchResult |
Paired response. Payload: { id, ok, status, body }. |
| Elm → JS | wpNotice |
Show an admin notice. Payload: { kind: "success"|"error"|"info"|"warning", message }. |
| Elm → JS | copyToClipboard |
Copy text to clipboard. Payload: string. |
id on wpApiFetch / wpApiFetchResult is a string correlation id the Elm side generates so multiple in-flight requests can be matched to their responses.
All three packages share a major version. Within 1.x, minor and patch versions can move independently per package — compatibility is guaranteed across the same major.
pinkcrab/elm-mount |
@pinkcrab/elm-wp-bootstrap |
Pink-Crab/elm-wp |
|---|---|---|
1.x |
1.x |
1.x |
MIT © PinkCrab