When I write new blog posts for this website, I create them in a "draft" workspace. Over the years I sadly also created quite a bunch of posts, which never saw the light of the day, as I was never able to finish them. The problem is that I cannot differentiate which blog posts have never been published, while I'm in this "draft" workspace. To verify that, I would need to open the backend twice to compare or flip back and forth between workspaces. Both are not fun. So I present a better alternative that I came up with some days ago and which you can see the article image above.
The Eel helper
The first step is to create a new Eel helper in your (site) package with the following code. Make sure to adjust the namespace in line 5 to match yours.
<?php
declare(strict_types=1);
namespace My\Site\Eel;
use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\Eel\ProtectedContextAwareInterface;
use Neos\Neos\Domain\Service\ContentContextFactory;
class NodeHelper implements ProtectedContextAwareInterface
{
public function __construct(protected ContentContextFactory $contextFactory)
{
}
/**
* Returns true if the given node exists (even if hidden or removed) in its base workspace,
* or its workspace has no base workspace.
*/
public function existsInBaseWorkspace(NodeInterface $node): bool
{
$workspace = $node->getWorkspace();
// If the node is in a personal workspace, we need to check its base workspace instead
if ($workspace->isPersonalWorkspace()) {
$workspace = $workspace->getBaseWorkspace();
}
$baseWorkspace = $workspace->getBaseWorkspace();
if (!$baseWorkspace) {
return true;
}
$baseContext = $this->contextFactory->create([
'workspaceName' => $baseWorkspace->getName(),
'dimensions' => $node->getContext()->getDimensions(),
'targetDimensions' => $node->getContext()->getTargetDimensions(),
'invisibleContentShown' => true,
'removedContentShown' => true,
'inaccessibleContentShown' => true,
]);
return $baseContext->getNodeByIdentifier($node->getIdentifier()) !== null;
}
public function allowsCallOfMethod($methodName): bool
{
return true;
}
}
The helper accepts a node as parameter and checks whether it is in a sub-workspace and its base workspace. For my case that would be the check if it exists in the currently selected "draft" workspace and the "live" workspace. If that is the case or I'm currently looking at a node that is in the live workspace, the helper returns true.
Register the new helper
Neos:
ContentRepository:
labelGenerator:
eel:
defaultContext:
'My.Site.Node': 'My\Site\Eel\NodeHelper'
Adjust the nodetype labels
And now we override the label of the basic document nodetype mixin (or any nodetype you want to mark this way) to use this helper to conditionally add a prefix to the nodes in the document tree:
Neos.Neos:Document:
label: "${Neos.Node.labelForNode(node).properties('title').prefix(Shel.Site.Node.existsInBaseWorkspace(node) ? '' : '(DRAFT) ')}"
This change can be added into a new yaml file located in your package under NodeTypes/Override/Document.yaml for example.
When you now reload the document tree in your Neos backend while you are in a workspace that contains documents that don't exist in the "live" workspace yet, you should see the the prefix "(DRAFT)" in the label of those documents.
Using unicode
You can also get more creative by using unicode characters:

Summary
This post shows a compact and well manageable way to highlight nodes with specific conditions in the page tree. Hopefully we will find good ways to make this easier for future Neos versions, f.e. by allowing multiple icons for a node or custom color variations.
Please get in touch if this helped you to find other or even better ways to mark nodes and I gladly link or mention your input.