Since React is a client side framework it is recommend to handle uploads using Presigned URLs using a server side framework like Hono. We would highly recommend following the Hono guide below before doing this React guide!

Hono Guide

Installation

Get Gateway URL

Before we start you’ll need your Dedicated Gateway domain. When you create a Pinata account, you’ll automatically have a Gateway created for you! To see it, simply visit the Gateways Page see it listed there.

The gateway domains are randomly generated and might look something like this:

aquamarine-casual-tarantula-177.mypinata.cloud

Start up React Project

Run the command below to make a new React project:

npm create vite@latest

Give the project a name and select the React framework. Then cd into the project and install pinata.

npm i pinata

After making the project, create a .env.local file in the root of the project and put in the following variable:

VITE_SERVER_URL=
VITE_GATEWAY_URL=

The VITE_SERVER_URL is the URL of your server endpoint that will return Presigned URLs. If you don’t have one be sure to follow the Hono guide first. If you did follow it then you’ll remember it was http://localhost:8787 and we’ll need to make sure it’s running in order for our flow to work.

The format of the Gateway domain should be mydomain.mypinata.cloud.

Implementation

Now that we have our initial setup, open the src/App.tsx file and replace it with the following code.

src/App.tsx
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
import { PinataSDK } from 'pinata'

const pinata = new PinataSDK({
  pinataJwt: "",
  pinataGateway: import.meta.env.VITE_GATEWAY_URL
})

function App() {
  const [file, setFile] = useState<File | null>(null)
  const [uploadStatus, setUploadStatus] = useState('')
  const [link, setLink] = useState('')

  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.files && e.target.files.length > 0) {
      setFile(e.target.files[0])
    }
  }

  const handleUpload = async () => {
    if (!file) return

    try {
      setUploadStatus('Getting upload URL...')
      const urlResponse = await fetch(`${import.meta.env.VITE_SERVER_URL}/presigned_url`, {
        method: "GET",
        headers: {
          // Handle your own server authorization here
        }
      })
      const data = await urlResponse.json()

      setUploadStatus('Uploading file...')

      const upload = await pinata.upload.public
        .file(file)
        .url(data.url)

      if (upload.cid) {
        setUploadStatus('File uploaded successfully!')
        const ipfsLink = await pinata.gateways.public.convert(upload.cid)
        setLink(ipfsLink)
      } else {
        setUploadStatus('Upload failed')
      }
    } catch (error) {
      setUploadStatus(`Error: ${error instanceof Error ? error.message : String(error)}`)
    }
  }

  return (
    <>
      <div>
        <a href="<https://vite.dev>" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="<https://react.dev>" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React + Pinata</h1>
      <div className="card">
        <input type="file" onChange={handleFileChange} />
        <button onClick={handleUpload} disabled={!file}>
          Upload to Pinata
        </button>
        {uploadStatus && <p>{uploadStatus}</p>}
        {link && <a href={link} target='_blank'>View File</a>}
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
    </>
  )
}

export default App

In our code we have a pretty simple form that will trigger handleSubmit(). This function will do the following:

  • Make a request to our server for a Presigned URL (you will want to handle any server side auth here)
  • Use the returned Presigned URL in a client side upload
  • Use the upload response to create a link to the file

To try it out make sure your server is running, then in another terminal Run

npm run dev

Open up the site on http://localhost:5173 and upload a file!