Skip to content

Start here

How to import CSV into Supabase

Use the Supabase Table Editor or PostgreSQL COPY, then check column ownership, tenant context, constraints and the imported result.

Mohamed Atef — Founder, ImportFlow
Last reviewed

For a small, prepared CSV, open the target table in Supabase’s Table Editor, choose Insert → Import Data from CSV, select the file, and review the import before confirming. For a new table, start with New table and its CSV import option. The dashboard currently documents a 100MB limit.

That is often enough for a development dataset or a controlled admin import. If the file is a customer export going into an existing application, first check that its fields mean what the destination expects. A successful upload cannot decide which tenant owns a contact or whether an old account number identifies the same account in your app.

The official Supabase import guide covers the available entry points. This guide works through the decisions around a one-time customer import.

Scope and assumptions. Supabase dashboard instructions reviewed September 13, 2026. SQL examples target PostgreSQL 17 and use synthetic data.

Choose the smallest import path that fits

SituationReasonable starting pointYou still own
Small file, known columns, admin doing the workTable Editor CSV importReviewing the target, source values and result
One-time import with deliberate SQL reviewpsql and COPY, often through stagingConnection authority, mapping, transaction and verification
Import must use an application user’s permissionsApplication API / INSERT under the intended roleAuthorization, request limits and error handling
Customers regularly bring different exportsA reusable importerMapping UX, jobs, recovery and support

A one-off script is a reasonable middle ground. Keep its input format explicit, rehearse it, and retain enough evidence to determine what happened. For an import your engineer will perform once, a reviewed script is usually enough.

For recurring customer uploads, use the CSV importer architecture checklist. The rest of this page assumes an engineer is preparing a particular import.

Prepare the file for an existing table

Start from a copy of the export. Preserve the original so that a correction remains explainable. Write down the delimiter, encoding, header row and meaning of blank values. If a field is an external identifier, treat it as text even when every visible value contains digits.

For a contacts table, the approved source might contain only these two fields:

CSV
email,display_name
first@example.test,First Contact
second@example.test,"Second, Contact"

The quoted comma belongs inside the second contact’s name. Splitting each line on commas would create a third field. A quoted field can also contain a newline, so counting physical lines is not a reliable row count.

Review duplicate headers and mappings before import. Two source columns called “Email” need an explicit choice. Extra columns should be excluded deliberately. Do not infer that a file’s id, tenant_id or created_at should be writable because the names match.

Starting from Excel or Google Sheets

Export the intended worksheet as UTF-8 CSV, then use the same import path. In Google Sheets, use File → Download → Comma-separated values for the current sheet. In Excel, save a copy in its CSV UTF-8 format. A workbook can contain several sheets; a CSV represents one flat table. Renaming an XLSX file to CSV does not convert it.

Inspect the exported text before uploading it. Check a leading-zero identifier, a date, a decimal and any formula-derived cell. If the spreadsheet has already changed 00042 into 42, exporting as UTF-8 cannot recover the missing zeros. Return to the source or obtain an approved correction.

Resolve dates such as 04/05/2026 with the data owner. Do not guess whether it means April 5 or May 4. Record the intended timezone for timestamps; a date-only anniversary should not acquire a timezone conversion accidentally. Continuous synchronization with a live sheet is a different requirement from this one-time export.

Review what the target expects

Make a short field-ownership list before the first write. For example: email and display name come from the approved source; a tenant comes from authorized application context; an identity comes from PostgreSQL; an audit timestamp follows the application’s chosen rule.

An ordinary default is not a prohibition on source values. A generated expression is different. A missing column, an explicit NULL and an empty string can also produce different outcomes. The defaults and generated values guide demonstrates those differences.

Check what your target table expects with PG Import Check if you have migration SQL or DDL. It reports supported structural declarations locally in your browser. It does not inspect live rows, validate the CSV, resolve effective RLS or approve a production import. Reconcile its input with the actual deployed schema yourself.

Use psql for an explicit column-list import

This small rehearsal uses a temporary table in a disposable PostgreSQL 17 database. It tests CSV mechanics without touching an application table. In a connected psql session, create the table:

SQL
CREATE TEMP TABLE import_contacts_demo (
  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  email text NOT NULL UNIQUE,
  display_name text,
  created_at timestamptz NOT NULL DEFAULT now()
);

Save the CSV above as contacts.csv in the client’s working directory, then run this on one line in psql:

psql
\copy import_contacts_demo (email, display_name) FROM 'contacts.csv' WITH (FORMAT csv, HEADER MATCH, ENCODING 'UTF8')
SQL
SELECT id, email, display_name, created_at
FROM import_contacts_demo
ORDER BY id;

Expect two rows, distinct generated IDs, the comma preserved in the second name, and database timestamps. The explicit column list leaves ID and timestamp generation to the database. In PostgreSQL 17, HEADER MATCH checks names and order. Plain HEADER true skips the header; it does not map values by name.

psql’s backslash-copy command reads a file on the client machine and streams it over the database connection. Server-side COPY FROM '/path' reads the database server’s filesystem. Supabase cannot read a path on your laptop that way.

For a real Supabase connection, obtain the correct endpoint and SSL configuration from the project’s connection instructions. Keep passwords out of saved commands, Git and screenshots. Have the database owner choose the execution role and connection mode; this temporary-table example does not establish production permissions.

Rehearse normalization and failure behavior

A customer file may contain a label such as “Active account” where your enum expects active. A staging representation lets you keep the original value beside the proposed value and a reason for the change. The business owner approves that translation; the importer applies it consistently.

Staging can be a temporary table, a separate disposable database, or a restricted durable area for a longer job. Pick according to sensitivity and recovery needs. It should not be an unprotected table exposed through your application API. Test production-relevant constraints and write behavior in an isolated environment, and record what the rehearsal cannot reproduce.

PostgreSQL’s default COPY behavior stops on an error. A transaction that rolls back does not leave its inserted rows committed, but earlier separately committed batches remain. A script with ten successful requests and an eleventh failed request has a partial result. Do not restart the whole file based only on the last error.

Keep constraints and triggers in the review. A foreign key needs a destination reference, not merely a correctly typed number. A trigger can reject or change a row and may initiate other work. Do not disable integrity checks as a routine fix for a rejected file. Consult the foreign-key import guide when source and destination IDs differ.

For large files, measure runtime, disk use, WAL growth, locks and application latency on a representative rehearsal. Choose bounded batches or a maintenance window from that evidence. The dashboard’s file-size limit is not a performance guarantee for any other path.

Verify before calling onboarding complete

Count parsed source records, record approved exclusions, and compare the resulting accepted set with the destination. If 200 contacts were accepted, 200 inserted rows is useful evidence. It is still possible to have the wrong 200 contacts, or the right contacts assigned to the wrong tenant.

Compare stable business keys, inspect important transformed fields and exercise the normal application read path. Within the authorized tenant, identify this import’s rows using trustworthy import attribution or a reviewed source-to-destination ID map. A tenant filter alone includes existing rows. If attribution is unavailable, compare against an explicit full post-import expectation and state the limitation.

The post-import verification guide provides executable examples for missing keys and mismatched values. Keep the approved source, mapping and result together under an agreed retention policy. Those records make a failed attempt or a customer’s correction much easier to resolve.

Sources and further reading