Fabric-first rich text renderer for React Native with NativeWind/Tailwind CSS support
npm install react-native-fabric-rich-textFabric-first HTML text renderer for React Native with iOS, Android, and web support.
- Features
- Installation
- Usage
- NativeWind Integration
- RTL Support
- Props
- Events
- Type Exports
- Error Handling
- Testing
- Requirements
- Contributing
- License
- Native Fabric component for optimal performance
- HTML parsing and rendering (bold, italic, lists, links)
- Link detection (URLs, emails, phone numbers)
- Custom tag styles via tagStyles prop
- XSS protection with built-in sanitization
- NativeWind/Tailwind CSS integration via /nativewind export
- RTL (Right-to-Left) text support with bdi, bdo, and dir attributes
``sh`
npm install react-native-fabric-rich-textor
yarn add react-native-fabric-rich-text
`sh`
cd ios && pod install
`tsx
import { RichText } from 'react-native-fabric-rich-text';
export default function App() {
return (
Hello world
"$3
`tsx
text='Visit our site
'
onLinkPress={(url, type) => {
console.log(Pressed ${type}: ${url});
}}
/>
`$3
`tsx
text="Normal bold red and italic blue
"
tagStyles={{
strong: { color: '#CC0000' },
em: { color: '#0066CC' },
}}
/>
`$3
`tsx
text="Call 555-123-4567 or email support@example.com
"
detectPhoneNumbers
detectEmails
onLinkPress={(url, type) => {
// type will be 'phone' or 'email'
}}
/>
`NativeWind Integration
This library supports NativeWind for Tailwind CSS styling in React Native.
> Full setup guide: See docs/nativewind-setup.md for complete babel, metro, and tailwind configuration instructions.
$3
`sh
Install NativeWind and Tailwind CSS (3.x required)
npm install nativewind
npm install -D tailwindcss@">=3.3.0 <4.0.0"
`$3
Import from the
/nativewind subpath for zero-config Tailwind CSS support:`tsx
import { RichText } from 'react-native-fabric-rich-text/nativewind';function MyComponent() {
return (
text="
Hello World
"
className="text-blue-500 text-lg font-medium p-4"
/>
);
}
`$3
`tsx
text="Responsive text
"
className="text-sm md:text-base lg:text-lg"
/>
`$3
`tsx
text="Theme-aware text
"
className="text-gray-900 dark:text-gray-100"
/>
`$3
For more control, apply
cssInterop yourself:`tsx
import { RichText } from 'react-native-fabric-rich-text';
import { cssInterop } from 'nativewind';// Apply once at app startup
cssInterop(RichText, { className: 'style' });
function MyComponent() {
return (
text="
Hello World
"
className="text-blue-500"
/>
);
}
`$3
Add NativeWind types to your project:
`typescript
// nativewind-env.d.ts
///
`$3
- NativeWind ^4.1.0
- Tailwind CSS 3.x
RTL (Right-to-Left) Support
Full support for RTL languages including Arabic, Hebrew, and Persian.
$3
RTL scripts are automatically detected and rendered correctly:
`tsx
`$3
Use the
dir attribute to control text direction:`tsx
// Explicit RTL
// Explicit LTR
// Auto-detect from first strong character
`$3
Control direction at the component level:
`tsx
// Force RTL for entire component
text="This will render RTL
"
writingDirection="rtl"
/>// Force LTR
text="
مرحباً
"
writingDirection="ltr"
/>// Auto-detect (default)
text="
Text
"
writingDirection="auto"
/>
`$3
The
tag isolates bidirectional text to prevent it from affecting surrounding content. Useful for user-generated content:`tsx
`$3
The
tag forces text direction, overriding the natural direction:`tsx
// Force RTL
// Force LTR within RTL context
`$3
RTL text with embedded LTR content (numbers, brand names) is handled automatically:
`tsx
`$3
All text formatting works with RTL:
`tsx
text="مهم: نص مائل وتحته خط
"
/>
`$3
On React Native, the component respects
I18nManager.isRTL as the default base direction when writingDirection="auto" (the default).Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
|
text | string | - | HTML markup to render (required) |
| style | TextStyle | - | Style applied to the text |
| className | string | - | Tailwind CSS classes (requires /nativewind import) |
| testID | string | - | Test identifier for testing frameworks |
| tagStyles | Record | - | Custom styles per HTML tag |
| onLinkPress | (url: string, type: DetectedContentType) => void | - | Callback when a link is pressed |
| onLinkFocusChange | (event: LinkFocusEvent) => void | - | Callback when accessibility focus changes between links |
| onRichTextMeasurement | (data: RichTextMeasurementData) => void | - | Callback with line count data after layout |
| detectLinks | boolean | false | Auto-detect URLs in text |
| detectPhoneNumbers | boolean | false | Auto-detect phone numbers |
| detectEmails | boolean | false | Auto-detect email addresses |
| numberOfLines | number | 0 | Limit text to specified lines (0 = unlimited) |
| animationDuration | number | 0.2 | Height animation duration in seconds |
| writingDirection | 'auto' \| 'ltr' \| 'rtl' | 'auto' | Text direction |
| allowFontScaling | boolean | true | Enable font scaling for accessibility |
| maxFontSizeMultiplier | number | 0 | Maximum font scale (0 = unlimited) |
| includeFontPadding | boolean | true | Android: include font padding |> Note: On iOS, enabling
detectEmails also enables URL detection due to platform limitations.Events
$3
Fired when the user taps a link, phone number, or email.
`tsx
text='Visit our site
'
onLinkPress={(url, type) => {
// type: 'link' | 'email' | 'phone'
console.log(Pressed ${type}: ${url});
}}
/>
`$3
Fired when accessibility focus moves between links (screen readers, keyboard navigation).
`tsx
import type { LinkFocusEvent } from 'react-native-fabric-rich-text'; text='
'
onLinkFocusChange={(event: LinkFocusEvent) => {
// event.focusedLinkIndex: -1 (container), 0+ (link index), null (lost focus)
// event.url: string | null
// event.type: 'link' | 'email' | 'phone' | 'detected' | null
// event.totalLinks: number
console.log(Focused link ${event.focusedLinkIndex} of ${event.totalLinks});
}}
/>
`$3
Fired after text layout with line count information. Useful for "Read more" patterns.
`tsx
import type { RichTextMeasurementData } from 'react-native-fabric-rich-text';const [isTruncated, setIsTruncated] = useState(false);
text="
Long text content...
"
numberOfLines={2}
onRichTextMeasurement={(data: RichTextMeasurementData) => {
// data.measuredLineCount: total lines without truncation
// data.visibleLineCount: lines actually displayed
setIsTruncated(data.measuredLineCount > data.visibleLineCount);
}}
/>
{isTruncated && Show more }
`Type Exports
`tsx
import {
RichText,
FabricRichText,
sanitize,
ALLOWED_TAGS,
ALLOWED_ATTR,
} from 'react-native-fabric-rich-text';import type {
RichTextProps,
DetectedContentType, // 'link' | 'email' | 'phone'
LinkFocusEvent,
LinkFocusType, // DetectedContentType | 'detected'
RichTextMeasurementData,
WritingDirection, // 'auto' | 'ltr' | 'rtl'
} from 'react-native-fabric-rich-text';
`Error Handling
$3
Returns
null if text is empty or contains only whitespace:`tsx
// Returns null
// Returns null
// Renders empty paragraph
`$3
All HTML is sanitized before rendering. Dangerous content is automatically removed:
`tsx
// Script tags are removed
// Renders: Safe// Event handlers are stripped
// Renders: Click me
// javascript: URLs are blocked
// Renders: Link (href removed)
`$3
Access the sanitization allowlists:
`tsx
import { ALLOWED_TAGS, ALLOWED_ATTR } from 'react-native-fabric-rich-text';console.log(ALLOWED_TAGS);
// ['p', 'div', 'h1', ..., 'bdi', 'bdo', 'ul', 'ol', 'li', ...]
console.log(ALLOWED_ATTR);
// ['href', 'class', 'dir']
`Testing
$3
The library includes comprehensive test coverage:
`sh
Run all tests
yarn testRun tests with coverage
yarn test --coverage
`$3
Use the
testID prop for testing:`tsx
text="Test content
"
testID="my-rich-text"
/>
`On native platforms, this sets the accessibility identifier. On web, it sets
data-testid.$3
See the
/e2e` directory for Maestro-based end-to-end tests.- React Native >= 0.81 (New Architecture / Fabric required)
- iOS >= 15.1
- Android API >= 21
- Development workflow
- Sending a pull request
- Code of conduct
MIT
---
Made with create-react-native-library