Skip to main content

Transient Render Engine

This article is an introduction to the ​TRE architecture.

Element Models

Element models form the building block of the engine. These models specify how a DOM element of a peculiar tag should be translated. You can tamper with those models and add you own models, making this library extremely customizable.

HTMLElementModel

To each standard tag is attached an element model, instance of the ​HTMLElementModel class. Such model has multiple fields describing different behaviors related to translation of those DOM elements. There are two ways to instantiate an element model:

  1. When changing an existing tag model with the ​HTMLElementModel.extend method. You can access default models via ​defaultHTMLElementModels;

  2. When registering a new tag with the ​HTMLElementModel.fromCustomModel static method.

You will have to register custom and extended models with the ​customHTMLElementModels prop, please refer to ​Custom Rendering for examples. Below is a list of fields that can be set when defining or extending HTML element models:

contentModel

How should this tag be translated? See next chapter.

isVoid

Will be true for void elements , e.g. DOM elements which can't have children.

isOpaque

Will be true for those elements which children should not be translated. Useful for ​<svg> and other custom markups.

mixedUAStyles

Mixed User-Agent styles, e.g. default styles for this element. This is how default styles are set for tags.

getMixedUAStyles

A function which returns mixed UA styles given a minimal ​TNodeDescriptor and a DOM ​Node.

reactNativeProps

An object with three fields, text, view and native. Each field value is a props object that will be passed to the underlying native component at render time. Respecively, for​Text, ​View and all (catch-all).

getReactNativeProps

Serves the same purpose as reactNativeProps, but it's a function taking a ​TNode as first argument, pre-generated props as second argument (such as accessibilityLabel derived from ​aria-label) and the DOM ​Element as a third argument.

HTMLContentModel

There are 4 content models that can be attached to a tag:

HTMLContentModel.textual

For elements which can be translated to ​TText or ​TPhrasing. Examples: ​<span>, ​<strong> ...

HTMLContentModel.block

For elements which can only be translated to ​TBlock. Examples: ​<div>, ​<p>, ​<article> ...

HTMLContentModel.mixed

(rare) for elements which can be translated to ​TText, ​TPhrasing or ​TBlock. The sole mixed elements are ​<a>, ​<ins> and ​<del>.

HTMLContentModel.none

For element which shall not be rendered and will be translated to ​TEmpty. Examples: ​<button>, ​<map> ...

A powerful feature of the Foundry engine is that the models attached to a tag name can be customized! See the ​Custom Rendering page.

Steps

The ​TRT construction is broadly comprised of three steps.

Translation

Each DOM element is translated to a ​TNode. The translation will obide by the following rules:

  1. The root of the document will be translated to a ​TDocument node. This node has a special context field which holds metadata harvested in the ​<head> DOM element (see ​DocumentMetadata).

  2. Text nodes will be translated to ​TText, and will be merged with a parent DOM element if the parent's content model is textual or mixed when they are its only child. For example, a Text node with no siblings which parent is a ​<span> will be merged into a ​TText with tagName set to "span".

  3. DOM elements which content model is textual with multiple children will be translated to ​TPhrasing nodes.

  4. DOM elements with children which content model is mixed will be translated to ​TPhrasing if they only have ​TPhrasing or ​TText children, ​TBlock otherwise.

  5. DOM elements which content model is block will be translated to ​TBlock nodes.

  6. Finally, DOM elements which content model is none will be translated to ​TEmpty.

  7. <script>, comments and ​<style> DOM nodes will be ignored.

note

A DOM element might be untranslatable for a variety of reasons. For example, its name is not a standard HTML5 element and there is no custom HTML element model registered for it. An other reason is that it is an interactive element such as a form, input or button.

In addition, inline styles, User Agent styles and mixed styles are processed by the CSS Processor, see ​CSS Processing for more details.

Below is an example of a translation transformation from HTML to ​TRT:

<a href="https://domain.com">
This is
<span>phrasing content</span>
<img src="https://domain.com/logo.jpg" />
and this is <strong>too</strong>.
</a>
<TDocument tagName="html">
<TBlock tagName="body">
<TPhrasing tagName="a" href="https://domain.com">
<TText anonymous data="\nThis is\n" />
<TText tagName="span" data="phrasing content" />
<TText anonymous data="\n" />
<TBlock tagName="img" src="https://domain.com/logo.jpg" />
<TText anonymous data="\n and this is " />
<TText tagName="strong" data="too" />
<TText anonymous data=".\n" />
</TPhrasing>
</TBlock>
</TDocument>
This flow depicts the translation step. The TRT is represented in a JSX-like format thanks to TNode.snapshot() method.
note

You will notice that a ​<body> has been added, and the root is an instance of ​TDocument. This process is called normalization, and is also performed by Web browsers.

Hoisting

The hoisting phase consists in enforcing a basic constraint:

The Hoisting Constraint

A ​TPhrasing node should only have ​TText, ​TPhrasing and ​TEmpty nodes as children.

Therefore, any ​TBlock child of a ​TPhrasing node will be recursively hoisted to the parent, until it meets that constraint. This constraint must be enforced to insure that a React Native ​Text elements have no ​View children, since it will break the default box model and might lead to bugs and inconsistencies. This constraint is depicted below:

The View Constraint

React Native ​Text elements should not have ​View elements as children.

On one hand ​TBlock will be translated to ​View elements and on the other hand ​TPhrasing and ​TText nodes will be translated to ​Text elements. Therefore, enforcing The Hoisting Constraint in the ​TRT results in enforcing The View Constraint at render time. You can disable hoisting via ​dangerouslyDisableHoisting prop, but be advised this is yet experimental.

Below is an example of translation + hoisting transformation from HTML to ​TRT:

<a href="https://domain.com">
This is
<span>phrasing content</span>
<img src="https://domain.com/logo.jpg" />
and this is <strong>too</strong>.
</a>
<TDocument tagName="html">
<TBlock tagName="body">
<TBlock tagName="a" href="https://domain.com">
<TPhrasing anonymous>
<TText anonymous data="\nThis is\n" />
<TText tagName="span" data="phrasing content" />
<TText anonymous data="\n" />
</TPhrasing>
<TBlock tagName="img" src="https://domain.com/logo.jpg" />
<TPhrasing anonymous>
<TText anonymous data="\n and this is " />
<TText tagName="strong" data="too" />
<TText anonymous data=".\n" />
</TPhrasing>
</TBlock>
</TBlock>
</TDocument>
Notice that contrary to the translate-only example, the <a> element is now wrapped in a TBlock. Also, text preceding and following the <img> tag are wrapped in an anonymous TPhrasing node.

Whitespace Collapsing

The whitespace collapsing phase consists in implementing the algorithm associated with the ​white-space CSS property, depicted in the CSS Text Module Level 3 standard, by which unsignificant white-spaces are removed from the ​TRT. You can disable hoisting via ​dangerouslyDisableWhitespaceCollapsing prop, but be advised this is yet experimental.

Below is an example of translating + hoisting + collapsing transformation from HTML to ​TRT:

<a href="https://domain.com">
This is
<span>phrasing content</span>
<img src="https://domain.com/logo.jpg" />
and this is <strong>too</strong>.
</a>
<TDocument tagName="html">
<TBlock tagName="body">
<TBlock tagName="a" href="https://domain.com">
<TPhrasing anonymous>
<TText anonymous data="This is " />
<TText tagName="span" data="phrasing content" />
</TPhrasing>
<TBlock tagName="img" src="https://domain.com/logo.jpg" />
<TPhrasing anonymous>
<TText anonymous data="and this is " />
<TText tagName="strong" data="too" />
<TText anonymous data="." />
</TPhrasing>
</TBlock>
</TBlock>
</TDocument>
Notice when comparing with the previous example, the line breaks and extraneous spaces have been removed.

Anatomy of a TNode

A ​TNode has the following relevant fields (see ​TNodeShape for a reference):

attributes

The list of attributes attached to the underlying DOM Node.

id

The id attached to the underlying DOM Node.

classes

An array of classes associated with the underlying DOM Node.

domNode

The underlying DOM Node, if present.

tagName

The tag name attached to the underlying DOM Node.

parent

The parent ​TNode, if present, determined before hoisting.

nodeIndex

The position of this element relative to its parent, after hoisting and collapsing.

children

An array of ​TNode descendents to this node.

type

The type of this ​TNode. Either text, phrasing, block, document or empty.

markers

A registry of markers for this ​TNode. See explaination in below section.

hasClass

A utility function to check if this node has the provided CSS class.

snapshot

A utility function to create a JSX-like string representation of this node and its children. Very handy for debugging.

warning

The styles field which is not listed here is not consumable as a React Native component style prop.

Markers

Markers form an abstraction in which one ​TNode provides semantic information to itself and all its descendants. For example, ​<ins> elements, which stand for "insertion" of content in the context of an edit will provide the edits marker with value "ins" to all its descendants. Similarly, ​<a>, ​<ol> and ​<ul> elements will set their own markers. List markers such as olNestLevel are integers which are incremented each time a list is nested.

Markers can also be derived from attributes. This is the case with ​dir and ​lang attributes. Finally, you can customize the markers extraction logic with ​setMarkersForTNode prop and the eponym HTML model field.