React in Moodle core: what it means for your custom plugins
Moodle 5.2 added React, TypeScript and ES modules to core, and 5.3 started moving core screens over. What changes for plugins you own, and what you can leave alone.
For about a decade, Moodle’s frontend was server-rendered Mustache templates plus AMD JavaScript modules built with Grunt. YUI before that. It worked, but it was a long way from how the rest of the web builds interfaces.
That changed in Moodle 5.2. The 5.2 release notes list “base React support” in core: a React bundle, import maps, a React build system, a profiler, and a Mustache helper that mounts React components. Moodle 5.3 then started moving core screens over.
If you own custom Moodle plugins, here is what that means in practice.
The short answer
- Your existing plugins don’t need React. AMD modules and Mustache templates still work in 5.3.
- New interactive screens should be built the new way. Moodle’s own guide says to “avoid introducing new AMD based patterns”.
- The cost of waiting is small today and grows every release. Core is converting its own heavily used AMD modules to ES modules now.
What Moodle actually added
The frontend guide for 5.2 says Moodle “now supports modern frontend development using ECMAScript modules (ESM), React, and TypeScript.” The rules it sets for plugin authors are short:
- Source code goes in the component’s
js/esm/srcfolder, written in TypeScript. - It is compiled to browser-ready JavaScript. “Compiled files should not be edited directly.”
- AMD is being phased out in favour of ESM. Moodle is working on letting ESM code use the existing JavaScript APIs, and compatibility helpers exist for code that still has to reach AMD modules.
- The old
{{#js}}block withrequire()still works, but is discouraged for new React components.
React components are mounted from Mustache templates with a {{#react}} helper. The helper’s documentation describes a block that holds a small JSON object: the component’s module name, its props, and optional HTML attributes. Fallback HTML can follow the JSON and is shown until the component loads. The component module must have a default export that is a React function component.
On the page, an auto-init script finds every element marked as a React component, imports the module through the import map, and mounts it with createRoot. A MutationObserver watches for components added later by AJAX and mounts those too, and unmounts components that are removed.
That last detail matters for plugin developers. Moodle pages load fragments all the time: modals, inplace editors, course sections. Before, each AMD module had to handle “was I re-rendered?” itself. Now the platform does it.
What 5.3 did next
The 5.3 release notes show core starting to use the new stack on real screens:
- Top navigation and secondary navigation moved to Design System components.
- A standard PHP API for mounting React components (MDL-89296).
- Heavily used AMD modules converted to ESM (MDL-88762).
- FloatingUI added as a bundle for popovers and dropdowns (MDL-89232).
5.2 had already added the Moodle Design System, phase 1: design tokens and an npm package in core. So the direction is clear. Core UI moves to React components built on shared tokens, and Mustache stays as the server-side layer that decides what to render.
What it looks like in a plugin
Here is the same small plugin laid out for Moodle 4.5 and for 5.3. Click the files.
One plugin, two Moodle versions
moodle/public/local/attendance/ version.php amd/src/report.js js/esm/src/Report.tsx templates/report.mustache classes/privacy/provider.php db/upgrade.php
Two changes stack on top of each other:
- The plugin moved. Since 5.1, web files live under
public/. On a git-based install, the plugin folder has to be moved by hand (upgrade guide). - The JavaScript can be a React component. The Mustache template renders the server data and the fallback, and the React component takes over in the browser.
Everything else is the same as it has been for years: version.php, the Privacy API provider, db/upgrade.php with savepoints, capabilities, language strings. React changes the screen. It does not change what makes a Moodle plugin correct.
Should you rewrite your plugins in React?
Mostly, no. Here is how we decide for clients:
Leave it alone if the plugin is mostly forms and server-rendered pages. Moodle forms, Mustache templates and a small AMD module are fine in 5.3. Rewriting them buys nothing.
Plan to move it if the plugin has a heavy custom interface: drag and drop, live updating dashboards, a grader panel, an editor. These are the screens where AMD code gets tangled, and where React and TypeScript make the code smaller and easier to test. Also move it if the plugin still uses YUI. YUI has been on its way out for years, and core keeps removing YUI pieces (5.2 removed YUI TreeView from mod_folder).
Build new screens in React from now on, on Moodle 5.2 or later. A new interactive plugin written in AMD today is technical debt on day one.
One catch: if you must support Moodle 4.5 LTS and 5.3 LTS with one codebase, the React path is only available on 5.2 and up. For those plugins we keep the AMD version for 4.5 and ship the React screen in the 5.x branch, or we wait until the client drops 4.5. Moodle 4.5 gets security fixes until 4 October 2027, so many organisations will run both for a while.
What this means for cost
React in core makes some plugins cheaper to build, not more expensive:
- Interactive screens take less code. State handling that used to be dozens of lines of DOM updates is a component.
- TypeScript catches a class of bugs before anyone opens a browser.
- AI coding agents are very good at React and TypeScript, because there is so much public code to learn from. They are much weaker at Moodle’s older AMD and YUI patterns. Moving the frontend to a mainstream stack makes agent-assisted development work better on Moodle.
What does not get cheaper is the Moodle part: capabilities, contexts, the Privacy API, backup and restore, upgrade steps. A React component that shows grades to the wrong role is still a data leak. That part still needs someone who knows Moodle well.
What to do now
- Running 4.5: nothing urgent. Note which custom plugins have heavy interfaces, and plan them into your 5.3 move.
- Moving to 5.3: budget for moving plugin folders under
public/. Ask for any new interactive screen to be built with ESM and React. - Commissioning a new plugin: ask which Moodle versions it targets. If the answer is 5.2 or later only, the interactive parts should be React and TypeScript.
Sources
- Moodle HQ, Moodle 5.2 release notes and Moodle 5.3 release notes.
- Moodle HQ, Frontend development guide (5.2).
- Moodle HQ, React guide (5.2) and Mustache helper and autoinit.
- MoodleDocs, Upgrading (5.3).
- Moodle HQ, Releases and support dates.
Need a Moodle plugin built or rescued?
Tell the scoping assistant what you need. You get a fixed price and a timeline in a few minutes, and an engineer reviews it within one business day.
Get a fixed quote