Document node type best practices

There are many solutions on how to render custom document node types, the most common propagated are the “Additional root matcher condition and page path copy” or “Additional PrimaryContent condition” approaches for custom document node types. (I can elaborate on them if someone needs more explanation to follow this).

I’d like to propose a different solution that I think plays out much nicer for different scenarios and is more consistent with the way content node types are rendered in Neos by default.

Document prototypes

The default root matcher condition is adjusted to not render the path page by default, but a prototype derived by the name of the current document node type:

root {
    default {
        type = ${q(node).property('_nodeType') + '.Document'}
        renderPath >

The base page rendering is defined in a custom prototype, so it can be re-used easily:

prototype(MyProject.MySite:DefaultPage) < prototype(Page) {
    body {
        templatePath = 'resource://MyProject.MySite/Private/Templates/Page/Default.html'
    // And many more adjustments to the Page

Note: This prototype should only contain the bare minimum, so no ContentCollections or other node type specific definitions should be placed here.

The actual rendering of document node types is defined like this:

prototype(TYPO3.Neos.NodeTypes:Page.Document) < prototype(MyProject.MySite:DefaultPage) {
    body {
        content {
            main = PrimaryContent {
                nodePath = 'main'
    // E.g. do some other adjustments of the Page object

This already renders documents of the type TYPO3.Neos.NodeTypes:Page correctly. But what about custom document node types?

For each custom document node type a prototype is defined that renders the document for that type:

prototype(MyProject.MySite:Product.Document) < prototype(MyProject.MySite:DefaultPage) {
    body.content.main = MyProject.MySite:Product

This will use the auto-generated TS prototype for the MyProject.MySite:Product node type to render the main content. All Page properties can be freely adjusted (e.g. include additional JS / CSS, change classes).

The nice thing is, that the auto-generated prototype already contains all of the document node type properties as TS properties, so there’s no need to map every property individually (myProperty = ${q(node).property('myProperty')}) for the rendering.

There’s a small caveat when using layouts (or formats) because they are not based on prototypes, but it works well by declaring the paths to render one of the document prototypes:

default = TYPO3.Neos.NodeTypes:Page.Document

landingPage = TYPO3.Neos.NodeTypes:Page.Document {
    // Adjust some properties here

Hey Christopher,

in short: I like the proposal very very much :slight_smile: Really good suggestion!

All the best,

I usually use just the same approach, but only one level below: my default TS object usually has a header and footer, and all shared TS code as menus, and then for main content area I render TS object based on nodetype:

But of course your approach is perfect as sane defaults.