Next.js S3 Upload
repository·master·Indexed 20 days ago
https://github.com/ryanto/next-s3-uploadA utility library for streamlining file uploads from Next.js applications directly to Amazon S3 or S3-compatible hosts. It provides the useS3Upload hook, a FileInput component, and the getImageData helper for extracting image dimensions. Supports configuration via environment variables or APIRoute.configure(), and includes guidance for integrating with next/image.
What's inside next-s3-upload
- Next.js S3 Upload is a library designed to provide the fastest way to upload files from a Next.js application directly to Amazon S3.
Compare AWS SDK vs. Presigned Uploads
masterWhen choosing an upload method in
next-s3-upload, consider the following trade-offs:AWS SDK (
useS3Upload)- Pros: Supports large files (via multipart uploads), handles failures and retries automatically, and is the recommended approach for most use cases.
- Cons: Requires loading the AWS SDK JavaScript library on the frontend, increasing bundle size.
Presigned Uploads (
usePresignedUpload)- Pros: No AWS SDK required on the client; smaller bundle size; compatible with non-AWS S3-compatible hosts.
- Cons: Strict 1GB limit (S3 will reject files over 1GB); lacks the automatic multipart/retry features of the SDK.
Important: File extensions and IAM permissions
masterIf your AWS IAM user is configured to only allow specific file extensions, yourkeyfunction must return a path that ends with one of those allowed extensions. If the returned key does not include an allowed extension, the upload will fail with anaccess deniederror.How the useS3Upload workflow works
masterThe standard upload workflow using
useS3Uploadfollows these steps:- Trigger Selection: The user clicks a UI element (like a button) that calls
openFileDialog. - File Selection: The browser opens a file dialog. Once a file is selected, the
onChangehandler of the<FileInput />component is triggered. - Upload: The handler calls
uploadToS3(file). This function performs the upload to S3. - Completion: Upon a successful upload,
uploadToS3returns an object containing theurlof the uploaded file, which can then be used in the application (e.g., to display an image).
- Trigger Selection: The user clicks a UI element (like a button) that calls
Use the FileInput component
masterThe<FileInput />component is a utility provided by the library that renders a hidden file input tag. It is designed to reduce boilerplate code required to wire up functions to open file inputs in React. While<FileInput />is the easiest way to manage the file selection state, you can also implement your own custom file input logic if needed.Security considerations for S3 uploads
masterWhen usingnext/imagewith S3, ensure your S3 bucket policy is restricted to only allow image file uploads. Failing to restrict file types can create a potential XSS (Cross-Site Scripting) vector. Refer to the IAM user setup guide to implement a policy that enforces image-only uploads.Pass data from React to the S3 key function
masterYou can pass custom data from your frontend component to the API route's
keyfunction by using theendpoint.request.bodyoption withinuploadToS3. This data is serialized into the request body and can be accessed viareq.bodyin your API route. This is useful for including metadata likeprojectIdin the file path.// Frontend component function Component() { let { uploadToS3 } = useS3Upload(); let handleSubmit = async () => { await uploadToS3(file, { endpoint: { request: { body: { projectId: 123 } } } }); }; } // pages/api/s3-upload.js import { APIRoute } from "next-s3-upload"; export default APIRoute.configure({ async key(req, filename) { let projectId = req.body.projectId; // 123 return `projects/${projectId}/${filename}`; } });Run the app-dir test application
masterTo start the development server for the
app-dirtest application, use your preferred package manager to run thedevscript. Once running, the application will be available athttp://localhost:3000.npm run dev # or yarn dev # or pnpm dev # or bun devTrack upload progress with the useS3Upload hook
masterThe
useS3Uploadhook returns afilesarray that tracks the upload status of all files currently being processed. Each object in thefilesarray contains aprogressproperty, which is a number between0and100that updates continuously as the file uploads. You can map over this array to render a UI that displays the real-time progress percentage for each file.import { useS3Upload } from "next-s3-upload"; import { useState } from "react"; export default function UploadPage() { let { uploadToS3, files } = useS3Upload(); let handleFileChange = async event => { let file = event.target.files[0]; await uploadToS3(file); }; return ( <div> <input onChange={handleFileChange} type="file" /> <div className="pt-8"> {files.map((file, index) => ( <div key={index}> File #{index} progress: {file.progress}% </div> ))} </div> </div> ); }Configure environment variables for testing
masterBefore running tests, you must configure an S3 bucket and create a
.env.localfile inpackages/docs-site/with the required environment variables. It is recommended to use a dedicated S3 bucket for testing as the suite performs multiple uploads.# packages/docs-site/.env.local S3_UPLOAD_KEY=XXXXX S3_UPLOAD_SECRET=XXXXX S3_UPLOAD_BUCKET=XXXXX S3_UPLOAD_REGION=XXXXXCustomize S3 upload keys
masterBy default, the addon generates a unique key for every upload. To customize the S3 object key (the file path), use
APIRoute.configureto provide akeyfunction in your API route. Thekeyfunction receives the request object (req) and the originalfilename. It can be synchronous or asynchronous (returning a Promise).// pages/api/s3-upload.js import { APIRoute } from "next-s3-upload"; export default APIRoute.configure({ key(req, filename) { return `my/uploads/path/${filename}`; } });Run the Cypress test suite
masterThe test suite uses Cypress and is located in
packages/docs-site/cypress. To run the tests, you must have both the development build server (yarn dev) and the documentation site (yarn docs) running in separate terminals.# Terminal 1: Start build server yarn dev # Terminal 2: Start docs site yarn docs # Terminal 3: Run tests yarn workspace docs-site cypress open