> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pinata.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# React

Since React is a client side framework it is recommend to handle uploads using [Presigned URLs](/files/presigned-urls) using a server side framework like [Hono](/frameworks/hono). We would highly recommend following the Hono guide below before doing this React guide!

<Card href="/frameworks/hono" horizontal icon="fire" title="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](https://app.pinata.cloud/gateway) see it listed there.

<img style={{ width: "100%", borderRadius: "0.5rem" }} src="https://docs.mypinata.cloud/ipfs/bafkreiaedels7u6z6f3dwgmp5t25a2o74xt2kqe4kf72tk6orri7hrbfy4" />

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:

```bash theme={null}
npm create vite@latest
```

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

```bash theme={null}
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](/frameworks/hono) 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.

```typescript src/App.tsx theme={null}
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

```bash theme={null}
npm run dev
```

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