The analytical database for React Native. Run OLAP queries, full-text search, and vector similarity search on iOS and Android with native C++ performance via Nitro Modules.
- Columnar OLAP engine (not row-based OLTP like SQLite)
- Full-text search with BM25 ranking and 27 language stemmers
- Vector similarity search with HNSW indexing for on-device RAG/AI
- Remote data queries over HTTPS (Parquet, CSV, JSON, Hugging Face datasets)
- Streaming results for large datasets without OOM
- Bulk insert via Appender API
- Columnar typed array access (Float64Array, BigInt64Array)
- 30+ DuckDB types including HUGEINT, DECIMAL, ARRAY, MAP, STRUCT
- Query cancellation, progress callbacks, JSON profiling
- Expo config plugin for managed workflow
Download DuckDB Explorer — a free companion app to try everything before writing a single line of code. Interactive SQL runner, Hugging Face dataset browser, full-text search, vector similarity search, and more.
npm install react-native-duckdb react-native-nitro-modulesFor iOS, run pod install after installation.
import { HybridDuckDB } from 'react-native-duckdb'
const db = HybridDuckDB.open(':memory:', {})
db.executeSync('CREATE TABLE users (id INTEGER, name VARCHAR, score DOUBLE)')
db.executeSync("INSERT INTO users VALUES (1, 'Alice', 95.5), (2, 'Bob', 87.3)")
const result = db.executeSync('SELECT * FROM users ORDER BY score DESC')
const rows = result.toRows()
// [{ id: 1, name: 'Alice', score: 95.5 }, { id: 2, name: 'Bob', score: 87.3 }]
db.close()const db = HybridDuckDB.open(':memory:', {
threads: '2',
memory_limit: '256MB',
default_order: 'DESC',
})const result = await db.execute('SELECT * FROM large_table WHERE category = ?', ['electronics'])
const rows = result.toRows()const stmt = db.prepare('SELECT * FROM users WHERE age > ?')
stmt.bind([21])
const result = stmt.executeSync()
stmt.finalize()const result = db.executeSyncNamed(
'SELECT * FROM users WHERE name = $name AND age > $age',
{ $name: 'Alice', $age: 21 }
)const promise = db.execute('SELECT * FROM generate_series(1, 100000000)')
setTimeout(() => db.cancel(), 50)db.executeSync("PRAGMA enable_profiling='json'")
db.executeSync('SELECT * FROM large_table ORDER BY score DESC')
const profile = db.getProfilingInfo() // JSON string with timing breakdowndb.setProgressCallback((percentage) => {
console.log(`Query progress: ${percentage}%`)
})
const result = await db.execute('SELECT * FROM big_join')
db.removeProgressCallback()const { rowsAffected } = db.executeBatchSync([
{ query: 'INSERT INTO logs VALUES (?, ?)', params: [1, 'start'] },
{ query: 'INSERT INTO logs VALUES (?, ?)', params: [2, 'end'] },
])import { executeTransaction } from 'react-native-duckdb'
const count = await executeTransaction(db, async (tx) => {
tx.executeSync('INSERT INTO orders VALUES (1, 99.99)')
tx.executeSync('UPDATE inventory SET stock = stock - 1 WHERE id = 1')
return tx.executeSync('SELECT count(*) as n FROM orders').toRows()[0].n
})
// auto-commits on success, auto-rolls-back on errordb.attach('/path/to/other.duckdb', 'analytics', { readOnly: true })
const result = db.executeSync('SELECT * FROM analytics.events LIMIT 10')
db.detach('analytics')import { DOCUMENTS_PATH, LIBRARY_PATH } from 'react-native-duckdb'
const db = HybridDuckDB.open(`${LIBRARY_PATH}/analytics.duckdb`, {})
// iOS: NSLibraryDirectory (no iCloud backup)
// Android: getFilesDir()See docs/database-location.md for all available paths, backup behavior, and platform details.
HybridDuckDB.deleteDatabase(`${DOCUMENTS_PATH}/old.duckdb`)Process millions of rows chunk-by-chunk without loading everything into memory.
import { streamChunks } from 'react-native-duckdb'
const stream = await db.stream('SELECT * FROM large_table')
for await (const chunk of streamChunks(stream)) {
processChunk(chunk.toRows())
}See docs/streaming.md for the Appender API, progress callbacks, and ETL patterns.
BM25-ranked search with 27 language stemmers. Requires the fts extension.
db.executeSync("LOAD 'fts'")
db.executeSync("PRAGMA create_fts_index('docs', 'id', 'title', 'body', stemmer='english')")
const results = db.executeSync(`
SELECT *, fts_main_docs.match_bm25(id, 'search query') AS score
FROM docs WHERE score IS NOT NULL ORDER BY score DESC
`)See docs/fts.md for multi-language stemming, field-specific search, and limitations.
HNSW-indexed nearest-neighbor queries for on-device semantic search and RAG. Requires the vss extension.
db.executeSync("LOAD 'vss'")
db.executeSync('CREATE TABLE docs (id INTEGER, vec FLOAT[384])')
db.executeSync("CREATE INDEX idx ON docs USING HNSW (vec) WITH (metric = 'cosine')")
const similar = db.executeSync(`
SELECT id, array_cosine_distance(vec, $query::FLOAT[384]) AS distance
FROM docs ORDER BY distance LIMIT 10
`)See docs/vss.md for distance metrics, use cases, and HNSW tuning.
Extensions are statically linked at build time. Configure in package.json (bare) or app.json (Expo):
{
"react-native-duckdb": {
"build": {
"extensions": ["core_functions", "parquet", "json"]
}
}
}| Extension | Description |
|---|---|
core_functions |
Essential SQL functions (sum, avg, uuid, etc.) — recommended |
parquet |
Apache Parquet file format |
json |
JSON file format |
httpfs |
Remote file access over HTTPS |
fts |
BM25 full-text search |
vss |
HNSW vector similarity search |
sqlite_scanner |
Read SQLite databases |
icu |
Unicode collation and locale |
delta |
Delta Lake table format |
autocomplete |
SQL autocomplete |
tpch / tpcds |
Benchmark data generators |
See docs/extensions.md for configuration details and per-extension guides.
Expo: Add to app.json plugins:
["react-native-duckdb", { "extensions": ["core_functions", "parquet"] }]See docs/expo.md for the full Expo guide.
react-native-duckdb is an OLAP (Online Analytical Processing) database — optimized for analytical queries over large datasets. The libraries below are OLTP (Online Transaction Processing) databases — optimized for many small read/write transactions typical in app state management.
These are complementary paradigms. Use SQLite-based libraries for your app's transactional data (users, settings, state). Use DuckDB for analytics, search, and data processing.
| Feature | react-native-duckdb | nitro-sqlite | op-sqlite | WatermelonDB |
|---|---|---|---|---|
| Engine | DuckDB (columnar OLAP) | SQLite (row OLTP) | SQLite (row OLTP) | SQLite (row OLTP) |
| Native bridge | Nitro Modules (JSI) | Nitro Modules (JSI) | JSI | JSI |
| Parquet/CSV/JSON file queries | Yes | No | No | No |
| Remote data (HTTPS) | Yes (httpfs) | No | No | No |
| Full-text search | BM25 with 27 stemmers | FTS5 (compile flag) | FTS5 (compile flag) | No |
| Vector search (HNSW) | Yes | No | sqlite-vec plugin | No |
| Columnar typed arrays | Yes (Float64Array, etc.) | No | No | No |
| Streaming results | Yes (chunk-by-chunk) | No | No | No |
| Bulk insert (Appender) | Yes | No | No | Batch insert |
| Query progress callbacks | Yes | No | No | No |
| Reactive queries | No | No | Yes | Yes |
| ORM / Model layer | No (raw SQL) | TypeORM compatible | TypeORM compatible | Built-in |
| Sync protocol | No | No | No | Built-in |
| Encryption | No | No | SQLCipher | No |
| Expo plugin | Yes | No | No | No |
| Guide | Description |
|---|---|
| API Reference | Complete API surface — every method, property, and type |
| Extensions | Configuration, available extensions, per-extension usage |
| Streaming & Appender | Chunk-by-chunk processing, bulk insert, ETL patterns |
| Type System | DuckDB → JavaScript type mapping for all 30+ types |
| Transactions | ACID transactions, batch execution, multi-database |
| Database Location | Storage paths, iCloud/Auto Backup, platform defaults |
| Full-Text Search | BM25 indexing, stemmers, field search, limitations |
| Vector Search | HNSW indexes, distance metrics, RAG patterns |
| Remote Data | httpfs, Hugging Face datasets, S3, TLS config |
| Expo Setup | Config plugin, extension flow, migration guide |
| Bare Workflow | iOS/Android setup without Expo |
I started working on this library in late 2023. The initial prototype took a full week and could barely run basic SQL statements — no extensions, no streaming, no type system. It proved the concept but was nowhere near production quality.
Fast forward to Feb 2026: with the help of LLMs, I rebuilt the entire library from scratch — production-grade, fully documented, with 11 statically-linked extensions, 30+ type mappings, streaming, vector search, full-text search, and an example app that doubles as a DuckDB dev studio. The entire rebuild took under one week.
The stack: Claude Opus 4 (claude-4-6) running through opencode, orchestrated by the Get Shit Done framework for structured multi-phase execution. The total inference cost was roughly ~$1,500.
But here's the thing — AI didn't make the decisions. I have extensive experience building React Native libraries, especially data libraries, and every architectural choice, every API design trade-off, every verification pass, every device test was done by a real human. The AI handled the volume; I handled the vision.
We're at the beginning of something massive. A single developer with the right tools can now ship what used to require a dedicated team and months of runway. The barrier to building ambitious software is collapsing. This library is proof.
MIT
Inspired by react-native-nitro-sqlite and op-sqlite. Built with DuckDB and Nitro Modules. See RELEASE.md for release security details.







