Rendering Macros

Last modified by superadmin on 2026/10/14 18:26

Explanation

A rendering macro is a component implementing the Macro role, registered under the name it is called by in wiki syntax. Rendering Macros describes what a macro is to the person reading the page; this one describes what a macro is in the code.

A Macro Is a Component

The name a macro is called by in the syntax is its component hint, and nothing registers it separately. The class below is what makes {{useravatar/}} work, declared in its module's META-INF/components.txt like any other component:

@Component
@Named("useravatar")
@Singleton
public class UserAvatarMacro extends AbstractMacro<UserAvatarMacroParameters>
{
    public UserAvatarMacro()
    {
        super("User Avatar", DESCRIPTION, UserAvatarMacroParameters.class);
        setDefaultCategories(Set.of(DEFAULT_CATEGORY_CONTENT));
    }

    @Override
    public List<Block> execute(UserAvatarMacroParameters parameters, String content,
        MacroTransformationContext context) throws MacroExecutionException
    {
        // Build and return the blocks the macro call is replaced by.
    }

    @Override
    public boolean supportsInlineMode()
    {
        return true;
    }
}

Changing the @Named value renames the macro. A hint of the form macroname/syntaxid, mymacro/xwiki/2.0 for instance, registers the macro for that one syntax instead of for all of them. Two components declared on the same hint are resolved by component priority, which is how an extension replaces a bundled macro without touching it.

A Macro Returns Blocks, Not Text

execute returns a list of Blocks, and the macro transformation substitutes them for the macro call in the XDOM, the tree the parser produced from the page. A renderer only ever sees that tree, once every macro in it has run. The components a macro injects to build those blocks are in Java Macro Helpers.

Two things follow, and they are what a first macro usually gets wrong:

  • The output is syntax-independent. A macro returning an ImageBlock renders in HTML, in a PDF export and in LaTeX alike, while one returning raw HTML renders in none of the others.
  • A macro may emit another macro call, and that call is executed in turn, because what the macro returned went back into the tree rather than into the output.

Macros are also not run in the order they are written. The transformation runs them by priority, lowest first, and AbstractMacro defaults to 1000. A macro that has to act before the ones its own content holds lowers it, as the container macro does:

@Override
public int getPriority()
{
    return 750;
}

Priority is one of several things the transformation decides rather than the macro. Macro Transformation describes how it runs the macros of a page, and Macro Execution Rules covers the rest, including when a macro is told not to execute at all.

The Parameters Bean Is What the Editors Show

A macro declares its parameters as a bean, and the descriptor the editors read is built from it. The annotations on each setter are the whole of the parameter metadata, so a parameter is described once, in the code:

public class UserAvatarMacroParameters
{
    private String username;

    @PropertyMandatory
    @PropertyDescription("the name of the user whose avatar is to be displayed")
    @PropertyDisplayType(UserReference.class)
    public void setUsername(String username)
    {
        this.username = username;
    }
}

@PropertyDescription is the help text beside the field, @PropertyMandatory makes the editor refuse an empty value, and @PropertyDisplayType replaces the plain text field by a picker, here the user picker. The Properties Module documents every annotation a parameter bean can carry. The categories the macro is listed under come from setDefaultCategories; AbstractMacro defines the bundled ones, listed in Macro Categories: Formatting, Content, Navigation, Development, Layout, Internal and Deprecated.

The Content Holds the Main Data

The main data of a macro goes in its content, between the opening and closing tags, whether it is rich text like a message or raw text like a code snippet. A parameter is for data that is always short, such as a title. The content is declared by a content descriptor, which the editors read like the parameters bean:

super("Info", DESCRIPTION,
    new DefaultContentDescriptor("Content of the message", true, Block.LIST_BLOCK_TYPE),
    MessageMacroParameters.class);

The description is the help text of the content field and the boolean makes the content mandatory. The type decides how the WYSIWYG editor edits it: Block.LIST_BLOCK_TYPE declares wiki content, edited in place like the rest of the page, while the default String declares raw text, edited in a text area of the "Edit Macro" dialog. When the content accepts less than the rest of the page, the description says what it accepts, so the restriction is visible before the macro fails to render it. Declaring the type is only half of it: the macro must also mark which of the blocks it returns are editable, as Make a Macro Editable Inline describes.

Java Macro or Wiki Macro

A macro can also be written as a wiki page carrying an XWiki.WikiMacroClass object, with no Java and no build, see Wiki Macros. That object holds the same metadata the Java descriptor does, plus the macro code itself and the visibility that decides which component manager the macro is registered in.

CharacteristicJava macroWiki macro
Easy to debug, and to write an automated test foracceptcancel
Optimized for performanceacceptcancel
Advanced parameter metadata, such as pickers and deprecationacceptaccept
Needs no development skills and no buildcancelaccept
Fast to write, and easy for a user to customize afterwardscancelaccept

Write a Macro in Java is the Java tutorial, and Write a Macro in a Wiki Page the wiki one.

Executing Content Is a Rights Decision

A macro that renders or executes the content it is handed runs what a page author wrote, so it also needs a required-rights analyzer. The analyzer does not enforce anything; it reports what the macro's content will need, and without one nothing tells the author to declare those rights or shows a reviewer what the page will run.

A macro whose result may be reused across requests extends AbstractExecutedContentMacro rather than driving the asynchronous renderer by hand; it then takes the standard async, cached and context parameters.

FAQ

Can a macro be used inside a sentence?

Only if its supportsInlineMode returns true. A macro that produces a paragraph or a group cannot, and using it inline is reported as an error instead.

Can an extension replace a bundled macro?

Yes. The macro name is the component hint, so a component declared on that hint at a stronger priority takes over, which is how the platform replaces the engine's toc macro with one that resolves a document reference.

How does a macro become asynchronous or cached?

By extending AbstractExecutedContentMacro. Whatever the macro reads while rendering has to be declared to AsyncContext, or the cached result survives the change that should have evicted it.

More

To find more about the current topic, you can search or use the table below and filter the columns to narrow your choices.

Related

Get Connected