Skip to content

Add CSV import to Next.js

Integrate CSVJetty with the Next.js App Router or Pages Router, review CSV records, and connect imports to your own API.

View as Markdown

1. Install and configure

Create an importer in the CSVJetty dashboard, select callback delivery, and copy its importer key. Replace YOUR_IMPORTER_KEY in the examples with that key.

Configure name and email as required fields, and company as optional. Enable Require valid rows for this walkthrough.

Display labelField nameData typeRequired
Full namenameTextYes
Email addressemailEmailYes
CompanycompanyTextNo
bash
npm install csvjetty
CSVJetty dashboard field configuration with labels, names, types, required values and examples
Dashboard configuration example. The screenshot uses a different schema from the contact examples below; configure the fields specified in each guide.

2. App Router: use a client component

Save the following as app/contacts/ContactImport.jsx. The client directive allows the component to hold state and handle the import callback.

jsx
"use client";

import { useState } from "react";
import CSVJetty from "csvjetty";

export default function ContactImport() {
  const [contacts, setContacts] = useState([]);
  return (
    <section>
      <CSVJetty
        importerKey="YOUR_IMPORTER_KEY"
        onData={({ rows }) => {
          setContacts((current) => [...current, ...rows]);
        }}
      >
        Import contacts
      </CSVJetty>
      <pre aria-live="polite">{JSON.stringify(contacts, null, 2)}</pre>
    </section>
  );
}

Render it from app/contacts/page.jsx:

jsx
import ContactImport from "./ContactImport";

export default function ContactsPage() {
  return (
    <main>
      <h1>Import contacts</h1>
      <ContactImport />
    </main>
  );
}

3. Pages Router: add a page

For an application using the Pages Router, save the following as pages/import-contacts.jsx. Choose the example for your router; you do not need both.

jsx
import { useState } from "react";
import CSVJetty from "csvjetty";

export default function ContactImport() {
  const [contacts, setContacts] = useState([]);
  return (
    <section>
      <CSVJetty
        importerKey="YOUR_IMPORTER_KEY"
        onData={({ rows }) => {
          setContacts((current) => [...current, ...rows]);
        }}
      >
        Import contacts
      </CSVJetty>
      <pre aria-live="polite">{JSON.stringify(contacts, null, 2)}</pre>
    </section>
  );
}

4. Test the workflow

Download contacts.csv →

Open your new page, click Import contacts, and upload the sample. Review mappings and finish the import. The received records appear on the page. Refreshing clears this demonstration data.

Enable Development Mode on your importer while testing. Localhost does not enable it automatically. Development imports still deliver records, so use a test backend when you connect persistence.

CSV records with dropdowns for matching source headers to destination fields
Customers review the source records and adjust destination fields in each column header. The screenshot uses the demo schema.

5. Send records to your server

Once your application provides POST /api/contacts/import, use this callback component. For App Router, keep the client directive shown below. For Pages Router, the same component works without that directive.

jsx
"use client";

import CSVJetty from "csvjetty";

export default function ContactImport() {
  return (
    <CSVJetty
      importerKey="YOUR_IMPORTER_KEY"
      onData={async ({ rows, importId, rowNumbers }) => {
        const response = await fetch("/api/contacts/import", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ rows, importId, rowNumbers }),
        });
        if (!response.ok) throw new Error("Could not save contacts.");
        const text = await response.text();
        return text ? JSON.parse(text) : undefined;
      }}
    >
      Import contacts
    </CSVJetty>
  );
}

The API route must use your existing authentication, authorization, validation, and database code. Return an empty JSON errors array for success, or row errors using original CSV row numbers. Use importId and rowNumbers to make writes idempotent.

json
{
  "errors": [
    { "row": 2, "message": "Email already exists." }
  ]
}

Read callback delivery documentation →

Watch the import and configuration workflows

Open or download this recording
A 27-second dashboard preview walkthrough; preview mode does not deliver rows to your application.

Watch the backend setup recording →

Check whether CSVJetty fits your requirements →

Build your importer

Configure the fields your app expects, then connect CSVJetty to your application.

Create an importer →