---
title: Switch to uploadthing
description: How to change the default storage provider to uploadthing.
---

[uploadthing](https://uploadthing.com) is a platform for storing files in the cloud. It's a great alternative to AWS S3 and it's free for small projects. Here's how to switch the default storage provider to uploadthing.

## 1. Swap out the required dependencies

First, uninstall the existing dependencies from the Storage package...

```sh Terminal
pnpm remove @vercel/blob --filter @repo/storage
```

... and install the new dependencies...

```sh Terminal
pnpm add uploadthing @uploadthing/react --filter @repo/storage
```

## 2. Update the environment variables

Next, update the environment variables across the project, for example:

```js apps/app/.env
// Remove this:
BLOB_READ_WRITE_TOKEN=""

// Add this:
UPLOADTHING_TOKEN=""
```

Additionally, replace all instances of `BLOB_READ_WRITE_TOKEN` with `UPLOADTHING_TOKEN` in the `packages/env/index.ts` file.

## 3. Update the existing storage files

Update the `index.ts` and `client.ts` to use the new `uploadthing` packages:

<CodeGroup>

```ts packages/storage/index.ts
import { createUploadthing } from 'uploadthing/next';

export { type FileRouter, createRouteHandler } from 'uploadthing/next';
export { UploadThingError as UploadError, extractRouterConfig } from 'uploadthing/server';

export const storage = createUploadthing();
```

```ts packages/storage/client.ts
export * from '@uploadthing/react';
```

</CodeGroup>

## 4. Create new files for the storage client

We'll also need to create a couple of new files for the storage package to handle the Tailwind CSS classes and SSR.

<CodeGroup>

```ts packages/storage/ssr.ts
export { NextSSRPlugin as StorageSSRPlugin } from '@uploadthing/react/next-ssr-plugin';
```

```ts packages/storage/tailwind.ts
export { withUt as withStorage } from 'uploadthing/tw';
```

</CodeGroup>

## 5. Create a file router in your app

Create a new file in your app's `lib` directory to define the file router. This file will be used to define the file routes for your app, using your [Auth](/packages/authentication) package to get the current user.

```ts apps/app/app/lib/upload.ts
import { currentUser } from '@repo/auth/server';
import { type FileRouter, UploadError, storage } from '@repo/storage';
  
export const router: FileRouter = {
  imageUploader: storage({
    image: {
      maxFileSize: '4MB',
      maxFileCount: 1,
    },
  })
    .middleware(async () => {
      const user = await currentUser();

      if (!user) {
        throw new UploadError('Unauthorized');
      }

      return { userId: user.id };
    })
    .onUploadComplete(({ metadata, file }) => ({ uploadedBy: metadata.userId }),
};
```

## 6. Create a route handler

Create a new route handler in your app's `api` directory to handle the file routes.

```ts apps/app/app/api/upload/route.ts
import { router } from '@/app/lib/upload';
import { createRouteHandler } from '@repo/storage';

export const { GET, POST } = createRouteHandler({ router });
```

## 7. Update your root layout

Update your root layout to include the `StorageSSRPlugin`. This will add SSR hydration and avoid a loading state on your upload button.

```tsx apps/app/app/layout.tsx {4,5,7,16}
import '@repo/design-system/styles/globals.css';
import { DesignSystemProvider } from '@repo/design-system';
import { fonts } from '@repo/design-system/lib/fonts';
import { extractRouterConfig } from '@repo/storage';
import { StorageSSRPlugin } from '@repo/storage/ssr';
import type { ReactNode } from 'react';
import { router } from './lib/upload';

type RootLayoutProperties = {
  readonly children: ReactNode;
};

const RootLayout = ({ children }: RootLayoutProperties) => (
  <html lang="en" className={fonts} suppressHydrationWarning>
    <body>
      <StorageSSRPlugin routerConfig={extractRouterConfig(router)} />
      <DesignSystemProvider>{children}</DesignSystemProvider>
    </body>
  </html>
);

export default RootLayout;
```

## 8. Update your app's Tailwind config

Update your app's Tailwind config to include the `withStorage` function. This will add the necessary classes to your upload button.

```ts apps/app/tailwind.config.ts
import { withUt } from '@repo/storage/tailwind';
import { config } from '@repo/tailwind-config/config';

export default withUt(config) as typeof config;
```

## 9. Create your upload button

Create a new component for your upload button. This will use the `generateUploadButton` function to create a button that will upload files to the `imageUploader` endpoint.

```tsx apps/app/app/(authenticated)/components/upload-button.tsx
'use client';

import type { router } from '@/app/lib/upload';
import { generateUploadButton } from '@repo/storage/client';
import { toast } from 'sonner';

const UploadButton = generateUploadButton<typeof router>();

export const UploadForm = () => (
  <UploadButton
    endpoint="imageUploader"
    onClientUploadComplete={(res) => {
      // Do something with the response
      console.log('Files: ', res);
      toast.success('Upload Completed');
    }}
    onUploadError={(error: Error) => {
      toast.error(`ERROR! ${error.message}`);
    }}
  />
);
```

Now you can import this component into your app and use it as a regular component.

## 10. Advanced configuration

uploadthing is a powerful platform that offers a lot of advanced configuration options. You can learn more about them in the [uploadthing documentation](https://docs.uploadthing.com/).

<Card title="File Routes" icon="route" href="https://docs.uploadthing.com/file-routes" horizontal>
  Learn how to define file routes
</Card>

<Card title="Security" icon="shield" href="https://docs.uploadthing.com/concepts/auth-security" horizontal>
  How to protect different parts of the UploadThing flow.
</Card>
