S3 backend for remix/file-storage. Use this package when you want the FileStorage API backed by AWS S3 or an S3-compatible provider.
Features
- S3-Compatible API - Works with AWS S3 and S3-compatible APIs (MinIO, LocalStack, etc.)
- Metadata Preservation - Preserves
File metadata (name, type, lastModified)
- Runtime-Agnostic Signing - Uses
aws4fetch for SigV4 signing
Installation
API Reference
createS3FileStorage
Create an S3-backed file storage instance.
options
S3FileStorageOptions
required
Configuration options for S3 storage
S3FileStorageOptions
AWS region (e.g., us-east-1)
Custom endpoint URL for S3-compatible providers (e.g., MinIO, LocalStack)
Use path-style URLs instead of virtual-hosted-style. Required for some S3-compatible providers.
Custom fetch implementation. Defaults to global fetch.
Returns
A FileStorage instance backed by S3
Usage Examples
AWS S3
Connect to AWS S3:
import { createS3FileStorage } from 'remix/file-storage-s3'
let storage = createS3FileStorage({
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
bucket: 'my-app-uploads',
region: 'us-east-1',
})
// Store a file
await storage.set(
'uploads/hello.txt',
new File(['hello world'], 'hello.txt', { type: 'text/plain' })
)
// Retrieve a file
let file = await storage.get('uploads/hello.txt')
console.log(await file?.text())
// Remove a file
await storage.remove('uploads/hello.txt')
MinIO
Connect to MinIO or other S3-compatible providers:
import { createS3FileStorage } from 'remix/file-storage-s3'
let storage = createS3FileStorage({
accessKeyId: 'minioadmin',
secretAccessKey: 'minioadmin',
bucket: 'my-bucket',
region: 'us-east-1',
endpoint: 'http://localhost:9000',
forcePathStyle: true, // Required for MinIO
})
LocalStack
Connect to LocalStack for local development:
import { createS3FileStorage } from 'remix/file-storage-s3'
let storage = createS3FileStorage({
accessKeyId: 'test',
secretAccessKey: 'test',
bucket: 'test-bucket',
region: 'us-east-1',
endpoint: 'http://localhost:4566',
forcePathStyle: true,
})
File Upload Handling
Integrate with form data parser for file uploads:
import { createS3FileStorage } from 'remix/file-storage-s3'
import { parseFormData } from 'remix/form-data-parser'
import { createRouter } from 'remix/fetch-router'
let storage = createS3FileStorage({
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
bucket: 'uploads',
region: 'us-east-1',
})
let router = createRouter()
router.post('/upload', async ({ request }) => {
let formData = await parseFormData(request, {
uploadHandler: async (fileUpload) => {
// Stream file directly to S3
let key = `uploads/${Date.now()}-${fileUpload.name}`
await storage.put(key, fileUpload)
return key
},
})
let fileKey = formData.get('file')
return Response.json({ fileKey })
})
List Files
List files in a directory:
import { createS3FileStorage } from 'remix/file-storage-s3'
let storage = createS3FileStorage({ /* ... */ })
// List all files with a prefix
let result = await storage.list({ prefix: 'uploads/' })
for (let file of result.files) {
console.log(file.key, file.size, file.lastModified)
}
// Paginate through results
if (result.cursor) {
let nextPage = await storage.list({
prefix: 'uploads/',
cursor: result.cursor,
})
}
Environment Variables
Store credentials in environment variables:
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
S3_BUCKET=my-app-uploads
S3_REGION=us-east-1
Best Practices
- Store credentials in environment variables, never in code
- Use IAM roles when running on AWS infrastructure
- Set appropriate bucket policies and CORS configuration
- Use separate buckets for different environments (dev, staging, production)
- Implement file size limits to prevent abuse
- Consider using presigned URLs for large file uploads
File Storage
Core FileStorage interface and API
Form Data Parser
Parse file uploads from forms