Uploading Files with Next.js and Edge Store
Uploading files in a modern web application can be a complex task, but with the right tools, it can be streamlined. In this article, we will explore how to set up file uploads using Next.js and Edge Store, a powerful serverless storage solution. We will walk through the steps of setting up the backend and frontend, ensuring a smooth integration and user experience.
Prerequisites
Before we begin, ensure you have Node.js installed on your machine. We’ll be using Next.js, a React framework, and Edge Store for file storage.
Step 1: Setting Up Next.js and Edge Store
First, create a new Next.js project if you haven’t already:
npx create-next-app@latest my-upload-app
cd my-upload-app
Next, install the necessary dependencies:
npm install @edgestore/server @edgestore/react zod
Step 2: Configuring the Edge Store Backend
Create a new file at app/api/edgestore/[...edgestore]/route.ts
with the following content:
import { initEdgeStore } from '@edgestore/server';
import { createEdgeStoreNextHandler } from '@edgestore/server/adapters/next/app';
const es = initEdgeStore.create();
/**
* This is the main router for the Edge Store buckets.
*/
const edgeStoreRouter = es.router({
publicFiles: es.fileBucket(),
});
const handler = createEdgeStoreNextHandler({
router: edgeStoreRouter,
});
export { handler as GET, handler as POST };
/**
* This type is used to create the type-safe client for the frontend.
*/
export type EdgeStoreRouter = typeof edgeStoreRouter;
This code sets up the Edge Store router, which will handle file uploads.
Step 3: Setting Up the Frontend Client
Next, create a file at lib/edgestore.ts
:
"use client"
import { EdgeStoreRouter } from "@/app/api/edgestore/[...edgestore]/route"
import { createEdgeStoreProvider } from "@edgestore/react"
export const { EdgeStoreProvider, useEdgeStore } = createEdgeStoreProvider<EdgeStoreRouter>();
This code initializes the Edge Store client for the frontend.
Step 4: Integrating Edge Store Provider in Layout
Update app/layout.tsx
to include the Edge Store provider:
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import { EdgeStoreProvider } from "@/lib/edgestore";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={inter.className}>
<EdgeStoreProvider>
{children}
</EdgeStoreProvider>
</body>
</html>
);
}
Step 5: Configuring Environment Variables
Create an account on Edge Store and create a new project. Copy the access keys and save them in a .env
file in the root of your project:
EDGE_STORE_ACCESS_KEY=your_access_key
EDGE_STORE_SECRET_KEY=your_secret_key
Step 6: Creating the Upload Component
Now, let’s create a page that allows users to upload files. Create or update the app/page.tsx
with the following content:
"use client"
import { useEdgeStore } from "@/lib/edgestore";
import Link from "next/link";
import { useState } from "react";
export default function Home() {
const [file, setFile] = useState<File | null>(null);
const [urls, setUrls] = useState<{ url: string, thumbnailUrl: string | null }>();
const { edgestore } = useEdgeStore();
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.files && event.target.files.length > 0) {
setFile(event.target.files[0]);
}
};
const handleUpload = async () => {
if (file) {
console.log('Uploading:', file.name);
const res = await edgestore.publicFiles.upload({ file });
console.log(res);
setUrls({
url: res.url,
thumbnailUrl: res.thumbnailUrl,
});
}
};
return (
<div className="flex flex-col items-center p-4 border border-dashed border-gray-300 rounded-lg">
<input
type="file"
onChange={handleFileChange}
className="mb-4"
/>
{file && (
<div className="mb-4 text-gray-700">
Selected file: {file.name}
</div>
)}
<button
onClick={handleUpload}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
>
Upload
</button>
{urls?.url && <Link href={urls.url} target="_blank">URL</Link>}
{urls?.thumbnailUrl && <Link href={urls.thumbnailUrl} target="_blank">THUMBNAIL</Link>}
</div>
);
}
Step 7: Adding a Progress Bar
To enhance user experience, we can add a progress bar to show the upload progress. Update the app/page.tsx
:
"use client"
import { useEdgeStore } from "@/lib/edgestore";
import Link from "next/link";
import { useState } from "react";
export default function Home() {
const [file, setFile] = useState<File | null>(null);
const [progress, setProgress] = useState(0);
const [urls, setUrls] = useState<{ url: string, thumbnailUrl: string | null }>();
const { edgestore } = useEdgeStore();
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.files && event.target.files.length > 0) {
setFile(event.target.files[0]);
}
};
const handleUpload = async () => {
if (file) {
console.log('Uploading:', file.name);
const res = await edgestore.publicFiles.upload({
file,
onProgressChange: (progress) => {
setProgress(progress);
}
});
console.log(res);
setUrls({
url: res.url,
thumbnailUrl: res.thumbnailUrl,
});
}
};
return (
<div className="flex flex-col items-center p-4 border border-dashed border-gray-300 rounded-lg">
<input
type="file"
onChange={handleFileChange}
className="mb-4"
/>
<div className="h-[6px] w-44 border rounded overflow-hidden">
<div
className="h-full bg-blue-500 transition-all duration-150"
style={{
width: `${progress}%`
}}
/>
</div>
{file && (
<div className="mb-4 text-gray-700">
Selected file: {file.name}
</div>
)}
<button
onClick={handleUpload}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 mt-5"
>
Upload
</button>
{urls?.url && <Link href={urls.url} target="_blank">URL</Link>}
{urls?.thumbnailUrl && <Link href={urls.thumbnailUrl} target="_blank">THUMBNAIL</Link>}
</div>
);
}
With these steps, you’ve set up a complete file upload solution using Next.js and Edge Store. This integration allows you to handle file uploads efficiently, providing feedback to users through progress bars and links to uploaded files. This setup is scalable and can be customized further based on your application’s requirements.
Happy coding!