Writing Plugins
Last updated Oct 20th, 2026 | Page history | Improve this page | Report an issue
Support the team building MODX with a monthly donation.
The budget raised through OpenCollective is transparent, including payouts, and any contributor can apply to be paid for their work on MODX.
Backers
Budget
$194 per month—let's make that $500!
Learn moreWhat is a Plugin?¶
Plugins are PHP that runs at a system event rather than on page view. A Snippet runs when the page renders; a Plugin runs when something happens — a Chunk is saved, the cache is cleared, a user logs in. Any Plugin attached to a fired event executes, lowest priority number first, and control then returns to the caller.
Other CMSes call this mechanism a hook, an action, or a filter; the name differs, the idea does not. Most MODX events fire only inside the Manager, so Plugins are not limited to front-end work — see System Events for the full list.
Any closing PHP tag ?> is stripped from Plugin code when it is saved. It is unnecessary: the body ends up inside other PHP code when executed.
The Event Model¶
MODX invokes System Events across its code processes to allow you to modify core functionality without hacking the core. These System Events can have any number of Plugins attached to them, and will execute each Plugin in rank according to its priority (lowest numbers first).
Handling an Event¶
In your Plugin, how you handle the output depends on the System Event you are in. For some system events, you return a value from the Plugin. For others, you access the output by reference and modify it.
If you need to know which event triggered your plugin (say, for a plugin that listens to more than one event), you can access the Event’s name like so:
$eventName = $modx->event->name;
The code for a Plugin listening to more than one event looks like this:
$eventName = $modx->event->name;
switch($eventName) {
case 'OnWebPageInit':
/* do something */
break;
case 'OnWebPagePrerender':
/* do something else */
break;
}
Message the User¶
Description: Send a custom message to the user as they create/edit a page… a custom header. System Events: OnDocFormPrerender
$modx->event->output('Hi there user!');
Custom Validation¶
Description: Do some custom validation on saving a page resource System Events: OnBeforeDocFormSave
// Do some logical stuff.... if validation failed:
$modx->event->output('Something did not validate!');
return "This goes to the logs";
The trick here is that what you want to message the user has to be passed to the $modx->event->output() function; any text you want to write to the logs can simply be returned by the plugin. If you pass validation, simply return null.
No HTML Allowed The output you set in $modx->event->output() must not contain any HTML! Use plain text only! For OnBeforeDocFormSave the text becomes the processor’s error message, which is rendered as plain text.
Return value must be a string. If your return value will be a number, concatenate it with an empty string.
Word Filter¶
Description: Filter words from a document before it’s displayed on the web System Events: OnWebPagePrerender
$words = array("snippet", "template"); // words to filter
$output = &$modx->resource->_output; // get a reference to the output
$output = str_replace($words,"<b>[filtered]</b>",$output);
Page-Not-Found Redirector¶
Description: Redirects a user to selected document and sends a message System Events: OnPageNotFound System Settings: create these three keys in System → Settings — they do not ship with core:
- pnf.page: Error Resource ID
- pnf.mailto: Mail To Address
- pnf.mailfrom: Mail From Address
if ($modx->event->name == 'OnPageNotFound') {
$errorPage = $modx->getOption('pnf.page');
if (empty($errorPage)) {
$modx->sendErrorPage();
} else {
$mailto = $modx->getOption('pnf.mailto');
if (!empty($mailto)) {
// send a message to a local account
$resourceId = $modx->resource->get('id');
$subject = 'Page not found';
$body = 'Someone tried to access document id '.$resourceId;
$modx->mail->set(\MODX\Revolution\modMail::MAIL_BODY, $body);
$modx->mail->set(\MODX\Revolution\modMail::MAIL_FROM, $modx->getOption('pnf.mailfrom'));
$modx->mail->set(\MODX\Revolution\modMail::MAIL_FROM_NAME, 'MODX');
$modx->mail->set(\MODX\Revolution\modMail::MAIL_SENDER, 'MODX');
$modx->mail->set(\MODX\Revolution\modMail::MAIL_SUBJECT, $subject);
$modx->mail->address('to', $mailto);
$modx->mail->setHTML(true);
$modx->mail->send();
}
$modx->sendRedirect($modx->makeUrl($errorPage), ['count_attempts' => true]);
exit;
}
}
[!WARNING] Inside Plugin code use
$modx->…, never$this->…. In 3.x the Plugin body isincluded directly instead of being wrapped in a method, so$thisis themodPluginobject — it has nomakeUrl(),getOption(), or lexicon helpers.
See Also¶
- System Events
- OnBeforeCacheUpdate
- OnBeforeChunkFormDelete
- OnBeforeChunkFormSave
- OnBeforeDocFormDelete
- OnBeforeDocFormSave
- OnBeforeEmptyTrash
- OnBeforeManagerLogin
- OnBeforeManagerLogout
- OnBeforeManagerPageInit
- OnBeforePluginFormDelete
- OnBeforePluginFormSave
- OnBeforeRegisterClientScripts
- OnBeforeSaveWebPageCache
- OnBeforeSnipFormDelete
- OnBeforeSnipFormSave
- OnBeforeTempFormDelete
- OnBeforeTempFormSave
- OnBeforeTVFormDelete
- OnBeforeTVFormSave
- OnBeforeUserActivate
- OnBeforeUserFormDelete
- OnBeforeUserFormSave
- OnBeforeWebLogin
- OnBeforeWebLogout
- OnCacheUpdate
- OnCategoryBeforeRemove
- OnCategoryBeforeSave
- OnCategoryRemove
- OnCategorySave
- OnChunkBeforeRemove
- OnChunkBeforeSave
- OnChunkFormDelete
- OnChunkFormPrerender
- OnChunkFormRender
- OnChunkFormSave
- OnChunkRemove
- OnChunkSave
- OnContextBeforeRemove
- OnContextBeforeSave
- OnContextFormPrerender
- OnContextFormRender
- OnContextRemove
- OnContextSave
- OnDocFormDelete
- OnDocFormPrerender
- OnDocFormRender
- OnDocFormSave
- OnDocPublished
- OnDocUnPublished
- OnElementNotFound
- OnEmptyTrash
- OnFileManagerBeforeUpload
- OnFileManagerDirCreate
- OnFileManagerDirRemove
- OnFileManagerDirRename
- OnFileManagerFileCreate
- OnFileManagerFileRemove
- OnFileManagerFileRename
- OnFileManagerFileUpdate
- OnFileManagerUpload
- OnHandleRequest
- OnInitCulture
- OnLoadWebDocument
- OnLoadWebPageCache
- OnManagerAuthentication
- OnManagerLogin
- OnManagerLoginFormPrerender
- OnManagerLoginFormRender
- OnManagerLogout
- OnManagerPageAfterRender
- OnManagerPageBeforeRender
- OnManagerPageInit
- OnMODXInit
- OnPackageInstall
- OnPackageRemove
- OnPackageUninstall
- OnPageNotFound
- OnPageUnauthorized
- OnParseDocument
- OnPluginBeforeRemove
- OnPluginBeforeSave
- OnPluginEventRemove
- OnPluginFormDelete
- OnPluginFormPrerender
- OnPluginFormRender
- OnPluginFormSave
- OnPluginRemove
- OnPluginSave
- OnPropertySetBeforeRemove
- OnPropertySetBeforeSave
- OnPropertySetRemove
- OnPropertySetSave
- OnBeforeResourceGroupRemove
- OnResourceGroupBeforeSave
- OnResourceGroupRemove
- OnResourceGroupSave
- OnRichTextBrowserInit
- OnRichTextEditorInit
- OnRichTextEditorRegister
- OnSiteRefresh
- OnSiteSettingsRender
- OnTemplateVarBeforeRemove
- OnTemplateVarBeforeSave
- OnTemplateVarRemove
- OnTemplateVarSave
- OnUserActivate
- OnUserBeforeRemove
- OnUserBeforeSave
- OnUserChangePassword
- OnUserFormDelete
- OnUserFormSave
- OnUserNotFound
- OnUserRemove
- OnUserSave
- OnWebAuthentication
- OnWebLogin
- OnWebLogout
- OnWebPageComplete
- OnWebPageInit
- OnWebPagePrerender
Support the team building MODX with a monthly donation.
The budget raised through OpenCollective is transparent, including payouts, and any contributor can apply to be paid for their work on MODX.
Backers
Budget
$194 per month—let's make that $500!
Learn more










