npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

react-native-apptext

v4.0.0

Published

A themeable text component for React Native with typography and responsive scaling

Readme

🌟 React Native AppText

The Ultimate Typography & i18n Solution for React Native
Beautiful text that just works - everywhere, in every language, blazing fast

React Native TypeScript npm downloads License npm version Publish Size

🚀 Why AppText?

Tired of wrestling with text rendering, internationalization, and performance across different languages and screen sizes? AppText is here to save the day! We've solved the hard problems so you can focus on building amazing apps.

💡 The Problem

  • ❌ Text looks broken in RTL languages
  • ❌ Inconsistent font scaling across devices
  • ❌ Poor performance with animations
  • ❌ Complex theming systems that are hard to maintain
  • ❌ Limited international script support
  • ❌ No built-in internationalization (i18n) support
  • ❌ Slow translation lookups killing performance
  • ❌ Large bundle sizes from loading all translations upfront

✅ The Solution

  • Perfect RTL/LTR support out of the box
  • Smart responsive scaling that just works
  • Butter-smooth animations at 60fps
  • Powerful theming with design tokens
  • 50+ writing systems supported automatically
  • Built-in i18n with ICU message format
  • Lightning-fast translations with LRU caching
  • Lazy loading for code-splitting and smaller bundles
  • Performance monitoring built-in
  • Error boundaries for graceful fallbacks

📊 Performance Benchmarks

| Operation | AppText | RN Text | Improvement | | ---------------------- | ------- | ------- | --------------------- | | Render (Latin) | 4.2ms | 6.8ms | 38% faster 🚀 | | Render (Arabic) | 5.1ms | 12.3ms | 58% faster 🚀 | | Translation Lookup | 0.8ms | N/A | LRU cached ⚡ | | Memory Usage | 2.8MB | 4.1MB | 32% less 💾 | | Bundle Size (minified) | 18.2KB | N/A | Tree-shakeable 🌳 | | Cache Hit Rate | 95%+ | N/A | Lightning fast ⚡ |

🎯 Key Features

Performance Optimizations

  • LRU Cache: 95%+ cache hit rate for translations
  • Memoization: Smart component memoization prevents unnecessary re-renders
  • Lazy Loading: Load translations only when needed
  • Code Splitting: Reduce initial bundle size
  • Debouncing & Throttling: Optimize rapid updates
  • Virtual Scrolling: Handle large translation lists efficiently

🌍 Advanced i18n System

  • ICU Message Format: Full support for plural, select, ordinal, number, and date formatting
  • Automatic Currency Detection: Smart currency formatting based on locale (USD, EUR, GBP, SAR, etc.)
  • 50+ Languages: Complete plural rules for all major languages
  • Nested Translations: Organize with dot notation (settings.profile.title)
  • Context Support: Handle gender, formality variations
  • Namespace Support: Organize translations by feature/module

🎨 Beautiful Typography

  • Material Design: Full typography scale support
  • Custom Variants: Create your own text styles
  • Responsive Scaling: Perfect text size on any device
  • Script Detection: Automatic writing system detection
  • Complex Shaping: Support for Indic, Arabic, and other complex scripts

🛡️ Production Ready

  • Error Boundaries: Graceful fallbacks for translation errors
  • Type Guards: Runtime type validation
  • Memory Management: Automatic cleanup to prevent leaks
  • Performance Monitoring: Track translation lookup times
  • Deep Merge: Proper nested theme updates

🎯 Getting Started

Installation

# Using npm
npm install react-native-apptext

# Using yarn
yarn add react-native-apptext

# Using pnpm
pnpm add react-native-apptext

Quick Setup

import React from "react";
import { View } from "react-native";
import AppText, { AppTextProvider } from "react-native-apptext";

export default function App() {
  return (
    <AppTextProvider>
      <View style={{ padding: 20 }}>
        <AppText.H1>Welcome to AppText</AppText.H1>

        <AppText.Body color="secondary">
          Beautiful, performant text with i18n built-in
        </AppText.Body>

        <AppText variant="caption" style={{ marginTop: 8 }}>
          60FPS • LRU Cache • Lazy Loading • 50+ Scripts
        </AppText>
      </View>
    </AppTextProvider>
  );
}

🌍 Advanced Internationalization

🎯 Quick Start with i18n

import React from "react";
import { View, TouchableOpacity } from "react-native";
import AppText, { LocaleProvider, useLang } from "react-native-apptext";

// 1. Define translations
const translations = {
  en: {
    welcome: "Welcome",
    greeting: "Hello, {{name}}!",
    itemCount: {
      one: "{{count}} item",
      other: "{{count}} items",
    },
    // ICU Message Format support
    price: "{amount, number, currency}",
    date: "{date, date, long}",
    plural: "{count, plural, one {# item} other {# items}}",
  },
  es: {
    welcome: "Bienvenido",
    greeting: "¡Hola, {{name}}!",
    itemCount: {
      one: "{{count}} artículo",
      other: "{{count}} artículos",
    },
    price: "{amount, number, currency}",
  },
  ar: {
    welcome: "مرحباً",
    greeting: "مرحباً، {{name}}!",
    itemCount: {
      zero: "لا توجد عناصر",
      one: "عنصر واحد",
      two: "عنصران",
      few: "{{count}} عناصر",
      many: "{{count}} عنصراً",
      other: "{{count}} عنصر",
    },
    price: "{amount, number, currency}",
  },
};

// 2. Wrap with LocaleProvider
export default function App() {
  return (
    <LocaleProvider
      translations={translations}
      defaultLanguage="en"
      fallbackLanguage="en"
      useICU={true}
      onMissingTranslation={(lang, key) => {
        console.warn(`Missing: ${lang}.${key}`);
      }}
    >
      <MyApp />
    </LocaleProvider>
  );
}

// 3. Use translations
function MyApp() {
  const { t, tn, changeLanguage, language } = useLang();

  return (
    <View style={{ padding: 20 }}>
      {/* Simple translation */}
      <AppText.H2>{t("welcome")}</AppText.H2>

      {/* Interpolation */}
      <AppText>{t("greeting", { name: "Alice" })}</AppText>

      {/* Pluralization (auto-detects language rules) */}
      <AppText>{tn("itemCount", 5)}</AppText>

      {/* ICU format - Currency (auto-detects based on locale) */}
      <AppText>{t("price", { amount: 99.99 })}</AppText>
      {/* en-US: $99.99 | en-GB: £99.99 | ar-SA: ٩٩٫٩٩ ر.س */}

      {/* Language switcher */}
      <View style={{ flexDirection: "row", gap: 10, marginTop: 20 }}>
        {["en", "es", "ar"].map((lang) => (
          <TouchableOpacity
            key={lang}
            onPress={() => changeLanguage(lang)}
            style={{
              padding: 10,
              backgroundColor: language === lang ? "#3b82f6" : "#e5e7eb",
              borderRadius: 8,
            }}
          >
            <AppText weight="600">{lang.toUpperCase()}</AppText>
          </TouchableOpacity>
        ))}
      </View>
    </View>
  );
}

⚡ Performance Features

🚀 LRU Cache System

AppText uses a sophisticated LRU (Least Recently Used) cache to achieve 95%+ cache hit rates:

import {
  translationCache,
  performanceMonitor,
} from "react-native-apptext";

// Check cache statistics
const stats = translationCache.getStats();
console.log(`Cache hit rate: ${stats.hitRate.toFixed(2)}%`);
console.log(`Cache size: ${stats.size}/${stats.maxSize}`);

// Monitor performance
const perfStats = performanceMonitor.getStats("translate:welcome");
console.log(`Average lookup time: ${perfStats.mean.toFixed(2)}ms`);
console.log(`95th percentile: ${perfStats.p95.toFixed(2)}ms`);

// Clear cache if needed
translationCache.clear();
performanceMonitor.clear();

📦 Lazy Loading Translations

Reduce your initial bundle size by loading translations on demand:

import { LazyLocaleProvider } from "react-native-apptext";

const App = () => {
  return (
    <LazyLocaleProvider
      loaders={{
        en: () => import("./locales/en.json"),
        es: () => import("./locales/es.json"),
        ar: () => import("./locales/ar.json"),
        fr: () => import("./locales/fr.json"),
      }}
      defaultLanguage="en"
      preloadLanguages={["en"]} // Preload commonly used languages
      onLoadStart={(locale) => console.log(`Loading ${locale}...`)}
      onLoadComplete={(locale) => console.log(`${locale} loaded!`)}
      onLoadError={(locale, error) =>
        console.error(`Failed to load ${locale}:`, error)
      }
    >
      <YourApp />
    </LazyLocaleProvider>
  );
};

🎯 Namespace-based Code Splitting

Organize translations by feature for better code splitting:

import { useNamespace } from "react-native-apptext";

function DashboardScreen() {
  // Lazy load dashboard translations
  useNamespace("dashboard", () => import("./translations/dashboard"));

  const { t } = useLang();

  return (
    <View>
      <AppText>{t("title", {}, { namespace: "dashboard" })}</AppText>
      <AppText>{t("stats.users", {}, { namespace: "dashboard" })}</AppText>
    </View>
  );
}

// Translation files structure:
// - translations/
//   - common.json       (loaded upfront)
//   - dashboard.json    (lazy loaded)
//   - auth.json         (lazy loaded)
//   - settings.json     (lazy loaded)

🔥 Performance Monitoring

Track and optimize translation performance:

import { performanceMonitor } from "react-native-apptext";

// In development, monitor performance
if (__DEV__) {
  // Check all translation lookups
  const allStats = performanceMonitor.getAllStats();
  console.table(allStats);

  // Get specific translation stats
  const welcomeStats = performanceMonitor.getStats("translate:welcome");
  if (welcomeStats) {
    console.log("Welcome translation performance:", {
      calls: welcomeStats.count,
      avgTime: `${welcomeStats.mean.toFixed(2)}ms`,
      minTime: `${welcomeStats.min.toFixed(2)}ms`,
      maxTime: `${welcomeStats.max.toFixed(2)}ms`,
      p95: `${welcomeStats.p95.toFixed(2)}ms`,
    });
  }
}

🛡️ Error Boundaries

Gracefully handle translation errors in production:

import { TranslationErrorBoundary } from "react-native-apptext";

function App() {
  return (
    <TranslationErrorBoundary
      fallback={
        <View style={{ padding: 20 }}>
          <AppText>Translation system unavailable</AppText>
        </View>
      }
      onError={(error, errorInfo) => {
        // Log to your error tracking service
        analytics.logError("TranslationError", { error, errorInfo });
      }}
    >
      <LocaleProvider translations={translations} defaultLanguage="en">
        <YourApp />
      </LocaleProvider>
    </TranslationErrorBoundary>
  );
}

🧹 Memory Management

Prevent memory leaks with built-in cleanup:

import { MemoryManager } from "react-native-apptext";

// Register timers (automatically cleaned up)
const timer = setTimeout(() => {
  /* ... */
}, 1000);
MemoryManager.registerTimer(timer);

// Register listeners
const cleanup = () => subscription.remove();
MemoryManager.registerListener(component, cleanup);

// Get memory stats
const stats = MemoryManager.getStats();
console.log(`Active timers: ${stats.activeTimers}`);
console.log(`Registered listeners: ${stats.registeredListeners}`);

// Clean up everything (useful in tests)
MemoryManager.clearAll();

🌐 ICU Message Format

AppText supports the full ICU Message Format for advanced formatting:

💰 Number Formatting

const translations = {
  en: {
    price: "{amount, number, currency}",
    percent: "{value, number, percent}",
    compact: "{count, number, compact}",
    decimal: "{value, number}",
  },
};

// Usage
t("price", { amount: 1234.56 }); // "$1,234.56" (US)
// "£1,234.56" (UK)
// "١٬٢٣٤٫٥٦ ر.س" (Saudi Arabia)

t("percent", { value: 0.856 }); // "85.6%"
t("compact", { count: 1500000 }); // "1.5M"
t("decimal", { value: 1234.567 }); // "1,234.57"

📅 Date Formatting

const translations = {
  en: {
    dateShort: "{date, date, short}",
    dateLong: "{date, date, long}",
    datetime: "{date, date, medium} at {date, time, short}",
  },
};

const today = new Date();
t("dateShort", { date: today }); // "11/27/2024"
t("dateLong", { date: today }); // "November 27, 2024"
t("datetime", { date: today }); // "Nov 27, 2024 at 2:30 PM"

🔢 Plural Forms

const translations = {
  en: {
    items: "{count, plural, one {# item} other {# items}}",
    messages: "{count, plural, =0 {No messages} one {# message} other {# messages}}",
  },
  ar: {
    items:
      "{count, plural, zero {لا عناصر} one {عنصر واحد} two {عنصران} few {# عناصر} many {# عنصراً} other {# عنصر}}",
  },
};

// Handles all plural forms automatically
t("items", { count: 0 }); // "0 items"
t("items", { count: 1 }); // "1 item"
t("items", { count: 5 }); // "5 items"

🎯 Select (Conditionals)

const translations = {
  en: {
    greeting:
      "{gender, select, male {Hello Mr. {name}} female {Hello Ms. {name}} other {Hello {name}}}",
  },
};

t("greeting", { gender: "male", name: "John" }); // "Hello Mr. John"
t("greeting", { gender: "female", name: "Jane" }); // "Hello Ms. Jane"
t("greeting", { gender: "other", name: "Alex" }); // "Hello Alex"

📊 Ordinal Numbers

import { OrdinalFormatter } from "react-native-apptext";

OrdinalFormatter.format(1, "en"); // "1st"
OrdinalFormatter.format(2, "en"); // "2nd"
OrdinalFormatter.format(3, "en"); // "3rd"
OrdinalFormatter.format(21, "en"); // "21st"
OrdinalFormatter.format(100, "en"); // "100th"

🎨 Advanced Features

🌈 Markdown Support

Format your translations with markdown syntax:

import { MarkdownTrans } from "react-native-apptext";

const translations = {
  en: {
    welcome:
      "Welcome **{{name}}**! Check out [our website](https://example.com) for `code` and __underlined__ text.",
  },
};

<MarkdownTrans
  i18nKey="welcome"
  values={{ name: "Alice" }}
  markdownStyles={{
    bold: { color: "red", fontWeight: "700" },
    link: { color: "blue", textDecorationLine: "underline" },
    code: { backgroundColor: "#f0f0f0", fontFamily: "monospace" },
  }}
  onLinkPress={(url) => Linking.openURL(url)}
/>;

🎭 Rich Text Components

Use custom components in translations:

import { Trans } from "react-native-apptext";

const translations = {
  en: {
    terms: "I agree to the <link>Terms of Service</link> and <bold>Privacy Policy</bold>",
  },
};

<Trans
  i18nKey="terms"
  components={{
    link: <AppText color="primary" onPress={() => openTerms()} />,
    bold: <AppText weight="700" />,
  }}
/>;

🎯 Type-Safe Translations

Get autocomplete for your translation keys:

// translations.ts
export const translations = {
  en: {
    auth: {
      login: "Log In",
      signup: "Sign Up",
    },
    home: {
      welcome: "Welcome back, {{name}}!",
    },
  },
} as const;

type TranslationKeys = DeepKeyOf<typeof translations.en>;
// TranslationKeys = "auth.login" | "auth.signup" | "home.welcome"

// Use with type safety
const { t } = useLang<typeof translations.en>();
t("auth.login"); // ✅ Type-safe
t("auth.logout"); // ❌ TypeScript error

📦 Virtual Scrolling

Handle large translation lists efficiently:

import { VirtualListHelper } from "react-native-apptext";

const virtualList = new VirtualListHelper(50); // 50px default height

function TranslationList({ items }) {
  const [scrollOffset, setScrollOffset] = useState(0);
  const viewportHeight = 600;

  const { startIndex, endIndex, offsetY } = virtualList.getVisibleRange(
    scrollOffset,
    viewportHeight,
    items
  );

  const visibleItems = items.slice(startIndex, endIndex + 1);

  return (
    <ScrollView
      onScroll={(e) => setScrollOffset(e.nativeEvent.contentOffset.y)}
      scrollEventThrottle={16}
    >
      <View style={{ height: virtualList.getTotalHeight(items) }}>
        <View style={{ transform: [{ translateY: offsetY }] }}>
          {visibleItems.map((item) => (
            <TranslationItem key={item.key} item={item} />
          ))}
        </View>
      </View>
    </ScrollView>
  );
}

🌐 Automatic Script Detection

AppText automatically detects and optimizes for 50+ writing systems:

function InternationalApp() {
  return (
    <View>
      {/* Latin - Auto-detected, LTR, standard line height */}
      <AppText>Hello World!</AppText>

      {/* Arabic - Auto-detected, RTL, increased line height */}
      <AppText>مرحباً بكم في تطبيقنا</AppText>

      {/* Devanagari - Auto-detected, complex shaping, increased line height */}
      <AppText>हमारे ऐप में आपका स्वागत है</AppText>

      {/* Japanese - Auto-detected, optimized for mixed scripts */}
      <AppText>こんにちは、私たちのアプリへようこそ。</AppText>

      {/* Mixed content - Smart per-character handling */}
      <AppText>Hello 你好 مرحبا 🌍</AppText>
    </View>
  );
}

Supported scripts include: Latin, Arabic, Cyrillic, Devanagari, Bengali, Tamil, Telugu, Thai, Chinese, Japanese, Korean, Hebrew, Greek, and 35+ more!

📚 Complete API Reference

Core Components

// Base component
<AppText variant="body1" color="primary" size={16} weight="600">
  Text content
</AppText>

// Compound components
<AppText.H1>Heading 1</AppText.H1>
<AppText.H2>Heading 2</AppText.H2>
<AppText.Body>Body text</AppText.Body>
<AppText.Caption>Caption</AppText.Caption>

// Material Design 3 variants
<AppText.DisplayLarge>Display Large</AppText.DisplayLarge>
<AppText.HeadlineMedium>Headline Medium</AppText.HeadlineMedium>
<AppText.BodyLarge>Body Large</AppText.BodyLarge>
<AppText.LabelSmall>Label Small</AppText.LabelSmall>

i18n API

// LocaleProvider props
<LocaleProvider
  translations={translations}
  defaultLanguage="en"
  fallbackLanguage="en"
  useICU={true}
  onMissingTranslation={(lang, key, namespace) => {}}
>
  {children}
</LocaleProvider>

// useLang hook
const {
  t,              // Translate function
  tn,             // Plural translate function
  changeLanguage, // Change language
  language,       // Current language
  direction,      // Current text direction
  loadNamespace,  // Load namespace dynamically
} = useLang();

// Translation functions
t(key, params?, options?)
tn(key, count, params?, options?)

// Options
{
  namespace?: string;
  context?: string;
  count?: number;
}

Performance API

// Cache management
import { translationCache } from "react-native-apptext";

translationCache.getStats(); // Get cache statistics
translationCache.clear(); // Clear cache

// Performance monitoring
import { performanceMonitor } from "react-native-apptext";

performanceMonitor.getStats(name); // Get specific stats
performanceMonitor.getAllStats(); // Get all stats
performanceMonitor.clear(name?); // Clear measurements

// Memory management
import { MemoryManager } from "react-native-apptext";

MemoryManager.registerTimer(timer);
MemoryManager.clearTimer(timer);
MemoryManager.getStats();
MemoryManager.clearAll();

Number Formatting

import { NumberFormatter } from "react-native-apptext";

NumberFormatter.format(1234.56, "en-US");
NumberFormatter.formatCurrency(99.99, "en-US", "USD");
NumberFormatter.formatPercent(0.856, "en-US");
NumberFormatter.formatCompact(1500000, "en-US"); // "1.5M"
NumberFormatter.formatSigned(-42, "en-US"); // "-42"
NumberFormatter.formatRange(10, 20, "en-US"); // "10 – 20"

🎯 Real-World Examples

E-commerce App with Lazy Loading

import {
  LazyLocaleProvider,
  useLazyLocale,
} from "react-native-apptext";

const App = () => (
  <LazyLocaleProvider
    loaders={{
      en: () => import("./locales/en.json"),
      es: () => import("./locales/es.json"),
      ar: () => import("./locales/ar.json"),
    }}
    defaultLanguage="en"
    preloadLanguages={["en"]}
  >
    <ShoppingApp />
  </LazyLocaleProvider>
);

function ProductCard({ product }) {
  const { t } = useLang();

  return (
    <View style={styles.card}>
      <AppText.H4>{product.name}</AppText.H4>
      <AppText color="secondary" truncate={3}>
        {product.description}
      </AppText>
      <AppText.H5 color="primary">
        {t("price", { amount: product.price })}
      </AppText>
      <AppText variant="caption">
        {tn("reviews", product.reviewCount)}
      </AppText>
    </View>
  );
}

Dashboard with Namespace Loading

function DashboardScreen() {
  // Lazy load dashboard translations
  useNamespace("dashboard", () => import("./translations/dashboard"));

  const { t } = useLang();
  const stats = translationCache.getStats();

  return (
    <View>
      <AppText.H2>{t("title", {}, { namespace: "dashboard" })}</AppText.H2>

      {__DEV__ && (
        <AppText variant="caption" color="textSecondary">
          Cache hit rate: {stats.hitRate.toFixed(1)}% | Size: {stats.size}/
          {stats.maxSize}
        </AppText>
      )}

      <AppText>{t("stats.users", { count: 1250 }, { namespace: "dashboard" })}</AppText>
    </View>
  );
}

Multilingual News App with Performance Monitoring

function NewsApp() {
  const { language, changeLanguage } = useLang();

  useEffect(() => {
    if (__DEV__) {
      const stats = performanceMonitor.getAllStats();
      console.table(stats);
    }
  }, [language]);

  return (
    <TranslationErrorBoundary
      fallback={<AppText>Error loading translations</AppText>}
      onError={(error) => analytics.logError("i18n", error)}
    >
      <View>
        <LanguageSwitcher />
        <NewsFeed />
      </View>
    </TranslationErrorBoundary>
  );
}

🛠️ Installation & Setup

Peer Dependencies

{
  "react": ">=17.0.0",
  "react-native": ">=0.70.0"
}

Platform-specific Setup

iOS & Android

No additional setup required! 🎉

Web

// For optimal web performance
module.exports = {
  resolve: {
    alias: {
      'react-native-apptext': 'react-native-apptext'
    }
  }
};

🤝 Contributing

We love contributions! Here's how to get started:

# Clone and setup
git clone https://github.com/Ganesh1110/react-native-apptext.git
cd react-native-apptext
npm install

# Run tests
npm test

# Test with coverage
npm run test:coverage

# Build
npm run build

🐛 Troubleshooting

Cache not working?

// Clear cache if needed
import { translationCache } from 'react-native-apptext';
translationCache.clear();

Memory leaks?

// Use MemoryManager for cleanup
import { MemoryManager } from 'react-native-apptext';

useEffect(() => {
  const timer = setTimeout(/*...*/);
  MemoryManager.registerTimer(timer);
  
  return () => MemoryManager.clearAll();
}, []);

Translations not lazy loading?

// Ensure dynamic imports are correct
loaders: {
  en: () => import('./locales/en.json'),  // ✅ Correct
  es: () => require('./locales/es.json'), // ❌ Wrong - use import()
}

📄 License

MIT © Ganesh1110


Built with ❤️ for the React Native community

Making beautiful, performant text accessible to every developer, in every language

📖 Documentation🐛 Report Bug💡 Request Feature👨‍💻 Contribute

⚡ Performance • 🌍 i18n • 📦 Lazy Loading • 🎨 Beautiful