Why Dromo?

The CSV importer for any JavaScript app

Framework-free CSV and Excel import for any JavaScript app. One script tag, AI column mapping, real-time validation, results as clean JSON. Free dev mode.

View docs
import DromoUploader from "dromo-uploader-js";

const dromo = new DromoUploader(
  "YOUR_FRONTEND_LICENSE_KEY",
  [{ label: "Email", key: "email", type: "email" }]
);

dromo.open();

Add a CSV importer to JavaScript in 10 minutes

No framework, no bundler, no build step. Load the SDK from a script tag, define fields, and open the importer from a click handler. This works inside a Rails view, a Django template, a WordPress admin page, or a static HTML file.

<script src="https://cdn.jsdelivr.net/npm/dromo-uploader-js/dist/dromo-uploader-js.js"></script>

<button id="import-products" type="button">Import products</button>

<script>
  const fields = [
    { label: "SKU", key: "sku", type: "string", validators: [{ validate: "required" }, { validate: "unique" }] },
    { label: "Name", key: "name", type: "string", validators: [{ validate: "required" }] },
    { label: "Price", key: "price", type: "number" },
    {
      label: "Category",
      key: "category",
      type: "select",
      selectOptions: [
        { label: "Apparel", value: "apparel" },
        { label: "Footwear", value: "footwear" },
        { label: "Accessories", value: "accessories" },
      ],
    },
  ];

  const uploader = new DromoUploader(
    "YOUR_LICENSE_KEY",
    fields,
    { importIdentifier: "products", developmentMode: true },
    { id: document.body.dataset.userId }
  );

  uploader.onResults(async (rows, metadata) => {
    await fetch("/products/import", {
      method: "POST",
      headers: { "Content-Type": "application/json", "X-CSRF-Token": window.csrfToken },
      body: JSON.stringify({ importId: metadata.id, rows }),
    });
    window.location.reload();
  });

  document.getElementById("import-products").addEventListener("click", () => uploader.open());
</script>

If you do have a bundler, the npm package exposes the same class as an ES module:

npm install dromo-uploader-js
import DromoUploader from "dromo-uploader-js";

Everything else is identical. The constructor takes a license key, a fields array, a settings object, and a user object; onResults receives the validated rows and import metadata; open() shows the modal.

How it fits the JavaScript model

The SDK is a plain class with no opinion about how the rest of your page is built. It attaches its own modal to the document, so it coexists with jQuery plugins, Alpine directives, Web Components, and server-rendered HTML that never touches a virtual DOM. If your app is a Rails monolith with Turbo, initialize the uploader inside a turbo:load listener and it survives navigation.

Nothing in the flow depends on a router or a state library. Because the uploader is instantiated in your code, you decide when it is created: once at page load, or lazily on the first click if the script should not run until needed.

Vue and Svelte teams use the same package. Wrap the constructor in a composable or a store, call open() from a button, and treat the result the way you would any async event.

JavaScript gotchas Dromo handles for you

Encoding surprises. Excel on Windows exports CSV in a legacy code page, macOS adds a UTF-8 byte order mark, and a hand-rolled FileReader.readAsText call gets one of them wrong. Names with accents become question marks and nobody notices until a customer complains. Dromo handles encodings and lets you set a default for files that give no hints.

Excel is not CSV. The first support ticket after launching a "CSV" importer is a customer attaching an .xlsx. Supporting it yourself means pulling in a second parser and handling multi-sheet workbooks. XLS and XLSX work out of the box, alongside CSV and TSV.

A frozen tab. Parsing a large file synchronously blocks the main thread, and the browser eventually offers the user a "this page is unresponsive" dialog. Dromo processes the file inside the widget without stalling the page, and for files too large to correct in one session, the user can download the annotated workbook, fix it offline, and pick up where they left off.

Header rows that are not on row one. Exports from finance and inventory tools put a title, a date range, and a blank line above the real headers. A naive parser treats "Inventory Report 2026-08" as a column. Header row overrides handle it, and the user can confirm the header row visually during matching.

Delimiters that are not commas. European locales export with semicolons, some ERPs emit pipe-delimited text, and a parser hardcoded to , produces one giant column. Delimiter detection is configurable rather than assumed.

Dromo vs building it with PapaParse

Build it yourself Dromo
Column mapping Hand-built select elements and DOM plumbing for each header Suggestions that match headers to your fields and persist for repeat imports
Validation Regex and type checks written per field in your click handler Declared once per field; custom validators can hit your API during the import
Error correction UI Alert boxes, or a home-grown editable table Errors shown inline in a spreadsheet-style step the user edits directly
Large files Streaming callbacks and manual memory discipline Processed inside the widget with offline fix-and-resume
Internationalization Your own string table for every step 30+ languages built in
Ongoing maintenance Every new export format is code you own Configuration, editable by a PM in Schema Studio

PapaParse is excellent at what it does. If a script needs to read a predictable CSV and nothing more, use it and skip the vendor. Dromo becomes worth it when the file is written by someone outside your company and a human needs to be walked through fixing it. Parsing was never the bottleneck; the correction loop is.

Ship it to your users

White labeling comes with every plan at no extra cost. Set the colors and fonts to match a Bootstrap theme or a decade-old admin panel, edit any string in the flow, and the widget carries no Dromo branding.

The importer ships with translations for more than thirty languages, so a script tag on a page served in German or Japanese produces a German or Japanese import experience without extra files.

When customers refuse to let data leave their browser, Private Mode runs the whole import client-side with nothing retained by Dromo. The same class, the same onResults, one extra setting.

What Our Customers Say

G2 High Performer Award 2025 badge for Dromo
4.9 out of 5 stars
5 out of 5 stars
Ryan Chan logo
Ryan Chan
CEO at Upkeep.com
E&Y Entrepreneur of the Year finalist
"We have now rolled out Dromo across our entire customer onboarding experience, and it has made our lives so much easier. Most importantly, our customers love the new experience!"

FAQ

Frequently asked questions

  • No. The JavaScript SDK works with a single script tag or an npm import in any vanilla JavaScript, jQuery, or server-rendered app.
  • Files with hundreds of thousands of rows work in the browser. For millions of rows or scheduled feeds, use the Headless API with SFTP.
  • Yes. Errors are highlighted inline in a spreadsheet-style review step, and users can also download an annotated Excel file, fix it offline, and re-upload.
  • Validated JSON that matches your schema exactly, delivered to onResults, a webhook, or the REST API.

Ready to Ship a Better Data Import Experience?

AI-powered mapping. Real-time validation. SOC 2 certified. Deploys in minutes.