LocalMode
Core

Import / Export

Migrate vector data between cloud vector databases and LocalMode. Parse Pinecone, ChromaDB, CSV, and JSONL formats. Export to interoperable formats.

Import / Export

Migrate vector data from cloud vector databases (Pinecone, ChromaDB) to LocalMode's in-browser VectorDB. Parse, preview, import, and export vector data in multiple formats — all offline.

See it in action

Try Semantic Search and Data Migrator for working demos of these APIs.

The import/export module is separate from the existing db.import() / db.export() methods, which handle LocalMode's own internal JSON format. The new functions handle external cloud database formats.

Supported Formats

FormatShapeUse Case
Pinecone JSON{ vectors: [{ id, values, metadata }] }Pinecone dashboard exports
ChromaDB JSON{ ids, embeddings, metadatas, documents }ChromaDB collection exports
CSVHeader row with vector column (JSON array)Spreadsheet-compatible transfers
JSONLOne { id, vector, ... } per lineStreaming-friendly, large datasets

Quick Start

import { importFrom, createVectorDB } from '@localmode/core';
import { transformers } from '@localmode/transformers';

// 1. Create a VectorDB
const db = await createVectorDB({ name: 'migrated', dimensions: 384 });

// 2. Import from Pinecone export
const stats = await importFrom({
  db,
  content: pineconeExportJson,
  format: 'pinecone',
  model: transformers.embedding('Xenova/bge-small-en-v1.5'), // re-embed text-only records
  onProgress: (p) => console.log(`${p.phase}: ${p.completed}/${p.total}`),
});

console.log(`Imported ${stats.imported}, skipped ${stats.skipped}`);

Preview Before Import

Use parseExternalFormat() to inspect data before committing to an import:

import { parseExternalFormat } from '@localmode/core';

const result = parseExternalFormat(fileContent);
// result.format       → 'pinecone' (auto-detected)
// result.totalRecords → 1000
// result.recordsWithVectors → 800
// result.recordsWithTextOnly → 200
// result.dimensions   → 384

// Show preview to the user before importing

importFrom()

The main orchestrator function. Parses external data, validates dimensions, optionally re-embeds text-only records, and batches into db.addMany().

Options

OptionTypeDefaultDescription
dbVectorDBrequiredTarget VectorDB instance
contentstringrequiredRaw content string to parse
formatExternalFormatauto-detectSource format override
modelEmbeddingModelEmbedding model for re-embedding text-only records
batchSizenumber100Records per addMany() call
skipDimensionCheckbooleanfalseSkip dimension validation
onProgress(progress) => voidProgress callback
abortSignalAbortSignalCancellation signal

ImportStats Result

FieldTypeDescription
importednumberRecords successfully imported
skippednumberRecords skipped (no vector and no model)
reEmbeddednumberText-only records re-embedded
totalParsednumberTotal records parsed from source
formatExternalFormatDetected or specified format
dimensionsnumberVector dimensions
durationMsnumberTotal operation time

Export

exportToCSV()

import { exportToCSV } from '@localmode/core';

const csv = exportToCSV(records, {
  delimiter: ',',
  includeVectors: true,
  includeText: true,
});

exportToJSONL()

import { exportToJSONL } from '@localmode/core';

const jsonl = exportToJSONL(records, {
  includeVectors: true,
  vectorFieldName: 'embedding', // custom field name
});

Format Conversion

Convert between formats without a VectorDB:

import { convertFormat } from '@localmode/core';

// Pinecone JSON → CSV
const csv = convertFormat(pineconeJson, { to: 'csv' });

// ChromaDB JSON → JSONL
const jsonl = convertFormat(chromaJson, { from: 'chroma', to: 'jsonl' });

// CSV → Pinecone format
const pinecone = convertFormat(csvData, { to: 'pinecone' });

Re-embedding Workflow

When importing from a cloud database, some records may have text but no vectors (because the original embeddings were generated server-side). Pass an EmbeddingModel to importFrom() to re-embed these records locally:

import { importFrom } from '@localmode/core';
import { transformers } from '@localmode/transformers';

const stats = await importFrom({
  db,
  content: chromaExport,
  model: transformers.embedding('Xenova/bge-small-en-v1.5'),
});

// stats.reEmbedded → number of text-only records that were re-embedded

Re-embedded vectors will differ from the original cloud-generated vectors because different models produce different embeddings. This is expected — the local model becomes the new source of truth.

Error Handling

import { importFrom, ParseError, DimensionMismatchOnImportError } from '@localmode/core';

try {
  await importFrom({ db, content });
} catch (error) {
  if (error instanceof ParseError) {
    console.log(error.hint);           // actionable fix suggestion
    console.log(error.context?.format); // format being parsed
    console.log(error.context?.line);   // line number (JSONL/CSV)
  }
  if (error instanceof DimensionMismatchOnImportError) {
    console.log(error.expected); // target DB dimensions
    console.log(error.actual);   // imported vector dimensions
    console.log(error.recordId); // first mismatched record
  }
}

React Hook

import { useImportExport } from '@localmode/react';

function ImportPanel() {
  const {
    importData, parsePreview, exportCSV, exportJSONL,
    isImporting, isParsing, progress, stats, parseResult, error,
    cancel, reset,
  } = useImportExport({
    db: myVectorDB,
    model: embeddingModel,
  });

  return (
    <div>
      <button onClick={() => parsePreview({ content: fileText })}>Preview</button>
      <button onClick={() => importData({ content: fileText })}>Import</button>
      <button onClick={exportCSV}>Export CSV</button>
      {isImporting && <p>Phase: {progress?.phase}</p>}
      {stats && <p>Imported: {stats.imported}</p>}
    </div>
  );
}

Recipe: Moving from Pinecone to LocalMode

Export from Pinecone

In the Pinecone console, export your index as JSON. The file will have the format:

{ "vectors": [{ "id": "...", "values": [...], "metadata": {...} }] }

Preview the export

const result = parseExternalFormat(pineconeJson);
console.log(`${result.totalRecords} records, ${result.dimensions}d vectors`);

Create a matching VectorDB

const db = await createVectorDB({
  name: 'my-app',
  dimensions: result.dimensions!,
});

Import with progress

const stats = await importFrom({
  db,
  content: pineconeJson,
  format: 'pinecone',
  onProgress: (p) => updateProgressBar(p.overallCompleted / p.overallTotal),
});

Delete your Pinecone account

Your data now lives entirely in the browser. No servers, no API keys, no monthly bill.

Showcase Apps

AppDescriptionLinks
Semantic SearchExport and import vector indexesDemo · Source
Data MigratorMigrate vectors between Pinecone, ChromaDB, CSV, JSONLDemo · Source

On this page