This guide is for developers who are familiar with WordPress and want a basic introduction to MODX. With its fancy syntax and terminology, MODX may look daunting. The good news is that its easy to learn. It's also an incredibly flexible CMF that's a joy to use if you like having full control over the way you build your websites.

I came to MODX from a WordPress background. Initially, I found it jarring because the two systems have completely different approaches. This is the article I would have wanted to read when I first came across MODX.

We’ll start by looking at the building blocks of MODX and we’ll then compare directly (or as directly as we can) to WordPress.

But before we dig in, it’s worth mentioning that there’s two major versions of MODX: MODX Revolution 2.x (Revo) and MODX Evolution 1.x (Evo, now called Evolution CMS). We’ll be talking about the latest release, Revo. There’s subtle differences between the two, so when you’re looking at old forum posts, documentation, or guides, make sure you’re looking at the correct version.

MODX anatomy

Resources

A resource is a page in MODX. Each resource, like pages and posts in WordPress, has a unique ID. The ID is displayed next to each resource name in parenthesis in the resource tree.

If you want to link to a resource with an ID of 1, you’d use the following:

<a href="[[~1]]">Home</a>

Notice the square brackets: that's MODX tag syntax. We'll come back to that.

Resources have different content types. By default, the content type is set to HTML so it appears as a web page. Other content types include CSS, JavaScript, XML, JSON, etc.

So, for example, if you wanted to create an XML sitemap, you could create a resource, set the content type to XML, give the resource an alias of ‘sitemap’, add some code to loop through the resources, and you’re done! MODX will serve up the resource as an XML file.

Elements

MODX Elements include Templates, Template Variables, Chunks, Snippets, and Plugins. These are all created in the Manager in the ‘Elements’ tab in the left hand menu. In the following section, we’ll explore these Elements.

Templates

Templates are applied to a resource and they dictate how that resource should look. To create a Template, click the plus icon next to ‘Templates’ in the ‘Elements’ tab.

The markup of a template is stored in the database by default. If you use version control such as Git, you’ll probably want to use static files. To do this, select the template you want to edit, then select the ‘Is Static’ option and select the relevant file.

Templates can’t contain PHP code; you’ll want to use Snippets for that. While at first this might seem frustrating, the separation of logic and presentation means even the most complicated templates will still be easy to read and maintain.

Here’s what a very basic template in MODX looks like:

<html>
<head>
    <title>[[++site_name]] - [[*pagetitle]]</title>
    <base href="[[++site_url]]" />
</head>
<body>
    [[*content]]
</body>
</html>

Nice and clean, huh? It gets better! View source in the browser and you’ll quickly notice there’s no injected code. No emoji JavaScript, no meta tags, or any of the other cruft that WordPress throws in.

Template Variables

Template Variables or "TVs" are the equivalent to WordPress custom fields. A TV is tied to a template (or multiple templates). And, like WordPress, you can change the input type to text, image, select, checkbox, date, etc.

To output a TV, you’d use:

[[*name-of-tv]]

Chunks

Chunks are reusable HTML markup (i.e. header, footer, sidebar, etc.). This is similar to how you might use `get_template_part` or `include` in WordPress, except Chunks can’t contain PHP.

To call a chunk:

[[$name-of-chunk]]

Chunks are a great way of splitting up components. Our template might look like this:

[[$html-head]]
[[$header]]

[[*content]]

[[$footer]]
[[$html-foot]]

Snippets

Snippets are dynamic bits of PHP code. They operate like PHP functions in that you can pass properties to them.

Here’s a basic example of a Snippet:

<?php
$person = 'World';

if(!empty($name)) {
	$person = $name;
}

return "Hello {$person}!";

And you call it like this:

[[helloWorld]]
[[helloWorld? &name=`Marc`]]

Which would output:

Hello World!
Hello Marc!

Plugins

Plugins, like Snippets, are bits of PHP code that have access to the MODX API. The difference is when the code executes. Snippets run when a page is viewed. Plugins execute during certain system events (such as clearing the cache or saving a document).

Tag syntax

Here’s a quick reference of the MODX tag syntax:

Resource Fields - [[*field]]
These are fields that are pulled from the current document, such as [[*pagetitle]] and [[*content]].

Template Variables - [[*name-of-tv:tag]]
TVs are custom fields, again pulled from the current document.

Chunks - [[$name-of-chunk:tag]]
Reusable HTML markup - think headers, footers, sidebars, or other common components.

Snippets - [[snippet:tag]]
Dynamic bits of PHP.

Placeholders - [[+placeholder:tag]]
Placeholders are typically used with snippets such as getResources to get another document’s resource field, however placeholders can be set to any value. Check out Mark’s article for a more detailed explanation of the difference between placeholders and resource fields.

Links - [[~1]]
Generates a link to the resource. Replace 1 with the ID of the resource you want to link to.

System Settings - [[++system_setting]]
These are settings that are configurable in the Manager.

Comments - [[- My comment. ]]
Since you can’t use PHP in templates (with a Snippet), you’ll want to use this syntax if you want to remove your comments from the output.

Caching

MODX has caching built in and does so by default. You can tell MODX not to cache a Snippet, Setting tag, Placeholder, or Chunk by adding an exclamation mark.

For example, the following would be cached:

[[$name-of-chunk]]
[[helloWorld]]

And the following would not be cached:

[[!$name-of-chunk]]
[[!helloWorld]]

In general, you want to cache as much as you can to improve the speed of your site. However, if what you’re calling changes on every request, you don’t want to cache it.

If you’re encountering issues where MODX isn’t updating the frontend as you’d expect, the first place to check is the cache. To clear the cache, in the Manager click ‘Manage’ and then ‘Clear Cache’.

WordPress anatomy

Now we’ve been through the building blocks of MODX, we can start to compare WordPress to MODX.

Let’s jump in.

Themes

Themes don’t really exist in MODX. Well, they do sorta (you can buy various themes). Out of the box, MODX has no way to import/export ‘themes’. There are various ways around this, such as creating a Transport Package to bundle Templates/TVs/Chunks/Snippets/etc.

Templates

Templates in MODX work much in the same way as they do in WordPress. The main difference is that WordPress dictates how you should organise and name your files. In MODX, you can organise and name things however you like.

I typically set up my folder structure to look something like this:

assets/
    templates/ (for static template files)
    chunks/ (for static chunk files)
    snippets/ (for, you guessed it, more static files)
    css/ (for compiled CSS from SASS files)
    js/ (for compiled JavaScript files)

You can of course organise this however you like.

Custom Post Types

CPT’s are effectively ‘buckets’ to store your content in. Here’s an example of a Custom Post Type in WordPress:

To create this in WordPress, you’d first create the CPT in functions.php. You’d then create an archive-books.php file to list the books and a single-books.php file to display individual books. The code might look something like this:

archive-books.php

<h1>Books</h1>
<ol>
<?php while ( have_posts() ) : the_post(); ?>
    <li>
        <h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
    </li>
<?php endwhile; ?>
</ol>

single-books.php

<?php while ( have_posts() ) : the_post(); ?>
<article>
    <h1><?php the_title(); ?></h1>
    <?php the_content(); ?>
</article>
<?php endwhile; ?>

In MODX, one way to accomplish the same result is by using child resources and the getResources Snippet. First, install getResources by going to Extras > Installer in the MODX manager.

Next, set up the resource tree like this:

The Books resource would have it’s own template which would include a getResources call. The template would look something like this:

<h1>[[*pagetitle]]</h1>
<ol>
[[getResources?
    &parents=`[[*id]]`
    &tpl=`book-list-item`
    &includeContent=`1`
    &limit=`10`
]]
</ol>

In the example above, we’re calling the getResources Snippet and passing a couple of arguments. First, we’re specifying the parent as the current page so that it loops through the child resources. Next, we’re telling the Snippet to use the book-list-item chunk. &includeContent tells the Snippet to return the content of each resource and finally we’re specifying a limit of 10 resources. There’s more properties you can pass to getResources.

The book-list-item chunk would then look like this:

<li>
    <h2><a href="[[~[[+id]]]]">[[+pagetitle]]</a></h2>
</li>

Note: in the chunk above we’re using placeholders, not resource fields. This is because we want to pull the fields from the child page, not the current page.

Each individual resource would have a template (equivalent to single-books.php) that would look something like this:

<article>
    <h1>[[*pagetitle]]</h1>
    [[*content]]
</article>

Custom Fields

In our book example above, we’ll probably want to add some custom fields such as ‘author’ and ‘ISBN’.

In WordPress, you could create a custom field by using the Advanced Custom Fields plugin.

single-books.php would then look like this:

<?php while ( have_posts() ) : the_post(); ?>
    <article>
        <h1><?php the_title(); ?></h1>
        <p>Author: <?php the_field(‘author’); ?></p>
        <p>ISBN: <?php the_field(‘isbn’); ?></p>
        <?php the_content(); ?>
    </article>
<?php endwhile; ?>

In MODX, you’d use Template Variables, which are assigned to specific templates. In this case, we’d assign our Template Variables to the `Book Single` Template.

First, in order to pass through Template Variables, we’ll need to modify our getResources call by setting includeTVs and processTVs to 1:

[[getResources?
    &parents=`[[*id]]`
    &tpl=`book-list-item`
    &includeContent=`1`
    &includeTVs=`1`
    &processTVs=`1`
    &limit=`10`
]]

Our code would then look like this:

<article>
    <h1>[[+pagetitle]]</h1>
    <p>Author: [[+author]]</p>
    <p>ISBN: [[+isbn]]</p>
    [[+content]]
</article>

Easy, huh?

Taxonomies

MODX doesn’t really have a concept of taxonomies. You could use Template Variables to accomplish this or use a plugin.

The Loop

The Loop in WordPress returns the appropriate content for the given page you’re on. You might loop through blog posts like this:

<?php
while ( have_posts() ) : the_post();
?>
	<h1><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h1>
	<?php the_content(); ?>
<?php
endwhile;
?>

In MODX, we could again use the getResources Snippet to achieve the same result:

[[getResources?
    &parents=`[[*id]]`
	&tpl=`article-list-item`
	&includeContent=`1`
]]

The getResource Snippet above calls the chunk article-list-item, which would look like this:

<h1><a href="[[~[[+id]]]]">[[+pagetitle]]</a></h1>
[[+content]]

Note: in the chunk above we’re again using placeholders, not resource fields. This is because we want to pull the fields from the child page, not the current page.

Menus

To create a dynamic menu in WordPress, you’d register the menu in functions.php and then use the following in your template:

<?php wp_nav_menu( array('menu' => 'main-menu', 'depth' => 1 ) ); ?>

In MODX, a common way to do this is to use the Wayfinder extra. Once installed, you can call the Wayfinder like this:

[[Wayfinder? &startId=`0` &level=`1` &outerTpl=`menu.outer` &rowTpl=`menu.row`]]

Just like getResources, we’re passing a couple of properties such as &startId (the top level parent) and &level (how many nested resources to go down). We’re also using two chunks to build the menu. There’s a bunch more properties available to customise the menu.

Here’s what the menu.outer Chunk could look like:

<nav role="navigation" class="primary-nav">
	[[+wf.wrapper]]
</nav>

And menu.row:

<a href="[[+wf.link]]" title="[[+wf.title]]" [[+wf.classes]]>[[+wf.linktext]]</a>

To hide a resource from the menu, go to the resource in the Manager and select the "Hide From Menus" tick box.

Multi-site

My experience of building a WordPress multi-site, was... well, let’s just call it interesting.

Although this article isn’t a “MODX is better than WordPress” article, this is a case where MODX is clearly superior.

MODX has “Contexts” which allow you to build multiple sites that share elements. This means that you can host multiple sites, subdomains, landing pages, etc. on the same MODX install and administer via the same Manager. Using MODX user permissions, you can create users or groups of users that only have access to certain Contexts.

Multi-lingual

There’s multiple ways to handle multi-lingual sites in MODX. One way is to use “Contexts” as mentioned above to create unique resources for each language, while sharing elements.

Recommend Extras to get started

Wayfinder - for generating navigation menus

getResources - for listing resources (see examples above)

getResourceField - retrieve Resource Fields and Template Variables from other resources

pdoTools - can replace Wayfinder, getResources, getResourceField and several other snippets. It's very powerful and generally at least a bit faster. See our pdoTools introduction here.

Collections - groups resources into a container view and ideal for blogs, news, or sections with lots of child resources

Ace - adds syntax highlighting to Elements in the Manager

ClientConfig - create fields that your client can easily edit and use site-wide (such as phone number, email address, etc.)

FormIT - for processing forms

VersionX - track versions of resources and elements

Redactor - the best rich text editor for MODX

ContentBlocks - dynamically build pages in the Manager using blocks

More resources

MODX blog

MODX official documentation

MODX forums

Bob Ray's MODX guide and blog

Mark Hamstra's blog

Sepia River blog