Column names never match your schema
"Email Addr", "contact email", and "e_mail" all point at the same field.
Rowslint suggests a mapping based on headers and sample values, and asks the user to confirm.
Rowslint is a CSV and Excel import widget you drop into your app. Your users upload a messy file, the AI guesses the column mapping, validation flags the bad rows in their browser, and your backend gets clean data ready to write.
A spreadsheet is usually the moment a prospect becomes an activated user. If the import flow feels confusing, slow, or unforgiving, they leave before your product has a chance to prove itself.
"Email Addr", "contact email", and "e_mail" all point at the same field.
Rowslint suggests a mapping based on headers and sample values, and asks the user to confirm.
The user submits a file, waits a bit, and then your backend rejects row 387.
The rules run in the browser, so bad rows get fixed before anything is sent to your app.
The upload freezes, progress is unclear, and users refresh the tab before onboarding ever finishes.
Chunked parsing keeps the UI moving while the rows are processed in the background.
Your team starts asking for screenshots, cleaning spreadsheets by hand, and re-running imports for customers.
Users get guided fixes, can skip individual rows, and end up with clean import-ready output on their own.
Rowslint isn't just a file picker. It's meant for the workflows where clean customer data unlocks the next paid step: activation, migration, account setup, and the recurring import tasks that keep your team busy.
New customers already have their data somewhere. A guided import path lets them reach the "aha" moment without needing a call with your team first.
Give non-technical operators a branded importer for contacts, inventory, orders, members, leads, or whatever else they need to load, without opening up your admin tools.
Your team shouldn't need a custom script every time ops receives a spreadsheet. Move the validation and review into the workflow itself.
Every internal importer starts simple, then slowly grows mapping, validation, large file handling, retries, duplicate detection, and a small mountain of support debt. Rowslint ships all of that for you.
Start with the workflow that blocks the most users, then roll Rowslint out to the next one.
Every import flow starts the same way and breaks in the same places. Rowslint covers the last 10% (the messy bit your users actually see) so you can focus on what happens once the data lands.
One snippet for React, Vue, or plain JS. It picks up your theme tokens, your auth session, and your brand.
List the fields you expect, with types, regexes, enums, or async validators. Rowslint handles the rest.
Rows arrive at your webhook or database already typed, deduplicated, and validated. Nothing sits on our servers.
Every feature in Rowslint exists because someone, somewhere, got paged at 3am for a malformed CSV. We've seen the worst data you can imagine, and shipped defenses against it.
Point Rowslint at a schema, give it a handler, and ship. Works with React, Vue, Svelte, or plain JS, and the TypeScript types are there from the first line.
Your schema types flow through to the onImport handler. No anys, no casts.
@rowslint/importer-js, /react, /vue, /svelte all share the same API and bundle size.
Lightweight on purpose. Validators, the Excel parser, and locale bundles load on demand.
Bring your own UI and use the hooks for file parsing, mapping, and validation only.
Rowslint fingerprints the file's headers and sample data against your schema. Fuzzy matches, synonyms, casing, whitespace, and common typos are handled before the user even sees the mapping screen, so most of the time they don't have to touch a thing.
Write your rules once with Zod, Yup, regex, or async fetches to your own API. Rowslint runs them on every row and surfaces errors inline, so users can fix them, skip them, or bulk-edit before anything leaves the browser.
| Field | Rule | Sample | Status |
|---|---|---|---|
| z.string().email() | "[email protected]" | passing | |
| phone | z.string().regex(/^\+?[1-9]\d{7,14}$/) | "+14155550142" | passing |
| plan | z.enum(["free","pro","enterprise"]) | "pro" | passing |
| mrr | z.number().positive() | -32.00 | blocked |
| domain | async (v) => await checkDNS(v) | "acm.io" | review |
| created_at | z.coerce.date() | "2026-13-01" | blocked |
Rowslint is built so the risky part of a spreadsheet import happens as close to the user as possible. Rows get validated and reviewed in the browser, then only the clean result gets sent to a destination you control.
Parsing, mapping, and validation can all run before any customer data leaves the tab.
Once an import is ready, the clean rows go to your webhook, database, or storage flow.
Column suggestions are built so spreadsheet data doesn't have to be sent to a third-party AI API.
Self-hosted and customer-managed storage are available on request, just talk to us.
Pick the smallest plan that lets you prove out the import flow. Every paid plan comes with a 14-day free trial, no credit card needed. If you need something custom, drop us a line about an enterprise setup.
Run the importer on a real workflow before you decide.
For SaaS teams where a successful import affects activation.
For teams running imports across multiple portals or ops queues.
14-day trial with no credit card required.
You get usage alerts as you approach the cap, before imports are ever blocked.
Pricing follows import volume, not the number of teammates.
Files are parsed and validated in the browser unless you opt into storage.
Pick Free if you're just trying the workflow or testing a single import surface.
Pick Pro if imports are tied to onboarding, activation, or a paid customer journey.
Pick Max if several teams or customer portals run imports every month.
We've built these systems before, in-house, with the competitors, and from scratch. Rowslint is the version we wish we'd had each time.
Couldn't find what you're looking for? Check the documentation →