How to import CSV files in Strapi

6 min read
Learn how to build a CSV import feature in Strapi. Step-by-step guide for developers integrating spreadsheet uploads in SaaS applications.

How to Import CSV Data into Strapi Using CSVBox

Developers using Strapi for content or backend management often need a reliable way to ingest structured data from spreadsheets. Whether you’re migrating records, building an admin importer, or giving non-technical editors a bulk-edit workflow, a CSV import feature speeds onboarding and operations. This guide shows how to add a CSV upload flow to Strapi using CSVBox, with practical examples and Strapi v4-compatible code (as of 2026).

High-level flow: file → map → validate → submit → save.


Why Add a CSV Import Feature to Strapi?

Strapi doesn’t expose a built-in, user-facing CSV upload UI. Adding CSV import is useful for:

  • Bulk onboarding (product catalogs, customers, orders)
  • Migrating legacy spreadsheets into Strapi collections
  • Empowering non-technical editors to update structured content

A correct importer needs a UI, column mapping, validation, secure webhook delivery, and backend error handling. CSVBox handles the file UI, mapping, and validation, and posts structured JSON to your Strapi webhook so you can focus on data normalization and persistence.

Real-world examples:

  • Marketplace admins importing product catalogs
  • HR platforms ingesting candidate lists
  • SaaS apps letting clients upload customer or order data

What Is CSVBox and Why Use It?

CSVBox is a drop-in CSV import widget that helps you:

  • Validate spreadsheet structure and required columns before sending
  • Preview uploads and show friendly validation errors to users
  • Map columns and transform common formats (dates, booleans)
  • Deliver validated data as structured JSON to your webhook

Using CSVBox avoids building a custom CSV parser, mapping UI, and preview flow from scratch, saving developer time and improving UX.

For integration details and settings, see the official CSVBox docs and guides at help.csvbox.io.


Step-by-Step: How to Integrate CSV Import in Strapi

1. Create or Use an Existing Strapi Project

If you need a fresh project:

npx create-strapi-app my-project --quickstart
cd my-project
npm run develop

Confirm Strapi runs at http://localhost:1337 (default).


2. Create an Import Controller in Strapi (v4)

Create a controller to receive CSVBox webhook payloads and persist rows to a collection. In Strapi v4, use strapi.entityService to create entries and refer to collection type UIDs (api::collectionName.collectionName).

Create the file src/api/upload/controllers/upload.js with:

'use strict';

module.exports = {
  async import(ctx) {
    const { rows } = ctx.request.body || {};

    if (!Array.isArray(rows) || rows.length === 0) {
      return ctx.badRequest('No rows provided');
    }

    try {
      for (const row of rows) {
        // Change the UID to your content-type, e.g. 'api::product.product'
        await strapi.entityService.create('api::product.product', {
          data: row
        });
      }

      return { status: 'success', imported: rows.length };
    } catch (error) {
      strapi.log.error('CSV import failed', error);
      ctx.throw(500, 'Failed to process CSV data');
    }
  }
};

Notes:

  • Replace ‘api::product.product’ with your content type UID (see Content-Types in Strapi admin).
  • Validate and transform each row before saving (e.g., parse dates, enforce enums).

3. Add a Custom Route

Expose a POST route for the webhook. Create src/api/upload/routes/upload.js:

module.exports = {
  routes: [
    {
      method: 'POST',
      path: '/upload/import',
      handler: 'upload.import',
      config: { policies: [] }
    }
  ]
};

Because Strapi mounts API routes under /api by default, the full webhook URL will be: http://localhost:1337/api/upload/import

Restart Strapi after adding controllers/routes.

Pro tip: scope access for this route per your security model — public, authenticated, or token-protected.


4. Configure CSVBox and Create a Widget

In the CSVBox dashboard (app.csvbox.io):

  1. Create a widget (e.g., “User Import”).
  2. Define required CSV columns and formats (name, email, date_of_birth, etc.).
  3. Set the webhook URL to: http://localhost:1337/api/upload/import
  4. Choose POST and enable JSON payload delivery (CSVBox sends a structured rows array).

CSVBox will validate and map the uploaded CSV, then POST the JSON payload to your Strapi webhook.


5. Embed CSVBox Into Your Frontend

You can launch CSVBox from any frontend (React, Vue, Svelte, static pages).

Example HTML + JS integration:

<script src="https://widget.csvbox.io/widget.js"></script>

<button onclick="launchCSVBox()">Import Users</button>

<script>
  function launchCSVBox() {
    CSVBox.show({
      licenseKey: "your-widget-license-key",
      user: {
        user_id: "123",
        name: "Admin User"
      }
    });
  }
</script>

Replace licenseKey with the key from your CSVBox dashboard. The widget handles file selection, column mapping, and validation before sending data to your webhook.


Backend Code Reference (with basic validation & errors)

A slightly more defensive controller:

module.exports = {
  async import(ctx) {
    const { rows } = ctx.request.body || {};

    if (!Array.isArray(rows)) {
      return ctx.badRequest('Invalid payload: rows must be an array');
    }

    try {
      let imported = 0;

      for (const rawRow of rows) {
        // Example: basic normalization
        const row = {
          ...rawRow,
          email: rawRow.email?.toLowerCase?.().trim()
        };

        // Optional: validate required fields
        if (!row.email || !row.name) {
          // skip or collect errors instead of throwing
          continue;
        }

        await strapi.entityService.create('api::product.product', {
          data: row
        });

        imported++;
      }

      return { status: 'success', imported };
    } catch (error) {
      strapi.log.error('CSV Import Failed:', error);
      ctx.throw(500, 'Failed to process CSV data');
    }
  }
};

Make sure the Strapi role permissions allow the intended access to this route (Settings → Roles & Permissions) if you expose it to public or authenticated users.


Security Recommendations

  • Use a shared secret or token to authenticate webhook requests (add middleware to validate an HTTP header or query token).
  • Prefer scoped API tokens for machine-to-server requests rather than wide public access.
  • Validate and sanitize every row before persisting.
  • Log imports and errors for auditing and retries.

Troubleshooting

Common issues and fixes:

403 Forbidden on import

  • Verify role permissions and route exposure in Strapi admin.
  • If using an auth token, confirm CSVBox is including it in the request headers or URL.

Undefined rows in payload

  • Ensure CSVBox is configured to send JSON payloads (not raw CSV).
  • Confirm the webhook payload contains a top-level rows array.

CORS errors when embedding the widget from a different origin

  • Update Strapi CORS in config/middlewares.js, for example:

    module.exports = [ ‘strapi::errors’, ‘strapi::security’, ‘strapi::cors’, ‘strapi::poweredBy’, ‘strapi::logger’, ‘strapi::query’, ‘strapi::body’, ‘strapi::session’, ‘strapi::favicon’, ‘strapi::public’ ];

    Or configure the cors middleware with allowed origins:

    { name: ‘strapi::cors’, config: { origin: [‘http://localhost:3000’], credentials: true } }

Adjust origins to match your frontends (development and production).


CSVBox vs Building a Manual CSV Import

Why use CSVBox:

  • Built-in UI: drag-and-drop, mapping, and previews
  • Frontend validation reduces invalid webhook requests
  • Focus backend work on normalization and business logic instead of file parsing

When to build your own:

  • You need custom parsing logic that the widget can’t support
  • Tight offline/air-gapped workflows where a hosted widget is not acceptable

For most SaaS admin import scenarios, CSVBox accelerates delivery and reduces support burden.


Example Use Cases That Benefit from This Setup

  • SaaS dashboards where customers bulk-import user data
  • Internal CMS tools updating product listings or metadata
  • Admin panels managing geographic or taxonomies
  • LMS platforms ingesting enrollment lists

This setup fits teams of all sizes that need a reliable, auditable CSV import flow.


Extend and Harden Your Import Feature (best practices in 2026)

  • Add JWT or token-based protection to the import route.
  • Normalize and validate types (dates, enums, numbers) before saving.
  • Queue heavy imports for background processing to avoid timeouts.
  • Use separate widgets and routes per entity type to simplify mapping and permissions.
  • Add import summaries and per-row error reporting for better UX.

Explore Strapi docs for background jobs and CSVBox help for widget configuration at help.csvbox.io.


Final Thoughts

A reliable CSV import pipeline reduces manual effort and errors. Using Strapi as your headless CMS and CSVBox as the import layer lets you:

  • Empower users to bulk-upload structured content safely
  • Avoid building and maintaining CSV mapping and preview UIs
  • Focus backend work on validation, normalization, and business rules

Whether you’re handling e-commerce catalogs, CRM data, or editorial imports, this integration provides a fast path to production-ready CSV imports.

See the canonical guide: CSVBox → Strapi Integration (https://help.csvbox.io/integrations/import-to-strapi)


Need more help?

  • CSV validation tools
  • Webhook debuggers
  • JSON payload inspectors

Happy importing!

Related Posts