FormattedString and Span
Rich text inside a single text component:
<Label textWrap="true">
<FormattedString>
<Span text="14 " fontSize="24" fontWeight="bold" />
<Span text="new items " color="#2563eb" />
<Span text="since yesterday" color="#6b7280" />
</FormattedString>
</Label>
import { FormattedString, Span } from '@nativescript/core';
const fs = new FormattedString();
const bold = new Span();
bold.text = '14 ';
bold.fontWeight = 'bold';
fs.spans.push(bold);
label.formattedText = fs;
The traps
label.text = '...'whileformattedTextis set is a SILENT NO-OP on both platforms. The formatted text keeps rendering, nothing throws, and the "why won't my label update" hunt begins. To go back to plain text, clear the formatted text first:label.formattedText = null; label.text = 'plain';- The colour property is
color. Spans style with the same CSS-ish properties as text views (color,backgroundColor,fontSize,fontFamily,fontWeight,fontStyle,textDecoration) — there is no functioningforegroundColor. Span.textreplaces only the FIRST\n/\twhen collapsing whitespace — multi-line span content doesn't behave like a Label'stext. One span per line (or embed real line breaks via separate spans) is the reliable structure.- A span becomes tappable purely by having a
linkTaplistener — no property to set:
const link = new Span();
link.text = 'View all';
link.color = new Color('#2563eb');
link.on(Span.linkTapEvent, () => openAllItems());
fs.spans.push(link);
// iOS additionally needs user interaction enabled on the owning text view for links inside selectable text.
- Spans inherit unset properties from the owning text view — set base styling on the Label and override per span, not the reverse.
formattedTextparticipates in bindings like any property; but per-span bindings multiply observers — for long lists prefer rebuilding the FormattedString on data change.
When NOT to use it
Separate Labels in a layout beat FormattedString when pieces need independent layout (wrapping columns, tap targets bigger than the text). FormattedString is for inline runs within one flowing text.
Verified 2026-08 against @nativescript/core 9.1.0-rc.3: the text/formattedText no-op read from packages/core/ui/text-base/index.{ios,android}.ts, span colour property and first-newline collapse from ui/text-base/span.ts, linkTap wiring from span.ts; not re-run standalone.