Developers
Loading
Modules reach the page in three ways: a template asks for a position, a
component or plugin renders one inline, or an article contains a tag that
expands into one. All three end at the same place —
Hubzero\Module\Loader::render(),
reachable through the Module facade.
Almost always you want the first. A module placed by an administrator into a template position is the arrangement everything else is built around: the hub decides where it goes, on which pages, and who sees it, without touching code. Render a module from a component only when the component's own layout is the only place the block makes sense. Read this chapter mainly to know why your module is not appearing — the section on positions is the answer more often than not.
What the loader knows
Loader::all() runs one query per request and caches the result. It returns
the published instances that pass every one of these filters:
m.published = 1and the matching#__extensionsrow hase.enabled = 1publish_upis null or past,publish_downis null or futurem.accessis one of the current user's authorised view levelsm.client_idmatches the current client- the instance is assigned to the current
Itemid, or to all menu items - with language filtering on,
m.languageis the current tag or*
An instance assigned negatively to the current menu item is then removed, and
duplicates are collapsed. The result is ordered by position, then by
ordering.
Read that list as a checklist. Each line is a way for a published module to be
absent from one page and present on another, and two of them catch people. A
module assigned to a single menu item is invisible on every other page, which
looks exactly like a broken module if you test on the wrong one. And a module
whose access level is not Public disappears for the logged-out visitor you are
probably testing as. mod_upcoming_bookings has nothing to show a guest
anyway, so give it a Registered access level rather than letting it render an
empty box.
The result is cached under a key made from the menu item, the user's view levels, the client and the language tag, so a module that is right for one visitor is not served to another.
In a template
The template declares a position with a jdoc tag, which the document parser
replaces with the rendered output of every module in that position:
<jdoc:include type="modules" name="footer" />
type="modules" renders the whole position; type="module" renders one
named module. Either accepts style, naming the chrome function that wraps
each module, and params, an inline parameter override.
Collapsing empty regions
countModules() on the document tells a template whether a position has
anything in it, so that the markup around it can be omitted:
<?php if ($this->countModules('helppane')) : ?>
<div id="help">
<jdoc:include type="modules" name="helppane" />
</div>
<?php endif; ?>
The argument is an expression. Hubzero\Document\Type\Html::countModules()
splits it on +, -, *, /, ==, !=, <>, <, >,
<=, >=, and, or, and xor, replaces each position name with
the number of modules in it, and evaluates the result:
$this->countModules('left or right')
$this->countModules('user1 + user2')
In a component, plugin, or view
Sometimes the block has to sit inside a component's own markup rather than
beside it — a note halfway down a booking form, where no template position
reaches. Hubzero\Module\Helper is a static wrapper over the same loader, and
is what component views use:
| Call | Result |
|---|---|
Helper::renderModules($position) |
Returns the combined output of every module in $position. |
Helper::renderModule($name) |
Returns the output of one module, by element name or by name without the prefix. |
Helper::displayModules($position) |
Echoes the same. |
Helper::displayModule($name) |
Echoes the same. |
Helper::countModules($condition) |
The count expression, as above. |
Helper::getParams($id) |
A Registry of one instance's parameters, by id or element name. |
echo \Hubzero\Module\Helper::renderModules('extracontent');
The Module facade exposes the loader itself, which is the same thing with
more control: Module::position(), Module::name(), Module::byName(),
Module::byPosition(), Module::isEnabled(), Module::params(),
Module::count(), Module::render(), Module::path(), and
Module::canonical().
echo Module::position('notices');
echo Module::name('mod_login', 'xhtml');
Chrome
The style names a function modChrome_{style}, loaded from
core/templates/system/html/modules.php and, if it exists, from
{template}/html/modules.php. The shipped set is none, table, xhtml,
outline, sliders, and tabs. A template adds its own by defining another
function in its html/modules.php.
none echoes the module's content and nothing else. xhtml wraps it in a
<div class="module{moduleclass_sfx}"> with an <h3> title when the
instance's Show Title is on, and emits nothing at all when the content is
empty. Multiple styles may be given, space-separated.
In article content
With the Content — xHub Tags plugin enabled, article text can carry a tag that renders a position where it stands:
{xhub:module position="footer"}
style and params attributes are accepted; the plugin defaults the style to
xhtml, and translates the legacy numeric values -1 and -2 to none and
xhtml.
Assigning a module to a position
None of this happens without an instance. In the administrative interface, go to the Module Manager, create a new instance of the module, give it a title, choose a position, set the access level and menu assignment, and publish it.
Positions belong to the template
This is the one to read twice, because it is the most common reason a module that works is not on the page.
Your module never declares a position. It does not know what positions exist,
and it cannot create one. A position is a string that a template's index.php
asks for:
<jdoc:include type="modules" name="right" />
The loader returns the published instances whose position column holds that
string. That is the entire contract. A position is a name two parties happen
to agree on, and nothing checks that they do.
So there are three ways for a correct module to render nothing, and none of them logs anything:
| What you did | What happens |
|---|---|
Assigned the instance to a position the active template's index.php never includes |
Nothing renders. The instance is published and looks fine in the Module Manager. |
Assigned it to a position the template declares in templateDetails.xml but never uses in index.php |
Same. The <positions> block only fills the picker; it renders nothing by itself. |
| Switched the site to a template with different position names | Every module in a dropped position goes quiet at once. |
The <positions> block is documentation for the administrator, not a
declaration to the framework. Templates in the tree already drift from it:
kimera renders breadcrumbs and endpage without declaring them, and
declares banner and introblock without rendering them.
Which position to put mod_upcoming_bookings in is therefore a question about
the hub's template, not about your module. right and left are the two most
widely shared names; both kimera and lucent render them. See
Layouts for the full list per
shipped template.
Rewritten and checked against 2.4-main @ 91d03d0a23 on 2026-09-10.