How to Upload files to IPFS with React
Published: 2023-01-08 04:20:57

[✨ **Watch on YouTube**](https://youtu.be/z0YlnIxE4YA)  |  [🐙 GitHub](https://github.com/RodionChachura/react-toolkit)  |  [🎮 Demo](https://reactkit.radzion.com/ipfs)

Let's make a React component for uploading a file to a distributed storage IPFS.

```tsx
import { InputWrapperWithErrorMessage } from "lib/ui/inputs/InputWrapper"
import { useEffect } from "react"
import { FileInput } from "lib/ui/inputs/FileInput"
import { SelectedFile } from "lib/ui/inputs/SelectedFile"
import { useUploadFileMutation } from "web3/hooks/useUploadFileMutation"
import { getDistributedFileName } from "web3/utils/getDistributedFileName"

interface Props {
  value?: string
  error?: string
  onChange: (value?: string) => void
}

export const PdfFileInput = ({ value, error, onChange }: Props) => {
  const {
    mutate: uploadFile,
    data: uploadedFileUri,
    reset,
    isLoading,
  } = useUploadFileMutation()

  useEffect(() => {
    if (uploadedFileUri && !value) {
      onChange(uploadedFileUri)
      reset()
    }
  }, [onChange, reset, uploadedFileUri, value])

  return (
    <InputWrapperWithErrorMessage label="Upload a file" error={error}>
      {value ? (
        <SelectedFile
          name={getDistributedFileName(value)}
          onRemove={() => onChange(undefined)}
        />
      ) : (
        <FileInput
          isLoading={isLoading}
          onSubmit={(file) => {
            uploadFile(file)
          }}
          accept={{
            "application/pdf": [".pdf"],
          }}
        />
      )}
    </InputWrapperWithErrorMessage>
  )
}
```

Here we have a `PDFFileInput` component with `value`, `error`, and `onChange `properties. First, we render a wrapper that displays the label and error, if any. Then based on value presence, we show either the `SelectedFile` component or the `FileInput`.

```tsx
import styled from "styled-components"
import { Card } from "lib/ui/Card"
import { inputBackgroundCSS, inputBorderRadiusCSS } from "./config"
import { useDropzone, Accept } from "react-dropzone"
import { VStack } from "lib/ui/Stack"
import { Text } from "lib/ui/Text"
import { defaultTransitionCSS } from "lib/ui/animations/transitions"
import { UploadIcon } from "lib/ui/icons/UploadIcon"
import { OutlinedButton } from "lib/ui/buttons/rect/OutlinedButton"

interface Props {
  onSubmit: (file: File) => void
  accept: Accept
  isLoading?: boolean
}

const Container = styled(Card)`
  flex: 1;
  padding: 32px;
  ${inputBorderRadiusCSS};
  ${inputBackgroundCSS};
  cursor: pointer;

  ${defaultTransitionCSS};

  :hover {
    background: ${({ theme }) => theme.colors.backgroundGlass2.toCssValue()};
  }
`

export const FileInput = ({ onSubmit, accept, isLoading }: Props) => {
  const { getRootProps, getInputProps } = useDropzone({
    maxFiles: 1,
    accept,
    onDrop: (files) => {
      onSubmit(files[0])
    },
  })

  const acceptedExtensions = Object.values(accept).flat()

  return (
    <Container {...getRootProps()}>
      <VStack gap={16} alignItems="center">
        <Text size={34} color="supporting">
          <UploadIcon />
        </Text>
        <Text color="supporting">
          Drag and drop a{" "}
          {acceptedExtensions
            .map((extension) => extension.toUpperCase())
            .join("/")}{" "}
          file here or
        </Text>
        <OutlinedButton isLoading={isLoading} as="div">
          Click to upload
        </OutlinedButton>
      </VStack>
      <input {...getInputProps()} />
    </Container>
  )
}
```

The `FileInput` component receives the `onSubmit`, `accept`, and `isLoading` properties. We pass the accept object to the `useDropzone` hook to allow only specific file types and the onDrop callback that will trigger the `onSubmit` property. To allow dropping files into area input, we leverage the use dropzone hook from the `react-dropzone` library and pass the getRootProps to the container and getInputProps to an invisible input element.

Once the user has selected a file, we use the useUploadFileMutation hook. Here we leverage the web3storage library to upload files to IPFS. The put method will return a content identifier from IPFS that we can wrap with the gateway prefix to get an accessible URL.
The problem with this hook implementation is exposing the web3storage key with our front-end code. So take this part and use it on the back end. One solution could be to [upload the file to S3 using a pre-signed URL](https://youtu.be/uBRT1Yu2mng) and then ask the back-end to duplicate that file to IPFS. Another thing to note is that IPFS might delete your data to make room for other content. To prevent that, you need to use a pinning service. There are plenty of them, but your would need to pay after exceeding the free tier.
After upload success, we show the `SelectedFile` component with the files' name we extract with a tiny `getDistributedFileName` helper. To reset the field and upload the file again, the user can press the "Remove" button.