How do I add signed uploads to an existing Next.js app?
To implement secure file uploads in Next.js with Uploadcare, use the Uploadcare signed uploads packages that come with HMAC-SHA256 authentication.
This requires the @uploadcare/react-uploader and @uploadcare/signed-uploads packages.
The full implementation takes 4 steps.
Install Uploadcare in Next.js
Install the React uploader component (this provides the UI widget) and the signed uploads utility (generates cryptographic signatures):
npm install @uploadcare/react-uploader @uploadcare/signed-uploadsSet your environment variables in .env.local:
NEXT_PUBLIC_UPLOADCARE_PUBLIC_KEY=your_public_key_here
UPLOADCARE_SECRET_KEY=your_secret_key_hereYou can find these keys in your Uploadcare dashboard under “API Keys”. The public key is used in the client uploader, while the secret key is only used on the server to generate signatures.
Basic file upload with a public key
Start with the simplest working example using FileUploaderRegular. Import from the /next path for SSR safety:
'use client';
import { FileUploaderRegular } from '@uploadcare/react-uploader/next';
import '@uploadcare/react-uploader/core.css';
export default function BasicUploader() {
return (
<FileUploaderRegular
pubkey={process.env.NEXT_PUBLIC_UPLOADCARE_PUBLIC_KEY as string}
onFileUploadSuccess={file => {
console.log('File uploaded:', file);
}}
/>
);
}This approach works immediately, but note that the public key alone allows any client to upload files since there’s no server-side validation.
Production-ready signed uploads
For production, implement server-signed uploads using HMAC-SHA256 authentication. This restricts uploads to requests with valid signatures.
First, create an API route that generates secure signatures:
// app/api/uploadcare-signature/route.ts
import { generateSecureSignature } from '@uploadcare/signed-uploads';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
try {
await request.json();
const { secureSignature, secureExpire } = generateSecureSignature(
process.env.UPLOADCARE_SECRET_KEY as string || '',
{
expire: Date.now() + 30 * 60 * 1000, // 30 minutes
},
);
return NextResponse.json({
signature: secureSignature,
expire: secureExpire,
});
} catch (error) {
console.error('Error generating signature:', error);
return NextResponse.json(
{ error: 'Failed to generate signature' },
{ status: 500 }
);
}
}Then update your client component to fetch and use this signature:
'use client';
import { FileUploaderRegular } from '@uploadcare/react-uploader/next';
import '@uploadcare/react-uploader/core.css';
import { useEffect, useState } from 'react';
export default function SecureUploader() {
const [secureSignature, setSecureSignature] = useState<{
signature: string;
expire: number;
} | null>(null);
useEffect(() => {
const fetchSignature = async () => {
const response = await fetch('/api/uploadcare-signature', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({}),
});
const data = await response.json();
setSecureSignature(data);
};
fetchSignature();
}, []);
if (!secureSignature) {
return <div>Loading...</div>;
}
return (
<FileUploaderRegular
pubkey={process.env.NEXT_PUBLIC_UPLOADCARE_PUBLIC_KEY as string}
secureSignature={secureSignature.signature}
secureExpire={secureSignature.expire}
onFileUploadSuccess={file => {
console.log('File uploaded securely:', file);
}}
/>
);
}Common errors and troubleshooting
-
Problem: SSR errors with “document is not defined”
Cause: Using the standard import path fails in server components
Fix: Use@uploadcare/react-uploader/nextand mark your component with'use client' -
Problem: Signature expires immediately
Cause: Short expiries cause “expired signature” errors before users finish uploading, especially for large files
Fix: Set expiry to at least 30 minutes in the future using a Unix timestamp in milliseconds (for example,Date.now() + 30 * 60 * 1000) -
Problem: Component mounts with no signature prop
Cause: The signature resolver must be provided synchronously
Fix: Fetch signatures before renderingFileUploaderRegular, or use a loading state
Next steps
Check out how to implement rate limiting on your signature endpoint and webhook validation to process uploads automatically. If you deploy to Vercel, the serverless signed uploads guide covers that pattern end to end. For background on why this matters, see a guide to secure file uploads.