How do I add a custom action to the jContent menus, and can I override the Preview button?
Question
How do I add a custom action to the jContent menus, and can I override the Preview button?
Answer
A module registers a callback on jahiaApp-init, then registers its actions
inside it:
import {registry} from '@jahia/ui-extender';
import register from './MyModule/register';
export default function () {
registry.add('callback', 'myModule', {
targets: ['jahiaApp-init:50'],
callback: register
});
}
The priority matters. jContent registers at jahiaApp-init:1, so a module
must use a higher number to see or override jContent's own actions. Shipped
modules use 50.
Target names
| Target | Where it appears |
|---|---|
contentItemActions | the three-dot (kebab) menu on each row |
contentItemContextActions | right-click menu on each row |
visibleContentItemActions | the always-visible row icons, including the eye |
headerPrimaryActions | main header buttons |
selectedContentActions | header actions for a multi-row selection (gets paths) |
accordionContentActions | left tree context menu |
browseControlBar | kebab menu for the current node in the header |
Do not use contentActions. It has been deprecated since jContent 3.2.0 and
in current source the key is literally written '--contentActions' - the --
prefix means no action is assigned to it any more. Code targeting it does nothing.
To confirm a target name yourself rather than trusting a list, inspect the page:
jContent renders data-registry-key and data-registry-target attributes
(Jahia 8.1.6 and later).
An action that opens the render URL
This is the common case - a menu entry that opens /cms/render for the selected
node.
Do not build the URL by hand. There is a server-side GraphQL field that does it properly, respecting vanity URLs and URL rewriting:
{
jcr {
nodeByPath(path: "/sites/digitall/home") {
renderUrl(workspace: EDIT, language: "en", findDisplayable: true)
}
}
}
Run on 8.2.3.2 that returns:
/cms/render/default/en/sites/digitall/home.html
A hand-assembled string gives you the raw path and silently ignores any vanity URL the page has.
The action itself, modelled on jContent's own openInRepositoryExplorer:
export const OpenRenderActionComponent = ({path, render: Render, loading: Loading, ...others}) => {
const language = useSelector(state => state.language);
const res = useQuery(MY_RENDER_URL_QUERY, {variables: {path, language, workspace: 'EDIT'}, skip: !path});
if (res.loading && Loading) { return <Loading {...others}/>; }
const node = res?.data?.jcr?.result;
if (!node) { return <Render {...others} isVisible={false}/>; }
return (
<Render {...others}
isVisible
enabled
onClick={() => window.open(node.renderUrl, '_blank')}/>
);
};
registered as:
registry.add('action', 'openRender', {
targets: ['contentItemActions:10'],
buttonIcon: <Visibility/>,
buttonLabel: 'myModule:label.openRender',
component: OpenRenderActionComponent
});
Two things that trip people up:
- Your component must not render a button. It renders the injected
Rendercomponent and passesisVisible,enabledandonClickto it. - The node path arrives as the
pathprop - a string. For multi-selection targets you getpaths, an array, instead. There is nouuidprop; get it from a query or fromuseNodeChecks({path}, ...). Anodeprop is sometimes prefetched but is not always supplied, so do not depend on it.
Overriding the Preview button
registry.add throws if the key already exists. Use registry.addOrReplace,
and spread the existing descriptor so you keep the parts you are not changing -
this is what jExperience does in production:
registry.addOrReplace('action', 'edit', {
...registry.get('action', 'edit'),
hideOnNodeTypes: [...]
});
But first: there are two Preview buttons, both with an eye icon and both labelled "Preview".
| Key | What it does | |
|---|---|---|
| Eye icon on each row | preview | opens the in-app preview side panel |
| Preview button in the header | openInPreview | already opens the render URL in a new tab |
So if what you want is "Preview should open the rendered page in a new tab", the header button already does that - check it before writing code.
If you do override preview, be aware it changes the action everywhere it
appears, not just the row icon: it is also in the kebab and context menus. Adding
a new action is usually cleaner than overriding that one.
"Edit in a new tab" from the ordering section
Not available. The ordering UI renders four hardcoded move buttons and nothing else, and there is no registry target in that part of the tree - so it cannot be contributed by a module either.
The feature exists only as an open, unmerged pull request against jContent (#2325). Until that lands there is no supported way to do it short of forking.
This article was drafted with AI assistance, then reviewed and curated by Jahia Customer Support engineers before publication.