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.