A flexible and reusable file upload library with support for S3 and filesystem storage.
- 📤 Direct client-side uploads to S3
- 💾 Local filesystem support
- 🔒 Secure server-side credential handling
- 📊 Upload progress tracking
- 🔄 Automatic URL refreshing
- ⚛️ React components and hooks
- 📱 Next.js integration
- 🔌 Provider-based architecture
- 📂 File management utilities
- 📑 Intuitive file listing with icons
- 🎨 Enhanced FileList component with file type icons - Beautiful visual indicators for different file types
- 🖼️ Option to display file icons instead of image previews - More consistent UI and better performance
- 🗂️ File management utilities - Easy-to-use functions for listing, fetching, and deleting files
- 🔄 Improved filesystem integration - Better fallbacks for direct filesystem operations
npm install @gw-intech/upload-it
# or
yarn add @gw-intech/upload-it
# or
pnpm add @gw-intech/upload-it
The library is designed to work in both browser and Node.js environments:
-
S3 Provider: Fully supported in browsers
-
Filesystem Provider: Only for server-side use (Node.js)
When using the library in client-side applications (like Vite, Create React App, etc.), you should:
- Use the S3 provider for direct client-side uploads
- Or set up server endpoints for secure uploads
// ✅ Works in browsers
import { UploadButton } from '@gw-intech/upload-it';
function App() {
return (
<UploadButton
provider='s3'
s3={{
region: 'us-east-1',
bucket: 'my-bucket',
credentials: {
// For development only! Use server mode in production
accessKeyId: '...',
secretAccessKey: '...',
},
}}
/>
);
}
// ❌ Won't work in browsers - filesystem requires Node.js
import { UploadButton } from '@gw-intech/upload-it';
function App() {
return (
<UploadButton
provider='filesystem' // ⚠️ This won't work in browsers
filesystem={{
uploadDir: './public/uploads',
publicPath: '/uploads',
}}
/>
);
}
Basic Usage with React Component
import { UploadButton } from '@gw-intech/upload-it';
function MyApp() {
return (
<UploadButton
provider='s3'
s3={{
region: 'us-east-1',
bucket: 'my-bucket',
credentials: {
/* For development only! See secure usage below */
},
}}
onUploadComplete={(files) => console.log('Uploaded files:', files)}
multiple={true}
accept='image/*,.pdf'
/>
);
}
Secure Usage with Next.js
When using Next.js, you'll need to import the server-specific routes from the /server
subpath:
- Create API routes for uploads:
// app/api/upload/presigned/route.ts
import { presignedUrlRoute } from '@gw-intech/upload-it/server';
export { presignedUrlRoute as POST };
// app/api/upload/complete/route.ts
import { completeUploadRoute } from '@gw-intech/upload-it/server';
export { completeUploadRoute as POST };
// app/api/upload/url/[fileKey]/route.ts
import { getFileUrlRoute } from '@gw-intech/upload-it/server';
export { getFileUrlRoute as GET };
- Configure environment variables:
UPLOAD_IT_S3_REGION=us-east-1
UPLOAD_IT_S3_BUCKET=my-bucket
UPLOAD_IT_S3_ACCESS_KEY=your-access-key
UPLOAD_IT_S3_SECRET_KEY=your-secret-key
UPLOAD_IT_S3_FOLDER=uploads
- Use the component with server mode:
import { UploadButton } from '@gw-intech/upload-it';
function MyApp() {
return (
<UploadButton
provider='s3'
server={{
mode: 'server',
endpoints: {
getUploadUrl: '/api/upload/presigned',
completeUpload: '/api/upload/complete',
getAccessUrl: '/api/upload/url',
},
}}
onUploadComplete={(files) => console.log('Uploaded files:', files)}
/>
);
}
Using with Vite or non-Next.js environments
When using Vite or other build systems, you only need to import from the main package:
import {
UploadButton,
useUploader,
createUploader,
} from '@gw-intech/upload-it';
The Next.js specific routes are isolated in the /server
subpath to avoid dependency conflicts.
import { useUploader } from '@gw-intech/upload-it';
import { useState } from 'react';
function MyUploader() {
const [files, setFiles] = useState([]);
const uploader = useUploader({
provider: 's3',
s3: {
region: 'us-east-1',
bucket: 'my-bucket',
credentials: {
/* ... */
},
},
onSuccess: (uploadedFiles) => {
setFiles(uploadedFiles);
},
});
const handleFileChange = async (e) => {
const selectedFiles = e.target.files;
if (selectedFiles.length > 0) {
await uploader.uploadFiles(Array.from(selectedFiles));
}
};
return (
<div>
<input type='file' multiple onChange={handleFileChange} />
{uploader.isUploading && (
<div>
Uploading... {Object.values(uploader.progress)[0]?.percentage}%
</div>
)}
{files.map((file) => (
<div key={file.key}>
{file.name} -{' '}
<a href={file.url} target='_blank' rel='noopener noreferrer'>
View
</a>
</div>
))}
</div>
);
}
import { UploadButton } from '@gw-intech/upload-it';
function MyApp() {
return (
<UploadButton
provider='filesystem'
filesystem={{
uploadDir: './public/uploads',
publicPath: '/uploads',
}}
onUploadComplete={(files) => console.log('Uploaded files:', files)}
/>
);
}
import { createUploader } from '@gw-intech/upload-it';
const uploader = createUploader({
provider: 's3',
s3: {
region: 'us-east-1',
bucket: 'my-bucket',
credentials: {
/* ... */
},
},
onUploadProgress: (file, progress) => {
console.log(`${file.name}: ${progress.percentage}%`);
},
});
async function uploadFile(file) {
try {
const result = await uploader.uploadFile(file);
console.log('File uploaded:', result);
return result;
} catch (error) {
console.error('Upload failed:', error);
}
}
The UploadButton
component accepts the following props:
Prop |
Type |
Required |
Description |
provider |
's3' | 'filesystem' |
Yes |
Storage provider to use |
s3 |
S3Config |
Only with s3 provider |
S3 configuration |
filesystem |
FilesystemConfig |
Only with filesystem provider |
Filesystem configuration |
server |
ServerConfig |
No |
Server configuration for secure mode |
maxFileSize |
number |
No |
Maximum file size in bytes |
allowedFileTypes |
string[] |
No |
Allowed file types (e.g., ['image/*', '.pdf']) |
Prop |
Type |
Required |
Default |
Description |
className |
string |
No |
'' |
CSS class applied to the button |
buttonText |
string |
No |
'Upload' |
Text displayed on the button |
children |
React.ReactNode |
No |
null |
Content to render inside the button |
showProgressDialog |
boolean |
No |
true |
Whether to show the progress dialog |
dialogTitle |
string |
No |
'Upload File' |
Title for the progress dialog |
Prop |
Type |
Required |
Default |
Description |
multiple |
boolean |
No |
false |
Allow multiple file selection |
accept |
string |
No |
undefined |
File type filter (e.g., "image/*,.pdf") |
folder |
string |
No |
undefined |
Optional folder path for uploaded files |
Prop |
Type |
Required |
Description |
onSuccess |
(files: FileMetadata[]) => void |
No |
Called when uploads complete successfully |
onError |
(error: Error) => void |
No |
Called when an error occurs |
Prop |
Type |
Required |
Description |
renderButton |
(props: { onClick: () => void; isUploading: boolean }) => React.ReactNode |
No |
Custom button rendering |
renderDialog |
(props: { isOpen: boolean; onClose: () => void; progress: number; isComplete: boolean; hasErrors: boolean; }) => React.ReactNode |
No |
Custom dialog rendering |
Option |
Type |
Description |
provider |
's3' | 'filesystem' |
Storage provider to use |
maxFileSize |
number |
Maximum file size in bytes |
allowedFileTypes |
string[] |
Allowed file types (e.g., ['image/*', '.pdf'] ) |
Option |
Type |
Description |
region |
string |
AWS region |
bucket |
string |
S3 bucket name |
credentials |
{ accessKeyId: string, secretAccessKey: string } |
AWS credentials |
endpoint |
string |
Custom endpoint (for S3-compatible storage) |
uploadFolder |
string |
Folder prefix for uploaded files |
Filesystem Provider Options
Option |
Type |
Description |
uploadDir |
string |
Directory to store uploaded files |
publicPath |
string |
Public URL path to access files |
createDirIfNotExist |
boolean |
Create directory if it doesn't exist |
Option |
Type |
Description |
mode |
'client' | 'server' |
Whether to use client or server mode |
endpoints |
{ getUploadUrl?: string, completeUpload?: string, getAccessUrl?: string } |
API endpoints for server operations |
MIT