File Uploads
As you've probably heard, Redwood thinks the future is serverless. This concept introduces some interesting problems you might not have had to worry about in the past. For example, where do files go when you upload them? There's no server! Like many tasks you may have done yourself in the past, this is another job that we can farm out to a third-party service.
The Service
There are many services out there that handle uploading files and serving them from a CDN. Two of the big ones are Cloudinary and Filestack. We're going to demo a Filestack integration here because we've found it easy to integrate. In addition to storing your uploads and making them available via a CDN, they also offer on-the-fly image transformations so that even if someone uploads a Retina-ready 5000px wide headshot, you can shrink it down and only serve a 100px version for their avatar in the upper right corner of your site. You save bandwidth and transfer costs.
We're going to sign up for a free plan which gives us 100 uploads a month, 1000 transformations (like resizing an image), 1GB of bandwidth, and 0.5GB of storage. That's more than enough for this demo. (And maybe even a low-traffic production site!)
Head over to https://dev.filestack.com/signup/free/ and sign up. Be sure to use a real email address because they're going to send you a confirmation email before they let you log in. Once you verify your email, you'll be dropped on your dashboard where your API key will be shown in the upper right:

Copy that (or at least keep the tab open) because we're going to need it in a minute. (I already changed that key so don't bother trying to steal it!)
That's it on the Filestack side; on to the application.
The App
Let's create a very simple DAM (Digital Asset Manager) that lets users upload and catalogue images. They'll be able to click the thumbnail to open a full-size version.
Create a new Redwood app:
yarn create redwood-app uploader
cd uploader
The first thing we'll do is create an environment variable to hold our Filestack API key. This is a best practice so that the key isn't living in our repository for prying eyes to see. Add the key to the .env file in the root of our app:
REDWOOD_ENV_FILESTACK_API_KEY=AM18i8xV4QpoiGwetoTWd
We're prefixing with
REDWOOD_ENV_here to tell Vite that we want it to replace this variables with its actual value as it's processing pages and statically generating them. Otherwise our generated pages would still contain something likeprocess.env.FILESTACK_API_KEY, which wouldn't exist when the pages are static and being served from a CDN.
Now we can start our development server:
yarn rw dev
The Database
We'll create a single model to store our image data:
```javascript title="api/db/schema.prisma" model Image { id Int @id @default(autoincrement()) title String url String }
`title` will be the user-supplied name for this asset and `url` will contain the public URL that Filestack creates after an upload.
Create a migration to update the database; when prompted, name it "add image":
```bash
yarn rw prisma migrate dev
To make our lives easier, let's scaffold the screens necessary to create/update/delete an image, then we'll worry about adding the uploader:
yarn rw generate scaffold image
Now head to http://localhost:8910/images/new and let's figure this out!

The Uploader
Filestack has a couple of React components that handle all the uploading for us. Let's add the package:
yarn workspace web add filestack-react
We want the uploader on our scaffolded form, so let's head over to ImageForm, import Filestack's inline picker, and try replacing the Url input with it:
```jsx {9,49} title="web/src/components/ImageForm/ImageForm.js" import { Form, FormError, FieldError, Label, TextField, Submit, } from '@redwoodjs/forms' import { PickerInline } from 'filestack-react'
const formatDatetime = (value) => { if (value) { return value.replace(/:\d{2}.\d{3}\w/, '') } }
const ImageForm = (props) => { const onSubmit = (data) => { props.onSave(data, props?.image?.id) }
return (