react-native-ulid-jsi
v0.2.2
Published
High-performance ULID generator for React Native using JSI and C++. Supports both old and new architecture on iOS and Android.
Maintainers
Readme
react-native-ulid-jsi
⚡️ Ultra-fast ULID (Universally Unique Lexicographically Sortable Identifier) generator for React Native
Built with JSI (JavaScript Interface) and C++ for maximum performance. Features monotonic generation with thread-local state for guaranteed ID ordering. Supports both New Architecture (Fabric + TurboModules) and Old Architecture on iOS and Android.
🚀 500x faster than JavaScript | 🔄 Monotonic | 📦 Zero dependencies
🚀 Performance
500x faster than pure JavaScript implementations!
Comparison performed against the official ulid/javascript package.
| Implementation | Time (1000 iterations) | Performance | |---------------|----------------------|-------------| | react-native-ulid-jsi (JSI/C++) | 0.17ms | ⚡️ 500x faster | | Pure JavaScript (ulid package) | 83.62ms | 🐌 Baseline |
react-native-ulid-jsi ▓ 0.17ms
JavaScript ULID ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ 83.62ms
└────────────────────────────────────────────────┘
500x performance gainBenchmark performed on iPhone 16 Pro with production build
Why so fast?
- Zero Bridge Overhead: Direct JSI binding to C++ (no serialization)
- Native Random Generation: Platform-specific secure random APIs
- Optimized Encoding: Hand-tuned Base32 encoding in C++
- Thread-local State: No synchronization overhead for monotonic generation
- Minimal Allocations: Stack-allocated buffers, no heap fragmentation
Run your own benchmark
import { ulid } from 'react-native-ulid-jsi';
const iterations = 1000;
const start = performance.now();
for (let i = 0; i < iterations; i++) {
ulid();
}
const end = performance.now();
console.log(`Generated ${iterations} ULIDs in ${(end - start).toFixed(2)}ms`);
// Output: Generated 1000 ULIDs in 0.17ms✨ Features
- ⚡️ Lightning Fast - JSI + C++ implementation for native performance
- 🏗️ New Architecture Ready - Full support for React Native's new architecture (Fabric + TurboModules)
- 🔄 Backward Compatible - Works seamlessly with old architecture
- 📱 Cross Platform - iOS and Android support
- 🎯 Type Safe - Full TypeScript support
- 🪶 Lightweight - Zero dependencies, pure C++ implementation
- 📈 Monotonic Generation - Guarantees increasing IDs even within the same millisecond
- ⏱️ Timestamp Encoded - Contains creation timestamp (first 48 bits)
- 🎲 Cryptographically Secure - Platform-native secure random generation
- iOS:
SecRandomCopyBytes(Security Framework) - Android: getrandom when available, falls back to /dev/urandom
- iOS:
- 🔤 Crockford's Base32 - Excludes ambiguous characters (I, L, O, U)
📦 Installation
npm install react-native-ulid-jsior with yarn:
yarn add react-native-ulid-jsiiOS
cd ios && pod installAndroid
No additional steps required. Gradle will handle everything automatically.
📖 Usage
Basic Usage
import { ulid } from 'react-native-ulid-jsi';
// Generate a new ULID
const id = ulid();
console.log(id); // 01ARZ3NDEKTSV4RRFFQ69G5FAV
// Generate multiple ULIDs - they will be monotonically increasing
const id1 = ulid(); // 01HGW4Z6C8ABCDEFGHIJKLMNOP
const id2 = ulid(); // 01HGW4Z6C8ABCDEFGHIJKLMNPQ (guaranteed > id1)
const id3 = ulid(); // 01HGW4Z6C8ABCDEFGHIJKLMNPR (guaranteed > id2)With Seed Time
import { ulid } from 'react-native-ulid-jsi';
// Generate ULID with custom timestamp (milliseconds since epoch)
const timestamp = Date.now();
const id = ulid(timestamp);
console.log(id); // 01ARZ3NDEKTSV4RRFFQ69G5FAVValidate ULID
import { isValid } from 'react-native-ulid-jsi';
const id = '01ARZ3NDEKTSV4RRFFQ69G5FAV';
const valid = isValid(id);
console.log(valid); // trueDecode Timestamp
import { decodeTime } from 'react-native-ulid-jsi';
const id = '01ARZ3NDEKTSV4RRFFQ69G5FAV';
const timestamp = decodeTime(id);
console.log(timestamp); // 1469918176385
console.log(new Date(timestamp)); // 2016-07-31T04:42:56.385Z🎯 API Reference
ulid(seedTime?: number): string
Generates a new ULID.
Parameters:
seedTime(optional): Custom timestamp in milliseconds since Unix epoch. If not provided, uses current time.
Returns: A 26-character ULID string.
Example:
const id1 = ulid(); // Uses current time
const id2 = ulid(1469918176385); // Uses custom timeisValid(id: string): boolean
Validates whether a string is a valid ULID.
Parameters:
id: String to validate
Returns: true if valid ULID, false otherwise.
Example:
isValid('01ARZ3NDEKTSV4RRFFQ69G5FAV'); // true
isValid('invalid-id'); // falsedecodeTime(id: string): number
Extracts the timestamp from a ULID.
Parameters:
id: Valid ULID string
Returns: Timestamp in milliseconds since Unix epoch.
Example:
const timestamp = decodeTime('01ARZ3NDEKTSV4RRFFQ69G5FAV');
console.log(new Date(timestamp)); // Original creation time🏗️ Architecture Support
This library is built to support both React Native architectures:
New Architecture (Fabric + TurboModules)
✅ Fully supported with JSI direct bindings ✅ Zero overhead JavaScript ↔️ Native communication ✅ Synchronous API access
Old Architecture (Bridge)
✅ Fully supported with JSI module installation ✅ Same performance benefits ✅ No breaking changes required
The library automatically detects and works with the architecture your app is using.
🔍 What is ULID?
ULID (Universally Unique Lexicographically Sortable Identifier) is a better alternative to UUID for many use cases:
- Sortable: Unlike UUIDs, ULIDs are lexicographically sortable
- Timestamp: Contains a timestamp component (first 48 bits)
- Compact: 26 characters vs UUID's 36 characters
- URL-safe: Uses Crockford's base32 encoding
- Case-insensitive: Easy to use in various contexts
- No special characters: Just alphanumeric characters
ULID Format
01AN4Z07BY 79KA1307SR9X4MV3
|----------| |----------------|
Timestamp Randomness
(48 bits) (80 bits)🔄 Monotonic Generation
This library implements monotonic ULID generation, ensuring that IDs are always increasing even when generated within the same millisecond.
How it works:
- Same timestamp: If multiple ULIDs are generated in the same millisecond, the random component is incremented instead of generating new random bytes
- Overflow protection: If the random component overflows, a new random value is generated
- Time progression: When time advances, a fresh random value is used
Why monotonic?
// Without monotonic (pure random):
const id1 = ulid(); // 01HGW4Z6C8ABCDEFGHIJKLMNOP
const id2 = ulid(); // 01HGW4Z6C8ZYXWVUTSRQPONMLK ❌ Not guaranteed to be > id1
// With monotonic (this library):
const id1 = ulid(); // 01HGW4Z6C8ABCDEFGHIJKLMNOP
const id2 = ulid(); // 01HGW4Z6C8ABCDEFGHIJKLMNPQ ✅ Always > id1Benefits:
- ✅ Database indexes work optimally (B-tree friendly)
- ✅ No collisions within same millisecond
- ✅ Guaranteed sortability
- ✅ Better for distributed systems
💡 Use Cases
- Database IDs: Sortable by creation time without separate timestamp field
- Distributed Systems: Generate unique IDs without coordination
- API Resources: URL-safe identifiers for REST APIs
- Event Sourcing: Time-ordered events with unique identifiers
- Logging: Sortable log entry identifiers
- File Names: Unique, sortable file names
🔧 Technical Details
- Implementation: Pure C++ with JSI (JavaScript Interface) bindings
- Monotonic Generation: Thread-local state ensures IDs increment even within same millisecond
- Thread Safety: Thread-local storage per thread, no locks or mutexes needed
- Random Generation (with automatic fallback):
- iOS:
SecRandomCopyBytes(Security Framework) - Android: getrandom when available, falls back to /dev/urandom
- Last resort:
std::random_device(other platforms)
- iOS:
- Encoding: Crockford's Base32 (0-9, A-Z excluding I, L, O, U)
- Memory: Minimal allocation, optimized for mobile devices
- Dependencies: Zero - pure C++ standard library
- Bundle Size: Native module only, zero JavaScript bundle impact
- Platforms: iOS 12+, Android API 21+
📊 Comparison with JavaScript ULID
| Feature | react-native-ulid-jsi | JavaScript ULID | |---------|------------------|-----------------| | Performance | ⚡️ 0.17ms (1000 ops) | 🐌 83.62ms (1000 ops) | | Speed Improvement | 500x faster | Baseline | | Implementation | C++ / JSI | JavaScript | | Monotonic | ✅ Thread-local | ⚠️ Varies by package | | Architecture Support | New + Old | N/A | | Secure Random | ✅ Platform native | ⚠️ JS Math.random or crypto | | Bundle Impact | Native only (0 KB JS) | +5-10 KB bundle | | Dependencies | Zero | Varies |
🤝 Contributing
Contributions are welcome! Please read our contributing guidelines and code of conduct.
Development Workflow
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'feat: add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
See CONTRIBUTING.md for detailed development setup.
📄 License
MIT © Alex Shumihin
🔗 Links
⭐️ Show Your Support
If this project helped you, please give it a ⭐️!
Made with ❤️ using create-react-native-library
