Articles · GuideUpdated May 2026

How to add photo upload to your app.
The 2026 walkthrough.

A photo upload button looks like one tap. Behind it sit a storage bucket, a signed URL, a direct upload, an image picker, and a row in your database. This is how to add photo upload to your app without writing all of that by hand. You describe it, Newly generates the flow in real React Native and Expo, and the code is yours.

The four steps

The four steps, prompt to saved photo.

Here is how to add photo upload to your app from the moment you open the editor to the moment a photo shows up and stays put. You write the first step as a prompt. Newly generates the other three, which run on the device and the backend.

  1. 1

    Ask for the upload in plain language

    Open your Newly project and prompt the AI the way you would brief a teammate: "let users upload a profile photo," or "add a photo to each listing." No file names, endpoints, or buckets to specify. Newly reads the request, picks the screen the photo belongs on, and generates the upload flow along with the UI around it. Your only job in this step is saying what you want.

  2. 2

    Newly wires up the image picker

    On the device, choosing the photo is Expo's image picker. Newly generates a screen that opens the phone's native photo library when the user taps to add a picture, after asking for the photo-library permission both platforms require. The user picks an image, the app grabs a reference to that local file, and the bytes are ready to go up. Nothing has touched the network yet.

  3. 3

    The app requests a signed upload URL

    Now the backend does its one job. Rather than stream the photo through your server, the app asks Liquid Backend for a signed upload URL: a short-lived link that grants permission to drop one file into object storage. Because the endpoint already exists in your project, Newly only has to generate the client code that calls it and catches the URL that comes back.

  4. 4

    Upload straight to storage, then save the URL

    With the signed URL in hand, the app sends the image bytes straight to storage and never routes them back through your server. Storage returns a permanent URL for the stored file, and the generated code writes that URL into the database against the right record. Next time the screen loads, the app reads the URL and renders the photo. That is the loop, start to finish.

The key detail: the database only stores the URL

The file goes straight from the phone to storage; the database only ever holds its URL. That keeps uploads fast and the database small as more photos come in, rather than slowing down on a busy day.

Photo upload, at a glance.

1 prompt

Describe it once, "let users upload a profile photo," and the flow appears

3 steps

Sign a URL, upload direct to storage, save the URL in the database

Built in

File storage ships with Liquid Backend on every Newly project

Signed URLs

Uploads go straight to object storage, then the URL is saved

What it really is

What sits behind a photo upload button.

The reason how to add photo upload to your app trips people up is that the visible part, a button and then a photo on screen, hides four invisible ones. You need somewhere to store the file, a safe way to grant permission to upload it, a way to get the bytes off the phone, and a place to record where the file landed. Miss one and the button does nothing, or the photo vanishes on the next launch.

Two of those four parts are not yours to build on Newly. File storage is a built-in feature of Liquid Backend, the backend bundled with every project, which means the place to keep files and the signing that protects them already exist. So adding photo upload is mostly a matter of connecting a screen to plumbing that is already running. The other two parts, picking the image and recording its URL, are what the generated code wires together.

Generated vs by hand

What Newly generates vs. what you’d write.

A photo upload is a lot of small, fiddly pieces that all have to line up at once. Here they are side by side: what Newly generates from your prompt, and what you would write yourself by hand.

Newly generates it

  • A storage bucket, since file storage is built into Liquid Backend
  • The backend endpoint that signs short-lived upload URLs
  • Direct upload from the device straight to object storage
  • Expo's image picker wired into a real screen
  • Photo-library permission prompts on iOS and Android
  • Saving the file URL in the database so it displays later

By hand, you build all of this

  • Provision and configure a storage bucket yourself
  • Write and secure the URL-signing endpoint by hand
  • Handle direct uploads, including the ones that fail halfway
  • Install and wire Expo's image picker into the UI
  • Request and test photo permissions on both platforms
  • Keep the database in sync with what is actually in storage

Both columns end up with the same React Native code. The difference is how long it takes to get there.

Where Newly fits

How photo upload fits into Newly.

Newly is a paid AI mobile-app builder, from $25 a month. You describe the app in plain language and it generates a React Native and Expo codebase, photo upload included the moment you ask for it. So how to add photo upload to your app on Newly comes down to a single prompt, and the generated upload flow is code you can open and edit.

Prefer a different backend? Supabase is the one-click alternative, and Supabase Storage gives you finer access control over files when your app needs database-style rules on each one. Short of that, the signed-URL approach in Liquid Backend covers the usual photo upload, from avatars to listing images, and it is the one Newly uses unless you say otherwise.

How it works

The four ideas behind the upload.

You can ship without writing any of this by hand, but knowing it makes the generated code easier to read and change. Four ideas cover the whole thing.

1

Why a signed URL, not a server upload

Route every photo through your backend and the server holds the whole file in memory, pays for the bandwidth twice, and turns into the bottleneck the moment a few users upload at once. The signed-URL pattern hands that problem to storage. The backend issues a short-lived permission slip and steps aside while the bytes travel from phone to bucket. Your server stays small no matter how many photos move through it.

2

Where the photo actually lives

Uploaded files sit in S3-style object storage, sorted by path. The database never holds the image itself, which would bloat it within a week of real use. It holds the file's URL, a short string, beside whatever record the photo belongs to. To show the picture, you read the string and render it. The database stays lean and the images come off storage built for serving them.

3

Permissions are part of the flow

Forget the permission and the picker dies quietly on a real device, which is the kind of bug that passes every test on the simulator and breaks the day a user installs the app. The generated code requests photo-library access before the picker opens, on both iOS and Android. This is the piece hand-rolled uploads forget most often, so it is worth knowing it is already handled.

4

Editing the upload flow later

What Newly generates is React Native and Expo code in your project. Want to crop the image before it uploads, or point the saved URL at a different table? Edit the code directly, or ask the AI to do it. The upload flow is as open to changes as the rest of the screens in your project.

File access: Liquid Backend vs. Supabase.

Liquid Backend organizes files by path and serves them through signed URLs. It does not apply per-user row-level access policies to files. That kind of per-file rule is a Supabase Storage feature, so if your app has to enforce database-style access on every single file, make Supabase the backend and use it for that. For everything else, the signed-URL default works well.

FAQ

How to add photo upload to your app, in plain language.

You describe it and the AI builds it. Write a prompt like "let users upload a profile photo" and Newly generates the screen, the image picker, the upload, and the part that saves the photo so it survives the next launch. What you get back is standard React Native code, generated in minutes instead of a weekend. Newly is paid, from $25 a month, since the cloud builds and the AI cost money to run. The fiddly, easy-to-break parts are handled for you.

Add photo upload with Newly.

From $25 a month, you describe the feature and the whole upload comes back as code, picker and permissions and signed URLs and all. Liquid Backend provides the file storage, and you write the prompt.