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.
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 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.
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
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, 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.