Implementing an Elegant Confirm Dialog and Product Export in Next.js
TL;DR: I replaced window.confirm with a custom ConfirmDialog component and implemented a product export feature using Next.js API routes. These changes improve the user experience and provide a more secure way to handle confirmations and data exports.
The Problem
The initial problem was to replace the native window.confirm function with a more customizable and elegant solution. Additionally, there was a need to export products data in CSV format. The existing implementation was using window.confirm which was not providing a good user experience.
What I Tried First
Initially, I tried to use a simple alert function to handle confirmations, but it was not providing the desired user experience. I also attempted to use a third-party library for handling confirmations, but it was not customizable enough.
The Implementation
Confirm Dialog Component
I created a new component called ConfirmDialog which uses React state to manage the confirmation process.
// components/ui/confirm-dialog.tsx
"use client";
import { useState } from "react";
import { AlertTriangle, Loader2 } from "lucide-react";
import { Button } from "@/components/ui/button";
interface ConfirmDialogProps {
title: string;
description: string;
onConfirm: () => void;
onCancel: () => void;
}
const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
title,
description,
onConfirm,
onCancel,
}) => {
const [isLoading, setIsLoading] = useState(false);
const handleConfirm = async () => {
setIsLoading(true);
await onConfirm();
setIsLoading(false);
};
return (
<div>
<h2>{title}</h2>
<p>{description}</p>
<Button onClick={handleConfirm} disabled={isLoading}>
{isLoading ? <Loader2 /> : "Confirm"}
</Button>
<Button onClick={onCancel} variant="outline">
Cancel
</Button>
</div>
);
};
export default ConfirmDialog;
Product Export API Route
I created a new API route to handle product exports.
// app/api/export/products/route.ts
import { NextRequest, NextResponse } from "next/server";
import { auth } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
export async function GET(req: NextRequest) {
const products = await prisma.product.findMany();
const csv = products
.map((product) => Object.values(product).join(","))
.join("\n");
return new NextResponse(csv, {
headers: {
"Content-Type": "text/csv",
"Content-Disposition": `attachment; filename="products.csv"`,
},
});
}
Integration with Existing Components
I integrated the ConfirmDialog component with existing components that were using window.confirm.
// features/manuals/manual-card-actions.tsx
"use client";
import { ConfirmDialog, useConfirm } from "@/components/ui/confirm-dialog";
import { useState } from "react";
import { Download, BookOpen, Trash2 } from "lucide-react";
const ManualCardActions = () => {
const { confirm } = useConfirm();
const handleDelete = async () => {
if (await confirm("Are you sure you want to delete this manual?")) {
// Delete manual logic
}
};
return (
<div>
<Button onClick={handleDelete}>
<Trash2 />
</Button>
<ConfirmDialog
title="Confirm Deletion"
description="Are you sure you want to delete this manual?"
onConfirm={handleDelete}
onCancel={() => {}}
/>
</div>
);
};
Key Takeaway
The key takeaway from this implementation is to use custom components to handle confirmations and data exports, rather than relying on native browser functions or third-party libraries. This approach provides more control over the user experience and security.
What's Next
The next step is to implement more features to the ConfirmDialog component, such as support for custom icons and loading states. Additionally, I plan to implement more API routes for exporting other types of data.
vibecoding #buildinpublic #nextjs #react #typescript
Part of my Build in Public series — sharing the real process of building Building Ismerely KB from Playa del Carmen, México.
Repo: zaerohell/bienestar-integral-kb · 2026-07-03
#playadev #buildinpublic
Top comments (0)