---
title: How can I use AWS S3 with Vercel?
description: Example how to use AWS S3 library on Vercel
url: /kb/guide/how-can-i-use-aws-s3-with-vercel
canonical_url: "https://vercel.com/kb/guide/how-can-i-use-aws-s3-with-vercel"
published: 2025-11-03
last_updated: 2026-07-16
authors: Lee Robinson
related:
  - /docs/vercel-blob
install_vercel_plugin: npx plugins add vercel/vercel-plugin
---
<!-- docsgraph:related -->
## Related pages

> **For AI agents:** Follow these links to understand how this page connects to the rest of the Vercel ecosystem. For the full cross-link map (inbound, outbound, prerequisites, and semantic neighbors), see the .graph.md link below.

- [Client Uploads](https://vercel.com/docs/vercel-blob/client-upload?from=related) — Learn how to upload files larger than 4.5 MB directly from the browser to Vercel Blob
- [Server Uploads](https://vercel.com/docs/vercel-blob/server-upload?from=related) — Learn how to upload files to Vercel Blob using Server Actions and Route Handlers
- [Examples](https://vercel.com/docs/vercel-blob/examples?from=related) — Examples on how to use Vercel Blob in your applications
- [AWS](https://vercel.com/docs/oidc/aws?from=related) — Learn how to configure your AWS account to trust Vercel's OpenID Connect \(OIDC\) Identity Provider \(IdP\).
- [Public Storage](https://vercel.com/docs/vercel-blob/public-storage?from=related) — Learn how to use public Vercel Blob storage to serve files accessible to anyone with the URL
- [How do I bypass the 4.5MB body size limit of Vercel Serverless Functions?](https://vercel.com/kb/guide/how-to-bypass-vercel-body-size-limit-serverless-functions?from=related) — Learn how to deal with the body size limit of Serverless Functions on Vercel.
- [Build with Vercel Blob on Next.js](https://vercel.com/kb/guide/vercel-blob-nextjs?from=related) — Deploy the Vercel Blob Next.js Starter and learn how client uploads store images securely in a private Blob store.
- [How to upload and store files with Vercel](https://vercel.com/kb/guide/how-to-upload-and-store-files-with-vercel?from=related) — Vercel file uploads done right cover Server Actions, client-direct upload, and multipart for 5 TB files, with auth and c
- [What are the best practices for hosting videos on Vercel?](https://vercel.com/kb/guide/best-practices-for-hosting-videos-on-vercel-nextjs-mp4-gif?from=related) — Learn the ideal solutions for using video files like .mp4 and .gif on Vercel to prevent excess bandwidth consumption.
- [How to process user-uploaded files with Vercel Sandbox and Vercel Blob](https://vercel.com/kb/guide/user-uploaded-files-vercel-sandbox-and-blob?from=related) — Learn how to upload files to Vercel Blob, process them safely with FFmpeg in an isolated Vercel Sandbox, and store the r

Full cross-link map for this page: [/kb/guide/how-can-i-use-aws-s3-with-vercel.graph.md](/kb/guide/how-can-i-use-aws-s3-with-vercel.graph.md)
<!-- /docsgraph:related -->


[Amazon Web Services (AWS) S3](https://aws.amazon.com/s3/) is a popular way to upload and store files. You can use AWS S3 together with Vercel to upload, store, and retrieve objects.

## Vercel Blob

Vercel Blob provides globally-distributed file storage for web applications, deeply integrated with Vercel's infrastructure. Blob is powered by AWS's S3 infrastructure, ensuring 99.999999999% of reliability. Your files are always secure and available without additional complexity.

```tsx
import { put } from '@vercel/blob';
 
export async function PUT(request: Request) {
  const form = await request.formData();
  const file = form.get('file') as File;
  const blob = await put(file.name, file, {
    access: 'public',
    addRandomSuffix: true,
  });
 
  return Response.json(blob);
}
```

Blob automatically leverages Vercel's global caching and data transfer, delivering your content quickly and cost-effectively. With built-in support for large files (up to 5TB), multi-part uploads, resumability, and concurrency, Blob handles even the most demanding media workloads, letting you focus on building great product experiences.

Pricing is transparent and usage-based, closely aligned with Amazon S3 but optimized further by Blob’s integration into Vercel’s delivery network. Get started with [Vercel Blob](https://vercel.com/docs/vercel-blob).

## With an Existing S3 Bucket

If you already have an existing AWS S3 bucket configured, you can retrieve (or generate) the access key and secret key for that [IAM User](https://aws.amazon.com/iam/). Ensure it has access to the proper policies: `s3:DeleteObject`, `s3:GetObject`, `s3:ListBucket`, `s3:PutObject`, `s3:PutObjectAcl.`

AWS credentials (e.g. `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`) and region configuration (e.g. `AWS_REGION`) can now be used directly as [Environment Variables](https://vercel.com/guides/how-can-i-use-aws-sdk-environment-variables-on-vercel) for Vercel deployments. Ensure these values are inside your Vercel project.

These variables are the default names expected by the AWS SDK, which means the user no longer has to configure credentials when using it. For example:

```javascript
import { S3Client } from '@aws-sdk/client-s3'

// AWS credentials are picked up from the environment
const client = new S3Client({ region: process.env.AWS_REGION });
```

## Creating a New S3 Bucket

We’ve created [an example Next.js application](https://vercel.com/templates/next.js/aws-s3-image-upload-nextjs) that allows you to upload photos to an S3 bucket.

1. In Object Ownership, select "ACLs enabled" and "Bucket owner prefered"
   
2. In Block Public Access settings for this bucket, uncheck "Block all public access".
   

### Create a new IAM User

1. Create a new [IAM user](https://aws.amazon.com/iam/)
   
2. Choose programmatic access
   
3. Select "Attach policies directly"
   
4. Add `s3:DeleteObject`, `s3:GetObject`, `s3:ListBucket`, `s3:PutObject`, `s3:PutObjectAcl.`
   

### Save the access key and secret key for the IAM User

1. Select the newly created user (IAM > Users > "your-user") and navigate to "Security Credentials".
   
2. Under "Access Keys", create a key and save this information. We will use this in the next step.
   

### Create an `.env.local` file similar to `.env.example`

1. In your `env.local` file, use the information from your access key, along with the region and bucket name.
   
2. Do not adjust the naming of the keys, only input your values. [This is to ensure S3 Client can read them as defaults](https://docs.aws.amazon.com/sdkref/latest/guide/settings-reference.html).
   

### Configure CORS to enable uploads from your browser

1. Navigate to your bucket, and go to the "Permissions" tab.
   
2. Scroll down to find "Cross-origin resource sharing (CORS)" and click "Edit" on the right side.
   
3. Paste the following code below.
   

```json
[
 {
     "AllowedHeaders": [
         "*"
     ],
     "AllowedMethods": [
         "GET",
         "PUT",
         "POST",
         "DELETE"
     ],
     "AllowedOrigins": [
         "*"
     ],
     "ExposeHeaders": []
 }
]
```

### Test

1. Start our Next.js app at `localhost:3000`
   
2. Choose a `.png` or `.jpg` file
   
3. You should see your file successfully uploaded to S3
   

## Uploading files on the server

You can use Vercel Serverless Functions to upload files to AWS S3 on the server. After creating an instance of the AWS S3 client, you can use `upload` to create a new object with the given file name and body (based on a stream).

```javascript
import { createReadStream } from 'fs';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';

export async function POST(request: Request) {
  try {
    const client = new S3Client({ region: process.env.AWS_REGION })
    const uploadCommand = new PutObjectCommand({
      Bucket: process.env.AWS_BUCKET_NAME,
      Key: 'file-name',
      Body: createReadStream('file-path'),
    });
  
    const response = await client.send(uploadCommand);
    return Response.json(response)
  } catch (error) {
    return Response.json({ error: error.message })
  }
}
```

## Uploading files in the browser

Alternatively, you can allow file uploads directly from the browser. For example, a user can select a file using an `input`, which then generates a pre-signed POST from a Route Handler to allow for secure uploads.

```tsx
'use client'

import { useState } from 'react'

export default function Page() {
  const [file, setFile] = useState<File | null>(null)
  const [uploading, setUploading] = useState(false)

  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault()

    if (!file) {
      alert('Please select a file to upload.')
      return
    }

    setUploading(true)

    const response = await fetch(
      process.env.NEXT_PUBLIC_BASE_URL + '/api/upload',
      {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ filename: file.name, contentType: file.type }),
      }
    )

    if (response.ok) {
      const { url, fields } = await response.json()

      const formData = new FormData()
      Object.entries(fields).forEach(([key, value]) => {
        formData.append(key, value as string)
      })
      formData.append('file', file)

      const uploadResponse = await fetch(url, {
        method: 'POST',
        body: formData,
      })

      if (uploadResponse.ok) {
        alert('Upload successful!')
      } else {
        console.error('S3 Upload Error:', uploadResponse)
        alert('Upload failed.')
      }
    } else {
      alert('Failed to get pre-signed URL.')
    }

    setUploading(false)
  }

  return (
    <main>
      <h1>Upload a File to S3</h1>
      <form onSubmit={handleSubmit}>
        <input
          id="file"
          type="file"
          onChange={(e) => {
            const files = e.target.files
            if (files) {
              setFile(files[0])
            }
          }}
          accept="image/png, image/jpeg"
        />
        <button type="submit" disabled={uploading}>
          Upload
        </button>
      </form>
    </main>
  )
}
```

The API Route to generate the pre-signed POST is as follows:

```tsx
import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
import { S3Client } from '@aws-sdk/client-s3'
import { v4 as uuidv4 } from 'uuid'

export async function POST(request: Request) {
  const { filename, contentType } = await request.json()

  try {
    const client = new S3Client({ region: process.env.AWS_REGION })
    const { url, fields } = await createPresignedPost(client, {
      Bucket: process.env.AWS_BUCKET_NAME,
      Key: uuidv4(),
      Conditions: [
        ['content-length-range', 0, 10485760], // up to 10 MB
        ['starts-with', '$Content-Type', contentType],
      ],
      Fields: {
        acl: 'public-read',
        'Content-Type': contentType,
      },
      Expires: 600, // Seconds before the presigned post expires. 3600 by default.
    })

    return Response.json({ url, fields })
  } catch (error) {
    return Response.json({ error: error.message })
  }
}
```

If you need to support pausing and resuming uploads, as well as other advanced file upload strategies, explore libraries like [Evaporate](https://github.com/TTLabs/EvaporateJS).