Import CSV or Excel
Bring a CSV, TSV, Excel or JSON file into a base: as a new table, with a field type guessed for each column (currency, checkbox, date, email, single select) and shown for you to change, or as records of a table, each column matched to the field it names. The import is one undo step. (requires @svgrid/enterprise)
A live, editable Svelte 5 data grid example from the SvGrid gallery (Base). See the SvGrid documentation for the full API.
About this example
Import a CSV, TSV, Excel or JSON file into an SvGrid Base, in Svelte 5. As a new table, each column gets a field type guessed from its values and shown for you to change; into an existing table, each column goes to the field it names and new select values become options. The file is read in the browser and the import is one undo step.
A CSV, TSV, Excel (.xlsx) or JSON file goes into a base two ways:
- as a new table (the list of tables, "Import a file as a table"): each column becomes a field typed from its values. "$1,200" makes a currency, "yes / no" a checkbox, dates a date, an email column an email, a column that repeats a few values a single select with a color per value. The dialog shows each guess and lets you change it.
- as records of the shown table (the table's menu, "Import records from a file"): each column of the file goes to the field it names, matched by name; new select values become options.
The file is read in the page and the import is one undo step. The two buttons below hand the base a sample file through openImport(mode, file), the same dialog the menus open.
Imports, features and API used
Imports: @svgrid/grid, @svgrid/enterprise/base, ../shared/base-people
Columns: name (Name), company (Company), email (Email), stage (Stage), subscribed (Subscribed), stage
Frequently asked questions
How are field types chosen for a new table?
From the values in each column: amounts with a currency sign make a currency field, yes and no a checkbox, ISO or slashed dates a date, addresses with @ an email, and a column that repeats a few short values a single select with a color per value. The dialog lists each guess and you can pick another type before importing.
How are columns matched when adding records to a table?
By name, ignoring case, spaces and punctuation, against each field's name and id. A column that matches nothing is skipped; you can point it at a field by hand. Computed fields such as formulas and lookups are not offered.
Can an app import a file it already has?
Yes. Call openImport(mode, file) on the component through bind:this; the dialog opens with the file read. The two buttons in this demo do exactly that with sample files.
Related documentation
Related articles
- Paste from Excel into a Svelte Data Grid - How to wire up clipboard paste so users can drop a copied Excel or Google Sheets block directly into SvGrid - TSV parsing, type coercion, validation, and row growth all covered.
- Importing CSV into a Svelte Data Grid - Parse a user-uploaded CSV file into objects, map its headers to grid columns, validate rows before committing, and handle the edge cases that actually bite you in production.
- How We Built Excel-Style Filters - How SvGrid implements per-column filter menus, type-aware operators, and a shared filter model that works identically for in-memory and server-side data.
Source code (709-base-import-csv-excel.svelte)
<script lang="ts">
/**
* 709. SvGrid Base: import CSV or Excel
* -------------------------------------
* A CSV, TSV, Excel (.xlsx) or JSON file goes into a base two ways:
*
* - as a new table (the list of tables, "Import a file as a table"): each
* column becomes a field typed from its values. "$1,200" makes a
* currency, "yes / no" a checkbox, dates a date, an email column an
* email, a column that repeats a few values a single select with a
* color per value. The dialog shows each guess and lets you change it.
* - as records of the shown table (the table's menu, "Import records from
* a file"): each column of the file goes to the field it names, matched
* by name; new select values become options.
*
* The file is read in the page and the import is one undo step. The two
* buttons below hand the base a sample file through `openImport(mode, file)`,
* the same dialog the menus open.
*/
import { SvToaster } from '@svgrid/grid'
import { SvBase, enableBase, type BaseTable } from '@svgrid/enterprise/base'
import { usePeople } from '../shared/base-people'
enableBase()
usePeople()
const STAGE = [
{ value: 'New', color: '#94a3b8' },
{ value: 'Contacted', color: '#2d7ff9' },
{ value: 'Customer', color: '#20c933' },
]
let tables = $state.raw<BaseTable[]>([
{
id: 'import-contacts',
name: 'Contacts',
primaryField: 'name',
data: [
{ id: 'c1', name: 'Mara Lindqvist', company: 'Northwind Traders', email: '[email protected]', stage: 'Customer', subscribed: true },
{ id: 'c2', name: 'Tomas Reyes', company: 'Contoso', email: '[email protected]', stage: 'Contacted', subscribed: false },
],
columns: [
{ field: 'name', header: 'Name', width: 200, fieldType: 'text' },
{ field: 'company', header: 'Company', width: 190, fieldType: 'text' },
{ field: 'email', header: 'Email', width: 230, fieldType: 'email' },
{ field: 'stage', header: 'Stage', width: 130, fieldType: 'singleSelect', fieldProps: { options: STAGE } },
{ field: 'subscribed', header: 'Subscribed', width: 110, fieldType: 'checkbox' },
],
views: [{ id: 'all', name: 'All contacts', type: 'grid', color: { mode: 'field', field: 'stage' } }],
},
])
// A sign-up export whose columns are named like the table's fields, in another order, with one extra column.
const CONTACTS_CSV = `Email,Name,Company,Stage,Subscribed,Signed up from
[email protected],Priya Nair,Fabrikam,New,yes,Webinar
[email protected],Li Wei,Litware,Contacted,no,Pricing page
[email protected],Sam Okoro,Tailspin Toys,Trial,yes,Referral
[email protected],Ines Duarte,Proseware,New,yes,Webinar
`
// An inventory sheet for a new table: a currency, a number, a checkbox, a date, a select.
const PRODUCTS_TSV = [
['Product', 'SKU', 'Category', 'Price', 'In stock', 'Reorder', 'Last counted', 'Supplier email'],
['Standing desk', 'DSK-120', 'Furniture', '$649.00', '14', 'no', '2026-10-28', '[email protected]'],
['Monitor arm', 'ARM-02', 'Accessories', '$89.00', '3', 'yes', '2026-10-28', '[email protected]'],
['Task chair', 'CHR-77', 'Furniture', '$329.00', '9', 'no', '2026-10-27', '[email protected]'],
['Desk lamp', 'LMP-10', 'Lighting', '$45.50', '0', 'yes', '2026-10-27', '[email protected]'],
['Cable tray', 'CBL-04', 'Accessories', '$24.00', '41', 'no', '2026-10-26', '[email protected]'],
['Footrest', 'FTR-01', 'Furniture', '$59.00', '6', 'no', '2026-10-26', '[email protected]'],
['Webcam light', 'LMP-22', 'Lighting', '$39.00', '2', 'yes', '2026-10-25', '[email protected]'],
].map((r) => r.join('\t')).join('\n')
let base = $state<ReturnType<typeof SvBase> | null>(null)
const file = (text: string, name: string, type: string) => new File([text], name, { type })
function importContacts() {
base?.openImport('append', file(CONTACTS_CSV, 'signups.csv', 'text/csv'))
}
function importProducts() {
base?.openImport('new', file(PRODUCTS_TSV, 'products.tsv', 'text/tab-separated-values'))
}
</script>
<section class="base-demo">
<div class="base-demo-bar">
<button type="button" class="base-demo-btn" onclick={importContacts}>Import signups.csv into Contacts</button>
<button type="button" class="base-demo-btn" onclick={importProducts}>Import products.tsv as a new table</button>
<span class="base-demo-tip">Or use your own file: the table menu (▾ next to Contacts) or the list of tables.</span>
</div>
<SvBase
bind:this={base}
bind:tables
editable
sortable
showRowNumbers
expandRows
rowNumberWidth={64}
showRowSelection={false}
rowHeight={40}
containerHeight="100%"
columnAdder
fieldMenu
/>
<SvToaster />
</section>
<style>
.base-demo { display: flex; flex-direction: column; flex: 1; min-height: 480px; height: 100%; gap: 10px; }
.base-demo-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.base-demo-btn {
height: 32px; padding: 0 12px; font: inherit; font-size: 13px; cursor: pointer; border-radius: var(--sg-radius, 6px);
color: var(--sg-fg, #0f172a); background: var(--sg-bg, #fff); border: 1px solid var(--sg-border, #e2e8f0);
}
.base-demo-btn:hover { border-color: var(--sg-accent, #2563eb); }
.base-demo-tip { font-size: 13px; color: var(--sg-muted, #64748b); }
</style>More Base examples
- Project tracker - A project tracker, ready to use: Tasks and Milestones as two linked tables. Tasks have status, tags, assignee, dates, estimate, progress, budget and files; each milestone lists its tasks with their progress and budget. Views for all tasks, the board by status, due dates on a calendar, a gallery and a request form. Automations close finished tasks, start work from a button and flag overdue ones.
- Content calendar - An editorial content calendar, ready to use: Posts and Campaigns as two linked tables. Posts have stage, channels, author, publish date, cover and brief. Views for the plan, the pipeline as a Kanban, the publishing calendar, a wall of covers and a pitch form.
- Sales CRM - A sales CRM, ready to use: Deals, Accounts and Contacts as three linked tables. Link a deal to an account and the account lists it. Views for all deals, the pipeline as a Kanban, the closing calendar, open deals by owner and a new-lead form.
- Product roadmap - A product roadmap, ready to use: Features and customer Feedback as two linked tables. Features have quarter, status, teams, lead, impact, effort and votes. Views for the roadmap grouped by quarter, a status board, a timeline, the work in progress now and a request form.
- Hiring tracker - A hiring tracker, ready to use: Candidates and Roles as two linked tables. Candidates have photo, role, stage, recruiter, rating, interview date and resume; each role lists its candidates. Views for the pipeline as a Kanban, all candidates, a gallery of faces, interviews and an application form.