Getting Started
Developer Guides
Building a React Uploader

Build a React Uploader with imgix

Use React and the imgix sessions endpoint to build a user interface that allows users to upload images to your imgix account. This guide uses upload sessions to support large file uploads up to 5GB.

The tutorial's complete working code and demo are in this CodeSandbox (opens in a new tab).

Demo

The Session Status is: No Status

Prerequisites

Steps

Step 1: Setting up the React project

First, create a new React project using the following command:

npx create-react-app imgix-uploader
cd imgix-uploader

Next, install the necessary dependencies:

npm install axios react-imgix

Let's create a new component named ReactUploader.js in the src directory. This component will handle the entire image upload process, from selecting a file to sending it to imgix.

Once installed and configured, your directory should look something like this (though with more files):

    • package.json
  • Step 3: Building the initial elements ReactUploader.js component

    Let's start by creating the form elements in the ReactUploader.js component. These elements will serve as the user interface for uploading images.

    We will also be importing the necessary dependencies and setting up the initial state for the component.

    Note that the form we are building will require your imgix API key (opens in a new tab) and Source ID.

    import { useState, useEffect } from "react"
    import axios from "axios"
    import Imgix from "react-imgix"
     
    export default function ReactUploader() {
      return (
        <div>
          <form className="mb-4" onSubmit={handleSubmit}>
            {/* API Key */}
            <input
              type="text"
              placeholder="Your imgix API key"
              value={apiKey}
              onChange={(e) => setApiKey(e.target.value)}
            />
            {/* Source ID */}
            <input
              type="text"
              placeholder="Source ID"
              value={sourceId}
              onChange={(e) => setSourceId(e.target.value)}
            />
            {/* imgix domain name */}
            <input
              type="text"
              placeholder="your_subdomain.imgix.net"
              value={imgixDomain}
              onChange={(e) => setImgixDomain(e.target.value)}
            />
            {/* path to upload the image to */}
            <input
              type="text"
              placeholder="path/to/upload/to/"
              onBlur={(e) => {
                // parses the upload path to remove any trailing slashes
                let parsedPath = ""
                if (uploadPath.charAt(0) !== "/" && uploadPath.length > 1) {
                  parsedPath = "/" + uploadPath
                } else {
                  parsedPath = uploadPath
                }
                if (uploadPath.charAt(uploadPath.length - 1) !== "/") {
                  parsedPath += "/"
                }
                setUploadPath(parsedPath)
              }}
              value={uploadPath}
              onChange={(e) => setUploadPath(e.target.value)}
            />
            {/* Image Upload */}
            <input type="file" onChange={(e) => setImg(e.target.files[0])} />
            <button disabled={disableForm}>Upload Image</button>
          </form>
          {/* status information will go here */}
          <div>
            <h3>The Session Status is: {sessionStatus}</h3>
            <div>
              {sessionStatus === "ERROR" && <h3>{error}</h3>}
              {/* if the session is closed, show an imgix image */}
              {sessionStatus === "COMPLETE" && (
                <Imgix
                  src={imgixUrl}
                  width={200}
                  height={200}
                  imgixParams={{ auto: "format,compress,enhance", fit: "crop" }}
                />
              )}
            </div>
            {sessionStatus === "COMPLETE" && <h4>{imgixUrl}</h4>}
          </div>
        </div>
      )
    }

    Step 4: Implementing the Form Handler

    Now that we've set up the form let's implement the logic for handling form submission and sending the image to the imgix API.

    React State

    We will use React's state to manage the form data and the status of the upload session. For more information about React state, refer to the React documentation (opens in a new tab).

    export default function ReactUploader() {
      const [img, setImg] = useState()
      const [sessionStatus, setSessionStatus] = useState("No Status")
      const [imgixUrl, setImgixUrl] = useState()
      const [apiKey, setApiKey] = useState("")
      const [sourceId, setSourceId] = useState("")
      const [imgixDomain, setImgixDomain] = useState("")
      const [uploadPath, setUploadPath] = useState("")
      const [sessionId, setSessionId] = useState("")
      const [presignedUrl, setPresignedUrl] = useState("")
      const [error, setError] = useState("")
      const [disableForm, setDisableForm] = useState(false)
     
      return {
        /* your form elements from step 1 */
      }
    }

    Form Handler (handleSubmit)

    The handleSubmit function is triggered when the form is submitted. It handles the validation, creates an upload session, and prepares the data for upload.

    export default function ReactUploader() {
      // your import statements and state variables from steps 2 and 3
     
      // function to handle the form submission
      const handleSubmit = async (e) => {
        e.preventDefault()
     
        // error handling
        if (!img) {
          alert("Please select an image to upload")
          return
        } else if (!apiKey) {
          alert("Please enter your imgix API key")
        } else if (!sourceId) {
          alert("Please enter your source ID")
        }
     
        setDisableForm(true)
        setSessionStatus("OPENING")
     
        //Create the session and set the session ID
        await axios({
          method: "POST",
          url:
            `https://api.imgix.com/api/v1/sources/${sourceId}/upload-sessions/create` +
            uploadPath +
            img.name,
          maxBodyLength: Infinity,
          headers: {
            "Content-Type": "application/vnd.api+json",
            Authorization: `Bearer ${apiKey}`,
          },
        })
          .then((response) => {
            setSessionId(response.data.data.id)
            setSessionStatus(response.data.data.attributes.status)
            setPresignedUrl(response.data.data.attributes.url)
          })
          .catch((error) => {
            // server error handling
            console.log(error?.response?.data?.errors[0]?.detail)
            setSessionStatus("ERROR")
            setError(error?.response?.data?.errors[0]?.detail)
            console.log(error.message)
          })
      }
     
      return {
        /* your form elements from step 1 */
      }
    }
    Key Steps in handleSubmit

    Validation: The function checks if an image, API key, and source ID have been provided. It also parses the upload path. If any are missing, it alerts the user and halts further execution.

    Disable Form: The form is disabled (setDisableForm(true)) to prevent further submissions while the current one is processing.

    Create Upload Session: An API request is made to imgix to create a new upload session. This request includes the file name, source ID, and path to store the image. If successful, it stores the session ID, session status, and a presigned URL for uploading the image.

    Step 5: Managing the Upload Process With useEffect:

    Once the upload session is successfully created, the useEffect (opens in a new tab) hook monitors the sessionStatus and handles the actual image upload. It also handles the state changes when the upload is complete or if an error occurs.

    export default function ReactUploader() {
     
      // your import statements and state variables from steps 2 and 3
     
      useEffect(() => {
        //Use the presigned URL from the session ID to upload the image
        // documentation: https://docs.imgix.com/apis/management/assets#using-upload-sessions
        if (sessionStatus === "PENDING") {
          axios({
            method: "PUT",
            url: presignedUrl,
            headers: {
                      "Content-Type": "application/octet-stream",
                    },
            data: img,
                  }
                )
            .then(() => {
              // once the image is uploaded, close the session and set the completed state
              closeSession(sessionId)
              setSessionStatus("COMPLETE")
              setImgixUrl("https://" + imgixDomain + uploadPath + img.name)
              setDisableForm(false)
            })
            .catch((error) => {
              // if there is an error, set an error handler
              closeSession(sessionId)
              setSessionStatus("ERROR")
              console.log(error.message)
              setDisableForm(false)
            })
        }
     
        // function for closing the session on errors and upload
        const closeSession = (sessionId) => {
          // close the session
          axios({
            method: "POST",
            url:
            `https://api.imgix.com/api/v1/sources/${sourceId}/upload-sessions/close/` +
            sessionId,
            headers: {
                      "Content-Type": "application/vnd.api+json",
                      Authorization: `Bearer ${apiKey}`,
                    },
                  }
          )
          .catch((error) => {
            console.log(error)
          })
        }
      }, [sessionStatus, error])
     
      // your handleSubmit function from step 4
     
      return (
        // your form elements from step 1
     )
    }
     

    Key Steps in useEffect

    Monitor sessionStatus: The useEffect hook runs whenever sessionStatus changes. When the status is "PENDING", it proceeds with the upload.

    Upload the Image: An HTTP PUT request is made to the presigned URL, uploading the selected image. The Content-Type is set to application/octet-stream because we're uploading binary data.

    Handle Success or Error: On success, the session is closed, and the image URL is stored. On error, the session is closed, and the form is re-enabled.

    Step 6: Integrating the Component into Your App

    Now that the ReactUploader component is ready let's integrate it into the main application. Open src/App.js and replace its contents with the following:

    import React from "react"
    import ReactUploader from "./ReactUploader"
     
    function App() {
      return (
        <div className="App">
          <h1>Image Uploader with imgix</h1>
          <ReactUploader />
        </div>
      )
    }
     
    export default App

    Step 7: Running the Application

    To see your image uploader in action, run the following command in your terminal:

    npm start

    Your app should now run on http://localhost:3000 (opens in a new tab), where you can upload images to imgix.

    Conclusion

    In this tutorial, you built a simple React application that uploads images to the imgix API. This app can be expanded with additional features like better error handling, dynamic API key management, and more sophisticated image processing. You can also eliminate the user's API key requirement by handling the session creation and image upload on the server side.

    Note: This is a simplified demo that requires the user to input API keys and the Source ID. In a real-life application, you would already have these specified.

    Additionally, it is not a good idea to handle credentials on the client side, as they can be exposed to malicious actors. Always ensure that your API keys and other sensitive information are stored securely and never exposed to the client. There are several ways to securely handle API keys, such as sending the request to a serverless function or a backend server to handle the image uploading instead of making the request to imgix from the client side.