Uploading Images in Flask and React

by Jose Javier Soto, Software Developer

Uploading Images in Flask and React

Uploading images in web applications is a common requirement.

In this post, we’ll guide you through the process of uploading photos to a Flask API, storing them on the server, returning a public URL, and displaying the photo immediately in a React frontend. We’ll use Flask with Flask-RESTful on the backend and Vite + React on the frontend.

This post assumes knowledge of Python, Flask, SQLAlchemy, and React.js.

Backend Setup (Flask)

1. Configure Flask to Handle Uploads

import os
from werkzeug.utils import secure_filename

UPLOAD_FOLDER = os.path.join(os.path.dirname(__file__), 'static', 'uploads')
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
os.makedirs(UPLOAD_FOLDER, exist_ok=True)

app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

2. Create the Upload Endpoint

from flask import request, jsonify, url_for
from flask_restful import Resource
import uuid

class UploadPhoto(Resource):
    def post(self):
        if 'photo' not in request.files:
            return {"error": "No file part"}, 400
        file = request.files['photo']

        master_car_record_id = request.form.get("master_car_record_id")
        if not master_car_record_id:
            return {"error": "Missing master_car_record_id"}, 400

        if file.filename == '':
            return {"error": "No selected file"}, 400

        if file and allowed_file(file.filename):
            filename = f"{uuid.uuid4().hex}_{secure_filename(file.filename)}"
            filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
            file.save(filepath)

            record = MasterCarRecord.query.get(master_car_record_id)
            if not record:
                return {"error": "MasterCarRecord not found"}, 404

            photo = CarPhoto(
                url=f"/static/uploads/{filename}",
                master_car_record_id=master_car_record_id
            )
            db.session.add(photo)
            db.session.commit()

            return {
                "message": "Photo uploaded",
                "url": url_for('serve_uploaded_file', filename=filename, _external=True)
            }, 201

        return {"error": "File type not allowed"}, 400

Add the route:

api.add_resource(UploadPhoto, '/api/upload_photo', endpoint='upload_photo')

3. Serve Uploaded Files

from flask import send_from_directory

@app.route('/static/uploads/<filename>')
def serve_uploaded_file(filename):
    return send_from_directory(app.config['UPLOAD_FOLDER'], filename)

Frontend Setup (React with Vite)

1. Configure Vite to Proxy to Flask

In vite.config.js:

export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:5555',
        changeOrigin: true,
        secure: false,
      },
    },
  },
})

2. React Component to Upload and Display Image

import React, { useState } from "react";

export default function PhotoUploader() {
  const [file, setFile] = useState(null);
  const [message, setMessage] = useState("");
  const [photoUrl, setPhotoUrl] = useState("");

  const handleUpload = async (e) => {
    e.preventDefault();
    if (!file) return;

    const formData = new FormData();
    formData.append("photo", file);
    formData.append("master_car_record_id", 1); // replace with actual ID

    const response = await fetch("/api/upload_photo", {
      method: "POST",
      body: formData,
    });

    if (response.ok) {
      const data = await response.json();
      setMessage("Photo uploaded successfully!");
      setPhotoUrl(data.url);
    } else {
      setMessage("Upload failed.");
      setPhotoUrl("");
    }
  };


  return (
    <form onSubmit={handleUpload}>
      <input type="file" onChange={(e) => setFile(e.target.files[0])} />
      <button type="submit">Upload</button>
      {message && <p>{message}</p>}
      {photoUrl && <img src={photoUrl} alt="Uploaded" style={{ maxWidth: "300px" }} />}
    </form>
  );
}

Example SQLAlchemy Model

class CarPhoto(db.Model, SerializerMixin):
    __tablename__ = 'car_photos'

    serialize_rules = ('-car_inventory', '-master_car_record',)

    id = db.Column(db.Integer, primary_key=True)
    url = db.Column(db.String, nullable=False)

    car_inventory_id = db.Column(db.Integer, db.ForeignKey('car_inventories.id'), nullable=True)
    car_inventory = relationship('CarInventory', backref=backref('photos', cascade='all, delete-orphan'))

    master_car_record_id = db.Column(db.Integer, db.ForeignKey('master_car_records.id'), nullable=True)
    master_car_record = relationship('MasterCarRecord', backref=backref('photos', cascade='all, delete-orphan'))

Run the Frontend

npm run dev

Start the Flask Server

pipenv shell
flask run --port=5555

Conclusion

With just a few lines of code on both the Flask backend and the React frontend, you’ve now built a simple yet functional photo upload and display system. This setup allows users to upload images, stores them securely on the server, and provides immediate visual feedback by rendering the uploaded image in the browser. From here, you can expand the system with features like image previews, validations, user authentication, or cloud storage integration.

More articles

Applying Biostatistics to Software Development: A Data-Driven Mindset

Explore how statistical techniques from biostatistics can guide software development decisions, testing, and feature design.

Read more

From React to NEXT.JS: A Quick Start Guide

If you’re comfortable with React and are looking to get started with Next.js, this post will help you transition quickly by walking through key concepts like project setup, components, routing, and dynamic routes.

Read more

Tell us about your project