Getting Started
Next.js App Router
Complete setup guide for ImportCSV in Next.js 15+ with App Router
Installation
npm install @importcsv/react zod
# Optional: for Excel support
npm install xlsxQuick Start (Recommended)
Since ImportCSV uses client-side file processing, you need to mark components as client components:
'use client';
import { CSVImporter } from '@importcsv/react';
import { z } from 'zod';
import { useState } from 'react';
// Define your data schema
const contactSchema = z.object({
name: z.string().min(1, 'Name is required'),
email: z.string().email('Must be a valid email'),
phone: z.string().optional(),
company: z.string()
});
// TypeScript automatically infers the type
type Contact = z.infer<typeof contactSchema>;
export default function ImportPage() {
const [isOpen, setIsOpen] = useState(false);
const handleComplete = (contacts: Contact[]) => {
console.log(`Imported ${contacts.length} contacts`);
// contacts is fully typed: contacts[0].name, contacts[0].email, etc.
setIsOpen(false);
};
return (
<div className="p-8">
<button
onClick={() => setIsOpen(true)}
className="px-4 py-2 bg-blue-500 text-white rounded"
>
Import Contacts
</button>
<CSVImporter
modalIsOpen={isOpen}
modalOnCloseTriggered={() => setIsOpen(false)}
schema={contactSchema}
onComplete={handleComplete}
/>
</div>
);
}With API Route
Create an API route to handle the imported data:
import { NextResponse } from 'next/server';
import { z } from 'zod';
import { db } from '@/lib/db'; // Your database
const contactSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
phone: z.string().optional(),
company: z.string()
});
export async function POST(request: Request) {
try {
const { contacts } = await request.json();
// Validate all contacts
const validated = contacts.map((c: unknown) =>
contactSchema.parse(c)
);
// Insert into database
const result = await db.contact.createMany({
data: validated,
skipDuplicates: true
});
return NextResponse.json({
success: true,
count: result.count
});
} catch (error) {
return NextResponse.json(
{ error: 'Import failed' },
{ status: 400 }
);
}
}Update your component to call the API:
const handleComplete = async (contacts: Contact[]) => {
try {
const response = await fetch('/api/contacts/import', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ contacts })
});
if (!response.ok) throw new Error('Import failed');
const result = await response.json();
console.log(`Successfully imported ${result.count} contacts`);
setIsOpen(false);
} catch (error) {
console.error('Import error:', error);
}
};Server Actions Integration
Process imported data with Next.js Server Actions:
'use client';
import { CSVImporter } from '@importcsv/react';
import { z } from 'zod';
import { useState } from 'react';
import { importContacts } from './actions';
const contactSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
phone: z.string().optional(),
company: z.string()
});
export default function ImportPage() {
const [isOpen, setIsOpen] = useState(false);
return (
<CSVImporter
modalIsOpen={isOpen}
modalOnCloseTriggered={() => setIsOpen(false)}
schema={contactSchema}
onComplete={async (data) => {
await importContacts(data);
setIsOpen(false);
}}
/>
);
}'use server';
import { db } from '@/lib/db';
export async function importContacts(contacts: any[]) {
// Validate and insert into database
await db.contacts.insertMany(contacts);
return { success: true };
}Inline Mode
Display the importer directly in the page without a modal:
'use client';
import { CSVImporter } from '@importcsv/react';
import { z } from 'zod';
const schema = z.object({
name: z.string().min(1),
email: z.string().email()
});
export default function ImportPage() {
return (
<div className="max-w-4xl mx-auto p-8">
<h1 className="text-2xl font-bold mb-4">Import Contacts</h1>
<CSVImporter
isModal={false}
schema={schema}
onComplete={(data) => {
console.log('Imported:', data);
}}
/>
</div>
);
}Theming
Match your app's design with custom theming:
<CSVImporter
schema={schema}
onComplete={handleComplete}
theme="dark"
primaryColor="#3b82f6"
/>Available themes:
"default"- Clean, modern light theme"dark"- Dark mode"professional"- Enterprise-grade styling- Custom CSS variables (see Theming Guide)
Next Steps
🎨 Headless Components
Build custom UI with headless primitives
🛡️ Zod Schemas
Advanced validation patterns
📖 API Reference
Complete props documentation
💡 Examples
More advanced examples
Common Issues
Error: "window is not defined"
Make sure you're using 'use client' directive at the top of your file:
'use client'; // ← Required!
import { CSVImporter } from '@importcsv/react';Excel files not working
Install the xlsx library:
npm install xlsxType errors with Zod schema
Ensure you're passing the schema correctly and the onComplete callback has proper types:
const schema = z.object({ name: z.string() });
type Data = z.infer<typeof schema>;
<CSVImporter
schema={schema}
onComplete={(data: Data[]) => {
// data is typed correctly
}}
/>