@csvbox/react

The CSV importer your React app was missing.

One dependency, a component that renders your own button, and validated rows in the callback. @csvbox/react behaves the same in Next.js, Remix, Vite, and Create React App — because all the SDK does is open the importer and hand you the result.

  • Under 10 lines to first import
  • Validated rows only
  • SOC 2 Type II + GDPR
Installnpm install @csvbox/react
ImportButton.jsx
import { CSVBoxButton } from '@csvbox/react'

export default function ImportButton() {
  return (
    <CSVBoxButton
      licenseKey="YOUR_LICENSE_KEY_HERE"
      user={{ user_id: 'default123' }}
      onImport={(result, data) => {
        if (result) {
          console.log(data.row_success + ' rows uploaded')
        } else {
          console.log('fail')
        }
      }}
      render={(launch, isLoading) => (
        <button disabled={isLoading} onClick={launch}>
          Upload file
        </button>
      )}
    >
      Import
    </CSVBoxButton>
  )
}

A CSV importer is deceptively large. Parsing is the easy part — what eats the sprint is column mapping, type coercion, per-row validation, readable error surfacing, partial re-uploads, and large-file handling that does not lock the main thread. @csvbox/react puts all of it behind a single component.

The SDK deliberately does very little. It loads the importer, opens the modal, and calls you back with the result. Your schema, your validation rules, and your destination all live in the CSVbox dashboard — which is why adding a column never means shipping a React build.

Because the trigger is a render prop, the button stays yours. Your design system, your disabled state, your className. Nothing about CSVbox leaks into your component tree or your CSS.

  • One dependency — no peer-dep sprawl and no bundled UI framework
  • The render prop hands you launch() and isLoading, so you style the trigger
  • Schema and validation live in the dashboard — column changes need no redeploy
  • Same component in Next.js, Remix, Vite, and Create React App

How it works

  1. 1
    Install the package

    Run npm install @csvbox/react. It is a thin wrapper — the importer itself loads at runtime from js.csvbox.io, so it adds very little to your bundle.

  2. 2
    Define your sheet

    In the CSVbox dashboard, set your columns, data types, and validation rules. Save it and you get a license key for that sheet.

  3. 3
    Render the button

    Drop <CSVBoxButton> into your tree, pass the license key and the current user, and return your own button from the render prop.

  4. 4
    Handle onImport

    Your callback fires with (result, data). Refresh a table, show a toast, or move the user on — the validated rows are already on their way to your destination.

Time to first import: ~10 minutes from npm install to your first validated import.

Works with Next.js, Remix, Vite, and CRA. The render prop hands you launch() and isLoading so the button stays yours.

CSVBoxButton props

PropTypeWhat it does
licenseKeystringThe sheet key from your CSVbox dashboard. Required.
userobjectAttributes attached to the import. user_id is mandatory; anything else you add is echoed back to your destination.
onImportfunctionFires when an import finishes. Receives (result, data) — result is a boolean, data carries the row counts.
renderfunctionReturn your own trigger. Receives (launch, isLoading); call launch() to open the modal.
optionsobjectPer-import overrides — row limits, language, theme, custom request headers, and more.
lazybooleanDefer loading the importer until the button is clicked, keeping it off your initial page load.
loadStartedfunctionFires once the importer bundle begins loading — useful for your own spinner.
onReadyfunctionFires when the importer is loaded and ready to open.
onSubmitfunctionFires when the user submits their file, before processing completes.
onClosefunctionFires when the user dismisses the modal.
Full React install docs

Next.js — render the button on the client

app/import/page.jsx
'use client'

import { CSVBoxButton } from '@csvbox/react'

export default function ImportPage() {
  return (
    <CSVBoxButton
      licenseKey={process.env.NEXT_PUBLIC_CSVBOX_KEY}
      user={{ user_id: 'default123' }}
      lazy
      onImport={(result, data) => {
        if (result) {
          console.log(data.row_success + ' rows uploaded')
        }
      }}
      render={(launch, isLoading) => (
        <button className="btn" disabled={isLoading} onClick={launch}>
          Import customers
        </button>
      )}
    >
      Import
    </CSVBoxButton>
  )
}

The importer runs in the browser, so it has to render client-side. In the App Router the "use client" directive is all it takes. In the Pages Router, pull the component in with next/dynamic and ssr: false.

Every React setup, one component

The SDK has no opinion about your build tool or your router. If your component tree renders in a browser, the importer works.

Next.js

Add "use client" in the App Router, or next/dynamic with ssr: false in the Pages Router.

Remix

Render inside a ClientOnly boundary, or gate on a mounted flag so it only paints after hydration.

Vite

Works with no configuration at all — install and import.

Create React App

Works with no configuration at all — install and import.

Common use cases

Customer onboarding

New accounts arrive with a spreadsheet of contacts, products, or records. Let them upload it on day one instead of emailing it to your support team.

Bulk record creation

Users add hundreds of rows at once — employee lists, SKUs, price books — without typing them into a form.

Migration from a competitor

Accept the CSV a customer exported from their old tool and map it to your schema without writing a bespoke parser per source.

Internal admin tooling

Give your own ops team a validated upload path so nobody is running INSERT statements by hand.

React import questions, answered

Does @csvbox/react work with Next.js?

Yes. The importer runs in the browser, so render the button client-side — the "use client" directive in the App Router, or next/dynamic with ssr: false in the Pages Router. Everything else is identical.

Can I use my own button styling?

Yes, that is the point of the render prop. It hands you launch() and isLoading and renders whatever you return, so the trigger is an ordinary element in your design system with your own classes and disabled state.

How much does the SDK add to my bundle?

Very little. @csvbox/react is a thin wrapper; the importer itself is loaded at runtime from js.csvbox.io. Set the lazy prop and that load is deferred until the user actually clicks the button.

How do I pass extra context to my backend?

Put it on the user object. user_id is mandatory, and any other attributes you add are attached to the import and echoed back to your destination, so you can scope rows to a tenant, workspace, or project.

Do schema changes require a redeploy?

No. Columns, data types, and validation rules live in the CSVbox dashboard. Change them there and the running importer picks them up — your React bundle never changes.

Can I hook into events other than the final import?

Yes. Alongside onImport there are onReady, loadStarted, onSubmit, and onClose, so you can drive your own loading states and analytics around the modal lifecycle.

React picks the SDK. Your back end picks the destination.

The SDK you install has nothing to do with where the data lands. Whatever you run server-side — Node, Rails, Django, Laravel, Go, .NET, or no server at all — CSVbox delivers validated rows to your webhook, database, or no-code tool.

Browse 20+ destinations

Stop building CSV importers.

Ship ours in 15 minutes. Free forever on the Sandbox plan.

No credit cardEmbed in minutesSecure by default