It has been over three months since the first final version of the Foundry release has been made public. Today, I am glad to announce the release of the 6.2 version! This new version focuses on two areas of improvements:
- Accessibility, and more specifically integration with VoiceOver and TalkBack screen readers;
- Richer model-based rendering, and the feature to define React Native props from those models.
As you will discover through this reading, both are somehow related. Let's find out how!
This post also covers a due overview of features introduced in the previous 6.1 minor release.
Model-based rendering via
customHTMLElementModels prop has been a lightweight alternative to
custom (component) renderers since the early stages of the Foundry release.
However, it was limited to setting user agent styles (
although those styles could be derived from the DOM node element attributes
The below example is a reminder on how those element models can be defined, for instance to
register a new
<blue-circle> tag which renders to a 50 by 50 blue circle!
Version 6.2 ships with a bunch of new fields for HTML element models which should increase model-based rendering adoption. Let's take a tour!
This field deprecates
getUADerivedStyleFromAttributes; it serves the same purpose but its
signature has changed. It now receives the target
tnode and DOM
which lets us implement more fine-grained logic such as CSS-selector-like
However and as stated before, those styles will have a lower specificity than tags, classes and ID styles and as such, these are not "real" CSS selectors. Hopefully, CSS selectors will be implemented at some point, you can upvote the feature request here! The hard challenge is that these should not impede performances. I am planning to explore this issue next year.
Beware that this
tnode is an instance of
TNodeDescriptor, which is a minimal
tnode shape available during the Transient Render
Engine creation. You will not have access to
since the hierarchy is yet in the making. This is why we are using the second
element, to query the DOM hierarchy instead. For this very reason,
you are advised to use
to query the DOM and create your conditional styling rules.
This field holds props that will be passed to the native component during the
rendering phase. It is an object with three optional properties (for reference,
the shape of this object is a
text, to pass native props to
view, to pass native props to
native, to pass props to either
In the next snippet, we are defining a custom tag,
<nav-widget>, and setting
accessibility props to any underlying native component for the render phase. We
can also define
onPress, which will cause the renderer to use the
GenericPressable instead of
View for block renderers.
However, this field is somehow limited in that it cannot depend on
This is where
getReactNativeProps comes to the rescue!
The purpose of this field is identical to
reactNativeProps field. It only differs in that instead of a plain
object, it is a method which takes three arguments. A
tnode (the Transient Node),
preGeneratedProps (props generated by the TRE such as accessibility props, see next section)
element (the DOM node). Finally, it returns a plain object (see
In the example below, a custom
nav-widget tag is registered. This time, we
onPress events conditionally, based on attributes of the
tnode. The snippets uses a phony API,
appNavigatorController, to navigate
between screens. Such API is easy to implement with a globally-defined ref to a
react-navigation "navigation" object.
It is worth noting that you cannot use React hooks in those element models functions. But you can use any ad-hoc API to emit events, and glue that logic to hooks. If you really need React hooks, it might be simple to create a custom component renderer.
Don't forget that you can mix model-based and component-based rendering!
Screen readers integration has been worked on sparsely since the Foundry release, by addressing issues raised by the community, but until v6.2 there has not been structural improvements to cover the full range of required features. Thanks to the new Transient Render Engine, it has become very easy to define translations from HTML attributes to React Native props within the engine. Let's find out what's new!
On one hand,
is used to hint screen readers on how a specific node and all its descendants should be read out loud. It is especially
useful to handle icons which don't have inner semantic meanings. This attribute has a React Native prop equivalent, namely
accessibilityLabel, which serves the same purpose.
On the other hand,
informs the screen reader of the target element nature and how a user might interact with it.
This attributes roughly maps to React Native
prop, although the set of allowed values slightly differs.
See all supported roles and their mapping
Remember that a majority of interactive elements will not be rendered by this library. You must change their
content model to
block in order for them to be rendered.
Nevertheless, those interactive element models are already shipped with the appropriate
The new Transient Render Engine will from now on translate both attributes to their React Native counterparts.
<a> tags now receive an
accessibilityRole="link" prop when their
attribute is non-empty. To link that part with model enhancements seen in
the previous section, let's see now how we could set an accessibility
extending the HTML model:
method can now take a generator function. Pretty convenient to merge
Because of a React Native
Text elements are not accessible, which means that the screen reader will not
be able to identify
<a> tags as links when grouped with other textual
elements. Below is an example:
Luke Walczak from Callstack explains how to circumvent this issue in a great post. Unfortunately, this workaround cannot be genericized and we will have to wait for an upstream fix.
Enhanced Accessibility for
<img> tags have been accessible since the Foundry beta. But the accessibility
props were set after the loading was complete. We have found that changing
accessibility annotations on the fly can degrade aural
experiences and have provided a fix.
accessibilityRole="image" will be set for
<img> only when either
aria-label attribute is present.
React Native has a “header” accessibility role which screen reader users depend
on widely to identify quickly the content hierarchy of a screen. Until this
react-native-render-html did not pass the appropriate role to
user-select CSS property#
With the new Transient Render Engine featuring React Native prop generation, it
has become very easy to pass the
selectable prop to React Native
components based on the presence of
user-select CSS property. Usage example:
Please not that this is not full support. The TRE will map
user-select: none; to
any other value to
I didn't publish a release notes post for this version; I'm catching up here! From now on, I will try to write up a post for each minor and major release.
This recommended prop allows consumers to circumvent a bug in the Foundry
release where line breaks (
<br>) would be printed erroneously, such
as at the end of a paragraph. Its default it yet
false to avoid introducing
breaking changes but it will be enabled by default in the next major
Read the complete explanation for this prop in the textual content guide.
This recommended prop allows to circumvent a React Native
bug where empty
elements would be printed as ghost lines of fixed height (around 20 dpi). Its
default it yet
false to avoid introducing breaking changes but it will be
enabled by default in the next major release.
Read the complete explanation for this prop in the textual content guide.
A function prop which allows to generate HTTP headers for
remote resources. It currently works with
<iframe> tags (since version 2.6 of the
See for example how it can be used with images in the image content guide.
true by default, makes the React rendering layer bypass grouping
TPhrasing nodes which have only one child. It is best understood by
example. For instance, the following HTML snippet:
will produce the below Transient Render Tree:
which by default rendering rules, would be translated to the below React render tree:
bypassAnonymousTPhrasingNodes prop is
true (the default),
the render tree will be simplified to:
This behavior is preferred for many reasons. The most simple one is that it
simplifies the render tree. The less React elements there are, the best it is
performance-wise. Moreover, there are a lot of React Native bugs related to
Text nodes, so this simplification limits the number of occurrences
of those bugs.