# @gmod/tabix

> Read Tabix-indexed files, supports both .tbi and .csi indexes

Latest version **3.8.3** (published 2026-09-22) · MIT license · 0 weekly downloads

## Install

```sh
npm install @gmod/tabix
pnpm add @gmod/tabix
yarn add @gmod/tabix
bun add @gmod/tabix
```

## Health

**Score 75/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; has provenance; recently updated; high maintenance score; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 3.8.3 |
| Published | 2026-09-22 |
| First published | 2018-09-09 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 4 |
| Unpacked size | 503.6 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 14 |
| Author | Robert Buels |
| Maintainers | rbuels, cmdcolin, garrettjstevens |
| Keywords | bionode, biojs, genomics |

## Links

- npm: https://www.npmjs.com/package/@gmod/tabix
- Repository: https://github.com/GMOD/tabix-js
- Homepage: https://github.com/GMOD/tabix-js#readme
- Issues: https://github.com/GMOD/tabix-js/issues
- npm.io page: https://npm.io/package/@gmod/tabix

## Dependencies (4)

- [@jbrowse/quick-lru](https://npm.io/package/@jbrowse/quick-lru.md) ^7.3.5
- [generic-filehandle2](https://npm.io/package/generic-filehandle2.md) ^2.2.1
- [@gmod/bgzf-filehandle](https://npm.io/package/@gmod/bgzf-filehandle.md) ^6.6.0
- [@gmod/shared-read-cache](https://npm.io/package/@gmod/shared-read-cache.md) ^1.7.2

## Recent versions

- 3.8.3 (latest) — 2026-09-22
- 3.8.2 — 2026-08-21
- 3.8.1 — 2026-08-12
- 3.8.0 — 2026-08-11
- 3.7.3 — 2026-08-10
- 3.7.2 — 2026-08-10
- 3.7.1 — 2026-08-10
- 3.6.1 — 2026-08-09
- 3.6.0 — 2026-08-06
- 3.5.7 — 2026-08-05
- 3.5.5 — 2026-08-04
- 3.5.4 — 2026-08-04
- 3.5.3 — 2026-08-04
- 3.5.2 — 2026-07-31
- 3.5.1 — 2026-07-31
- … 72 more at https://npm.io/package/@gmod/tabix/versions

## README

# @gmod/tabix

[![NPM version](https://img.shields.io/npm/v/@gmod/tabix.svg?style=flat-square)](https://npmjs.org/package/@gmod/tabix)
![Build Status](https://img.shields.io/github/actions/workflow/status/GMOD/tabix-js/publish.yml?branch=main)

Read Tabix-indexed files using either .tbi or .csi indexes.

## Install

```bash
npm install @gmod/tabix
```

## Usage

```typescript
import { TabixIndexedFile } from '@gmod/tabix'

// Local file — TBI index assumed at path + '.tbi'
const file = new TabixIndexedFile({ path: 'file.vcf.gz' })

// CSI index
const csi = new TabixIndexedFile({
  path: 'file.vcf.gz',
  csiPath: 'file.vcf.gz.csi',
})

// Remote files
const remote = new TabixIndexedFile({
  url: 'https://example.com/file.vcf.gz',
  tbiUrl: 'https://example.com/file.vcf.gz.tbi',
})

// Or with a filehandle from generic-filehandle2
import { RemoteFile } from 'generic-filehandle2'

const custom = new TabixIndexedFile({
  filehandle: new RemoteFile('https://example.com/file.vcf.gz'),
  tbiFilehandle: new RemoteFile('https://example.com/file.vcf.gz.tbi'),
})
```

Over HTTP it is worth swapping in
[`@gmod/range-cache-filehandle`](https://github.com/GMOD/range-cache-filehandle).
A query fetches the index once, then reads the BGZF blocks it points at as byte
ranges spread through the file. Overlapping queries re-read the same blocks:
panning twenty half-overlapping windows across the 3.4 MB test BED file reads 11
MB. The cache serves those reads out of 256 KiB chunks, so neighboring blocks
share a request and each byte is fetched once.

```typescript
import { RemoteFileWithRangeCache } from '@gmod/range-cache-filehandle'

const cached = new TabixIndexedFile({
  filehandle: new RemoteFileWithRangeCache('https://example.com/file.vcf.gz'),
  tbiFilehandle: new RemoteFileWithRangeCache(
    'https://example.com/file.vcf.gz.tbi',
  ),
})
```

### getLines

Fetches lines overlapping a region. `start`/`end` are 0-based half-open
coordinates (unlike the tabix CLI which uses 1-based closed).

```typescript
const lines: string[] = []
await file.getLines('chr1', 200, 300, line => lines.push(line))
```

The callback also receives the virtual file offset and parsed coordinates for
the line:

```typescript
await file.getLines('chr1', 200, 300, (line, fileOffset, start, end) => {
  lines.push(line)
})
```

Pass an options object instead of a bare callback to abort the query or track
download progress:

```typescript
const aborter = new AbortController()
await file.getLines('chr1', 200, 300, {
  lineCallback: (line, fileOffset, start, end) => lines.push(line),
  signal: aborter.signal,
  onProgress: (bytesDownloaded, totalBytes) => {
    console.log(`${bytesDownloaded}/${totalBytes}`)
  },
})
```

`onProgress` ticks once per chunk — the run of BGZF blocks the index resolves a
query to — including instant ticks for chunks already cached, and the index
supplies `totalBytes` up front, which is enough for a determinate progress bar.

Notes:

- The scan skips meta/comment lines
- Line strings have no trailing whitespace
- Pass `undefined` for `end` to read to the end of the contig
- A `refName` that is not in the index yields no lines and no error, so a
  `chr1`/`1` naming mismatch looks like an empty region. Check against
  [`getReferenceSequenceNames`](docs/api.md#getreferencesequencenamesopts-promisestring)
  if a query comes back unexpectedly empty
- `start > end` throws a `TypeError`; `start === end` returns without reading

### Without NPM (CDN)

```html
<script src="https://unpkg.com/@gmod/tabix/dist/tabix-bundle.js"></script>
```

See [example/index.html](example/index.html) for a working demo. It fetches the
VCF over HTTP, so serve the directory (e.g. `npx serve example`) rather than
opening the file directly.

## How a query flows

`getLines` turns a region into BGZF chunks through the index and decompresses
each one in wasm — index reads included, since `.tbi` and `.csi` are bgzipped
too. The rest is ordinary JS: it matches lines as bytes and decodes only the
ones you asked for. [docs/dataflow.md](docs/dataflow.md) has the diagram and
walks it through.

The file then holds on to those decompressed chunks, so overlapping and adjacent
queries reuse them instead of inflating again — up to 1GB per file, dropped
after three idle minutes. A consumer holding one file per track should bound
them together with a shared `chunkCacheBudget` rather than shrinking each file's
own ceiling: [docs/caching.md](docs/caching.md).

## Decompressing on a worker pool

BGZF blocks inflate independently, so that decompression can spread across
threads.

```typescript
import { getSharedWorkerPool } from '@gmod/bgzf-filehandle'

const file = new TabixIndexedFile({
  url: 'https://example.com/yourfile.vcf.gz',
  // the pending promise is fine — it is awaited at the point of use
  bgzfWorkerPool: getSharedWorkerPool(),
})
```

Safe to pass unconditionally: `getSharedWorkerPool()` returns `undefined` under
node, or anywhere the host forbids Workers, which keeps the in-process path. No
cross-origin isolation needed. tabix-js never creates a pool on its own — the
consumer controls the thread budget.

**Worth about 1.4x here, against the 1.95x a BAM reader reports.** Measured in
jbrowse-components on `test/data/1kg.chr1.subset.vcf.gz` — 213MB of 1000
Genomes, headless Chrome, real HTTP, four workers, arms interleaved, both
returning the same record count: **1.34-1.46x** across five window sizes and a
twelve-step pan.

The decompression itself moves **1.83x**. A **28% floor of per-line byte
scanning and string decoding** holds the end-to-end figure below that; no worker
count reaches it, and the floor is at its worst on multi-sample VCF, whose
records carry a genotype field per sample and run to ~60KB a line. A format with
narrower lines sits closer to BAM. Getting more than ~1.5x on a multi-sample VCF
means attacking the scan, not the decompression.

Worker counts, lifecycle and benchmarks:
[bgzf-filehandle's worker pool docs](https://github.com/GMOD/bgzf-filehandle/blob/main/docs/worker-pool.md);
the end-to-end numbers above, and how to confirm a pool is really engaging in
production rather than quietly falling back, are in jbrowse-components'
[BGZF_WORKER_POOL.md](https://github.com/GMOD/jbrowse-components/blob/main/agent-docs/reference/BGZF_WORKER_POOL.md).

## Docs

- [docs/api.md](docs/api.md) — every constructor arg and method
- [docs/dataflow.md](docs/dataflow.md) — a query end to end, diagrammed
- [docs/optimizations.md](docs/optimizations.md) — why each step of that path
  looks the way it does, and what measured it
- [docs/caching.md](docs/caching.md) — sizing the decompressed-chunk cache, and
  bounding many files together
- [agent-docs/adr/](agent-docs/adr/) — the measurements behind those decisions
- [agent-docs/TODO.md](agent-docs/TODO.md) — what is worth doing next, and what
  has to be measured before it
- [CONTRIBUTING.md](CONTRIBUTING.md) — development and release steps

## Academic Use

Written with [NHGRI](http://genome.gov) funding as part of
[JBrowse](http://jbrowse.org). If you use this in a publication, please cite the
most recent JBrowse paper at [jbrowse.org](http://jbrowse.org).

## License

MIT © [Robert Buels](https://github.com/rbuels)

---
_Source: https://npm.io/package/@gmod/tabix · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
