Component Fusion Objects

Components are the main building blocks of Fusion which contain markup and templates. They provide encapsulation and reusability.

In case your Fusion component should render a Content Node, make sure it extends from Neos.Neos:ContentComponent.

In case your Fusion component is a plain "Atom" which has no connection and knowledge of a Node, extend from Neos.Fusion:Component.

#Neos.Fusion:Component

Neos.Fusion:Component is the base component prototype. It takes its properties and exposes them on the renderer as props. This leads to well-encapsulated components with a defined API, looking roughly like this:

fusion
prototype(My.Package:MyComponent) < prototype(Neos.Fusion:Component) {
  # public API
  title = 'Bold text'
  description = 'This is a description'

  # implementation
  renderer = afx`
    <p class="my-component">
      <strong>{props.title}</strong><br/>
      <span>{props.description}</span>
    </p>
  `
}

Output example:

My Title
This is a description

Neos.Fusion:Component allows arbitrary markup

With Neos.Fusion:Component, you can create arbitrary markup which is not modified in any way.

#Neos.Neos:ContentComponent

Additionally, there is  Neos.Neos:ContentComponent , which provides integration into the Neos UI. 

It inherits from Neos.Fusion:Component and adds the Content Element Wrapping. The content element wrapping is responsible for ensuring a Node (represented by a Fusion object) is properly usable inside the Neos Backend. In detail, it does the following:

  • Ensure that a single wrapping tag exists inside the component. If not, a wrapping <div> is added.
  • The wrapping <div> gets additional CSS classes and data attributes if rendered in the backend, needed for the Neos User Interface - containing f.e. the currently rendered node's identifier, the currently rendered Fusion path, and the Node's properties as JSON.
  • For this to work, the current node is read from the Fusion context by Neos.Neos:ContentElementWrapping.

#What features does the Content Element Wrapping enable?

Content element wrapping is the basis for the following features:

  • the possibility to select a Node in the Neos backend, directly on a page.
  • the blue border around a selected node, and the floating toolbar attached to it.
  • If a new child node is added, refresh only the single node, and not the whole page.
  • If the node is removed, remove the node without refreshing the whole page.
  • If a property is changed in the Inspector (and it is marked with reloadIfChanged: true in NodeTypes.yaml), reload only the single Node and not the whole page.

#How should Neos.Neos:ContentComponent be used?

A common pattern is to define the markup inside a plain Neos.Fusion:Component, and then reuse that inside a Neos.Neos:ContentComponent, like in the following example:

fusion
prototype(My.Package:Content.Section) < prototype(Neos.Neos:ContentComponent) {
  # Edited in inspector
  title = ${q(node).property('title')}
  # Inline Edited
  description = Neos.Neos:Editable {
    property = "description"
  }

  # delegate the implementation to the plain component
  renderer = My.Package:Atoms.Section {
    title = ${props.title}
    description = ${props.description}
  }
}

prototype(My.Package:Atoms.Section) < prototype(Neos.Fusion:Component) {
  # public API
  title = ''
  description = ''

  # implementation
  renderer = afx`
    <section class="my-component">
      <h2>{props.title}</h2>
      <div>{props.description}</div>
    </section>
  `
}

#Internal Implementation

Sometimes it is useful to check out the internal implementation of a Fusion prototype, as this can help to understand how the various Fusion objects work together.

So, let's have a look into the internal implementation of Neos.Neos:ContentComponent:

Neos.Neos/Resources/Private/Fusion/Prototypes/ContentComponent.fusion
# internal implementation, slightly shortened
prototype(Neos.Neos:ContentComponent) < prototype(Neos.Fusion:Component) {
  @process.contentElementWrapping {
    expression = Neos.Neos:ContentElementWrapping
    @position = 'end 999999999'
  }
}

So, you see there is not much magic going on here:

  • We extend from Neos.Fusion:Component
  • Via @process, we take the full output and apply the content element wrapping (which does what is explained above).

#Neos.Neos:Editable

For making a property inline-editable using the Rich Text Editor, Neos.Neos:Editable is used - usually inside a Neos.Neos:ContentComponent. This looks usually like this:

fusion
prototype(My.Package:Content.Section) < prototype(Neos.Neos:ContentComponent) {
  description = Neos.Neos:Editable {
    property = "description"
  }

  renderer = afx`
    <section class="my-component">
      <div>{props.description}</div>
    </section>
  `
}

It is recommended to delegate the markup rendering to a Neos.Fusion:Component, like in the example above ("How should Neos.Neos:ContentComponent be used?"). In this case, we recommend to keep the Neos.Neos:Editable inside the Neos.Neos:ContentComponent

It is bad practice to use a Neos.Neos:Editable inside a plain Neos.Fusion:Component, as it breaks the logical layering and might lead to inconsistencies in the Neos UI.

#Legacy Prototypes

#Neos.Fusion:Template

Neos.Fusion:Template is a Fluid-backed component. For new projects, you should not use Fluid anymore to define the frontend rendering of your output. Instead, use Neos.Fusion:Component and AFX.

#Neos.Neos:Content

Neos.Fusion:Content is a Fluid-backed content component. For new projects, you should not use Fluid anymore to define the frontend rendering of your output. Instead, use Neos.Neos:ContentComponent and AFX.