Express CSV Logo

Recovered sessions

Pass the sessionRecovery option to useExpressCSV() when you want the importer to restore unfinished imports on the next visit.

By default, ExpressCSV does not persist importer state. If you omit sessionRecovery, unfinished imports are not stored and no Recovered sessions will be available later.

Recovery Fingerprint

Recovered sessions are stored against a combined fingerprint of two values:

  • importIdentifier, which should represent the same import flow over time
  • the schema shape

To keep recovery working across visits, treat both values as stable for a given flow. If either changes in an incompatible way, ExpressCSV may treat it as a different import flow and start a new session instead of recovering the old one.

Local Recovery

Use the built-in browser persistence backend:

import { useExpressCSV } from "@expresscsv/react";

const { open } = useExpressCSV({
  schema,
  getSessionToken: async () => fetchSessionToken(),
  importIdentifier: "product-import",
  sessionRecovery: { type: "local" },
});

Custom Recovery

Use type: "custom" when you want to persist recovery data in your own backend.

import {
  useExpressCSV,
  type RecoveredSession,
  type RecoveredSessionData,
  type SessionRecoveryKey,
} from "@expresscsv/react";

const { open } = useExpressCSV({
  schema,
  getSessionToken: async () => fetchSessionToken(),
  importIdentifier: "product-import",
  sessionRecovery: {
    type: "custom",
    get: async (key: SessionRecoveryKey): Promise<RecoveredSession | null> => {
      const response = await fetch(`/api/import-recovery/${encodeURIComponent(key)}`);
      if (!response.ok) return null;
      return (await response.json()) as RecoveredSession | null;
    },
    set: async (key: SessionRecoveryKey, value: RecoveredSession) => {
      await fetch(`/api/import-recovery/${encodeURIComponent(key)}`, {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(value),
      });
    },
    remove: async (key: SessionRecoveryKey) => {
      await fetch(`/api/import-recovery/${encodeURIComponent(key)}`, {
        method: "DELETE",
      });
    },
  },
});

Adapter Contract

Your custom adapter only needs three methods:

type SessionRecoveryKey = string;

type RecoveredSessionData = {
  rowData: Record<string, unknown>[];
  columnKeys: string[];
};

type RecoveredSession = {
  data: RecoveredSessionData;
};

get(key: SessionRecoveryKey): Promise<RecoveredSession | null>;
set(key: SessionRecoveryKey, value: RecoveredSession): Promise<void>;
remove(key: SessionRecoveryKey): Promise<void>;

Recovered Payload

type RecoveredSessionData = {
  rowData: Record<string, unknown>[];
  columnKeys: string[];
};

Recovered Session

type RecoveredSession = {
  data: RecoveredSessionData;
};

Recovery Key

SessionRecoveryKey is an opaque string generated by the SDK. Treat it as a lookup key only.