Dynamically Highlighting Text in React Native
Or… How I learned to love React FlowBox.
This one should have been easy — or so I thought. I had a block of text, and I needed to highlight text in that block. I figured I’d whip something together, and be done in a few minutes. Then, I started WAY overthinking things.
Let’s walk through all the things I did wrong. :)
First — here’s my stated output:

The text came from this:
“text”: “Carefully, ‘{var var1}’ you say to the Grue! “
This is a JSON file containing the data I’m reading in. It could just as easily be a REST service or other item.
I’ve defined a pseudo-grammar to allow me to specify variables and other actions in the text, which allow me to control the program flow, none of which is the point of this article.
What I want to do is to highlight the var1 in that sentence, ensuring that it stands out to the user.
This sentence, if it was not dynamic, could be composed of the following:
And that would have been all well and good.
Except….
We need a dynamic method of generating this.
Our text is read in, parsed, and then displayed. This allows us to fill in variables, user effects, and other items from a text definition file.
Let’s start with the first part — breaking the text apart.
After looking into this, there are multiple ways — straight parsing, reg-ex, etc.
I decided to go with the prewritten regexify-string: https://www.npmjs.com/package/regexify-string
This allowed me to write code like:
(Yes, I know that line 5 — where I slice off the ‘{var …} and leave just the value name, needs to change for future iterations. For now, ignore it, please? :) )
This takes a simple RegEx and returns a new component, with a certain key and text. (In this case, the text is everything but the {var and }.
This returns an array with multiple entries in it — separated by the text we wanted to see:

Okay- great! That is 2 strings and a React Node. Just what we wanted.
First, I naively tried to just display the result.

Next, tried wrapping it in a <Text> block:
return <Text>{textToDisplayArray}</Text>;
This resulted in … something that looked like what I wanted:

Something seemed off though. I was trying to see why the highlighted text was off, it wasn’t lined up.
I called up Flipper for this:

Our var1 text has… a height of 53. Something else is odd though — this text item is a child of a different view group than our other text.

This text group is a height of 64, and appears to be anchored differently than the var1 group.
Another clue — looking at the text for this textview, we can see something interesting: “Carefully, ‘0’, you say to the Grue!’.
Where did that 0 come from?
Here, I used Flipper’s ability to edit on the fly — something that’s pretty useful:


So, where is this 0 coming from?
By default, when presented with an Array, React will attempt to concatenate it all together and display it.
Notice what happens if we make an array of just text strings:

There is only 1 React node now, and it has our array (3 slots: slot1, slot2, slot3) in a single React Text node.
If we add an object to this array — for every object we add, we get a new ‘0’, as the concatenation runs into a NULL and converts it to 0.
And here’s… how I ran off the rails. :)
First, I looked back at my simple example:
And decided that I needed to build a string with the components in it.
So… insert code like this:
textArray.forEach((item) => {
textList += item;
}
That went about as well as you might think — what this tries to do is to create a new string with all the items specified. Except… this is a string, and the string combines an object (the JSX specifier) and a String, so the entire thing is pretty much gobblety gook.
React certainly tried though — mostly due to Javascript and its inherent flexibility to handle objects.
Carefully, '[object Object]' you say to the Grue!
Still, not what we wanted —

Okay — so now that I was thinking a bit more clearly, I referenced my earlier static string and decided maybe I needed to embed it in a Text component so that everything was rendered correctly inline.
This parses the entire array and creates a new array, although every object is a JSX object:

Nope. Still not what I’m looking for.
Same issue as before.
At this point, I really went into the weeds — looking into how to concatenate JSX elements. Suffice it to say, there are some really neat answers out there:
- https://gist.github.com/granmoe/274c299b792b039deecfb619753ea32c
- https://stackoverflow.com/questions/50888615/dynamically-nested-react-components
- https://github.com/facebook/react/issues/3386
I spent a while studying these… but they both felt overly complex — like I was missing something.
I then briefly walked through React Fragments.
React Fragments are a way to return bits of JSX from a function, in a way that a higher-order component can use them.
For instance, a function can return a TR\TD setup from a function — one for each row. By itself the TR\TD isn’t valid — we would normally have to wrap it in a <div> — (or View in React Native), but then the Table isn’t valid.
So a React Fragment is a way around that.
Still, that would require reordering my code, and again, made the code feel inefficient and incorrect. So… it just didn’t sit right with me.
Then, I realized something. React Native is based around FlexBox, and FlexBox has something called… a row flex.
This is where the child components are rendered in a way that is not the default. The default is top to bottom. What I needed was left to right, wrapping around the screen.

We can see that our ending text is part of a ReactTextView, a peer of the other TextView. That’s a good start. The ReactViewGroup (our Pressable) has 1 and exactly 1 ReactTextView as a child.

All of this looks right.
So, in the end, we made a list of the objects we want to display…. and then display them in row order, instead of column order:
See? Way, way overthought this here. :)
The only thing left to do is to give the items a key — otherwise React complains at us. I have 2 kids, I don’t need more complaining.
This is easy — in our textList fat arrow, let’s add ‘key={index + 5000}’ to this.
That gives our original regular expression the first 4000 entries for found regular expressions… and the rest of our text everything after 4000.
And, with that… we’re done!