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
npm install @csvbox/reactimport { 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
- 1Install 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.
- 2Define 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.
- 3Render the button
Drop <CSVBoxButton> into your tree, pass the license key and the current user, and return your own button from the render prop.
- 4Handle 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.
Works with Next.js, Remix, Vite, and CRA. The render prop hands you launch() and isLoading so the button stays yours.
CSVBoxButton props
| Prop | Type | What it does |
|---|---|---|
licenseKey | string | The sheet key from your CSVbox dashboard. Required. |
user | object | Attributes attached to the import. user_id is mandatory; anything else you add is echoed back to your destination. |
onImport | function | Fires when an import finishes. Receives (result, data) — result is a boolean, data carries the row counts. |
render | function | Return your own trigger. Receives (launch, isLoading); call launch() to open the modal. |
options | object | Per-import overrides — row limits, language, theme, custom request headers, and more. |
lazy | boolean | Defer loading the importer until the button is clicked, keeping it off your initial page load. |
loadStarted | function | Fires once the importer bundle begins loading — useful for your own spinner. |
onReady | function | Fires when the importer is loaded and ready to open. |
onSubmit | function | Fires when the user submits their file, before processing completes. |
onClose | function | Fires when the user dismisses the modal. |
Next.js — render the button on the client
'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.
Add "use client" in the App Router, or next/dynamic with ssr: false in the Pages Router.
Render inside a ClientOnly boundary, or gate on a mounted flag so it only paints after hydration.
Works with no configuration at all — install and import.
Works with no configuration at all — install and import.
Common use cases
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.
Users add hundreds of rows at once — employee lists, SKUs, price books — without typing them into a form.
Accept the CSV a customer exported from their old tool and map it to your schema without writing a bespoke parser per source.
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.