# react-filepond: the thin React adapter around the filepond uploader

> react-filepond renders one FilePond element and forwards your props to the filepond library, which makes it a small adapter rather than an upload engine of its own. Its two samples disagree on what onupdatefiles returns, and its manifest promises React versions its type check never sees.

**pqina/react-filepond** — 🔌 A handy FilePond adapter component for React

- Repository: https://github.com/pqina/react-filepond
- Website: https://pqina.nl/filepond
- Stars: 1,989 · Forks: 94
- Language: JavaScript
- License: MIT
- Published: 2026-10-13 · Updated: 2026-10-13 · Language: en
- Canonical page: https://hysenlabs.com/projects/pqina-react-filepond

## A thin component that forwards props to the filepond library

react-filepond is a wrapper, not an upload engine. It renders a single FilePond element and hands every prop you set to the filepond library underneath, so accepting blobs and remote URLs, resizing images and fixing EXIF orientation all happen in filepond rather than here. The peerDependencies block names three packages, react, react-dom and filepond, with filepond held to >=3.7.x <5.x. There is no dependencies block at all, which means the wrapper resolves nothing of its own at runtime and leaves you to install the core library, the plugins and the stylesheets separately. For a team that already wants FilePond's behaviour and needs React state to keep up with it, that thinness is the point. For a team that wants a component with its own retry policy, persistence or a progress queue, there is little code here to read, because dist/react-filepond.js is a generated bundle and the build reads lib/.

## Both packages are installed by hand, filepond as a peer

Start with the install command the README gives, which names both packages in one line:

```bash
npm install react-filepond filepond --save
```

Leave filepond out and the import of its stylesheet fails, since nothing in react-filepond depends on it directly. The opening lines of a working component then register what the wrapper needs:

```jsx
import { FilePond, registerPlugin } from 'react-filepond'
import 'filepond/dist/filepond.min.css'
registerPlugin(FilePondPluginImageExifOrientation, FilePondPluginImagePreview)
```

Those two plugin modules come from filepond-plugin-image-exif-orientation and filepond-plugin-image-preview, which the sample installs separately with npm i filepond-plugin-image-preview filepond-plugin-image-exif-orientation --save. A preview plugin also ships its own stylesheet, filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css, imported beside the core one. Forget that line and uploads still succeed, the images simply arrive without thumbnails, which is the kind of defect nobody catches in review.

## registerPlugin mutates a registry that every instance reads

registerPlugin is not a prop, it is a module level call, and both samples place it above the component definition. That placement matters, because the function adds the plugin to a registry inside filepond itself, which every FilePond instance on the page then consults. Call it inside a component body and you register once per mount, for the whole document. Neither sample offers an unregister call either, so a plugin registered on one route stays registered after the user navigates away, and two FilePond components in the same app cannot ask for different plugin sets. The element itself stays declarative:

```jsx
<FilePond
  files={files}
  onupdatefiles={setFiles}
  allowMultiple={true}
  maxFiles={3}
  server="/api"
  name="files" /* sets the file input name, it's filepond by default */
  labelIdle='Drag & Drop your files or <span class="filepond--label-action">Browse</span>'
/>
```

Everything above that line is configuration. Nothing in the element decides when to upload or what to send back.

## The two samples disagree about what onupdatefiles returns

Put the hooks sample and the class sample next to each other and the difference is not cosmetic. The hooks version writes onupdatefiles={setFiles}, handing your state setter straight to the wrapper, so whatever filepond emits becomes your state array. The class version maps first and keeps fileItems.map(fileItem => fileItem.file), which preserves the raw File objects and discards everything filepond attaches around them. One prop name, two different shapes in state, and the README does not say which one is authoritative. Then look at the import line. Both samples open with import React, { useState } from 'react' followed by import ReactDOM from 'react-dom', yet the class sample declares class App extends Component without ever importing Component, and neither sample uses the ReactDOM it pulls in. Drop the class sample into a file as printed and it breaks before the first render, on an identifier that was never brought into scope.

## The server prop sets a path prefix, not a contract

In both samples server="/api" is the entire upload configuration on the element. It tells filepond where to send files, while the request shape, the fields filepond expects in the reply, revert and restore handling sit in the filepond documentation at pqina.nl/filepond/docs rather than in this repository. So the part that decides whether an upload actually succeeds lives outside the adapter, and you still write the backend contract by hand. Two props in the sample are easy to read past. name="files" renames the form input, where filepond is the default, and maxFiles={3} caps the queue at three items while allowMultiple={true} permits more than one item to exist. allowReorder={true} appears only in the class sample, which matters when a user is expected to sort a queue before it is sent.

## No test script, and the build writes into the example app

The scripts block is the shortest honest summary of what runs in this project:

```json
"build:browser": "babel lib | bannerjs -m > dist/react-filepond.js && minicat dist/react-filepond.js > example/src/react-filepond/index.js",
"build:module": "minicat lib/index.js | bannerjs -m > dist/react-filepond.esm.js",
"prepare": "npm run build",
"dtslint": "dtslint types"
```

No test entry sits beside build, build:browser, build:module, build:watch and prepare, so nothing in this package exercises a rendered FilePond, a plugin registration or an upload round trip. The single check that does run is dtslint over the declarations in types/, and that validates types alone. Two details inside those lines are worth more attention. build:browser does not stop at dist/, it pipes the finished bundle a second time into example/src/react-filepond/index.js, so the example app runs a built copy instead of the source. And prepare runs npm run build, the hook npm fires for an install from a git checkout, so adding this repository as a dependency drives the Babel pipeline, with babel-cli at ^6.26.0, on the machine doing the install. That last write lands in a real directory at the repository root, where .babelrc, lib/, dist/, types/ and example/ sit side by side.

## React 19 is claimed while the type check stops at 16

The peerDependencies block is the sharpest gap in the repository:

```json
"peerDependencies": {
  "react": "16 - 19",
  "react-dom": "16 - 19",
  "filepond": ">=3.7.x <5.x"
},
```

That range promises support across three major React versions and, at the same time, locks FilePond users out of anything past the 4.x line. The devDependencies tell a narrower story about how much of that range gets exercised. @types/react sits at ^16.9.48, typescript at ^4.0.2 and dtslint at ^3.7.0, and the only validated type surface is types/index.d.ts. So the newest React the typings were written against is 16, the newest React the manifest accepts is 19, and nothing in this repository measures the distance between them. The activity record points the other way: the repository is not archived, its last push landed on 2026-07-13, and package.json carries version 7.1.3 while the release record lists 7.1.0 dated 2020-09-09, so the two numbers do not line up.

## Driving filepond directly trades React state for manual lifecycle

The alternative to this wrapper is not a rival React package, it is filepond on its own. You create the instance, attach your own handlers and destroy it yourself, which gives you per instance control over plugins and no dependency on React. What you give up is the translation shown above: props leave as camelCase settings, state comes back through onupdatefiles, and the instance arrives through oninit={() => this.handleInit()}, where the sample reads console.log("FilePond instance has initialised", this.pond) after storing it with ref={ref => (this.pond = ref)}. Both routes push files at the same server path and both need the core library installed. Choose the wrapper when your files already sit in React state and a single declaration can describe them. Choose the core library when one component needs plugins the rest of the page must never see, since the registry is global.

## Conclusion

Adopt react-filepond when your files already live in React state and you want one declaration to describe them, because the wrapper is small and every prop maps onto behaviour filepond already implements. Leave it out when you need cropping, rotating or flipping, since the README routes that work to Pintura instead. Verify two things first: install filepond yourself with npm install react-filepond filepond --save, and find out whether types/index.d.ts still compiles against the React and TypeScript versions your app runs.

## FAQ

### Does react-filepond install filepond for me?

No. package.json lists filepond under peerDependencies with the range >=3.7.x <5.x and has no dependencies block at all, so the README installs both packages in one command: npm install react-filepond filepond --save.

### Which React versions does react-filepond support?

The peerDependencies accept react and react-dom across 16 - 19. The development typings are pinned much older, with @types/react at ^16.9.48, typescript at ^4.0.2 and dtslint at ^3.7.0, so the checked type surface reflects React 16 rather than the newest version the manifest accepts.

### How do I add image previews to react-filepond?

Install filepond-plugin-image-preview separately, pass it to registerPlugin at module level, and import filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css. Both sample files in the README register it that way, outside the component.

### Why does the class component example in the react-filepond README fail?

It declares class App extends Component, but its import line only pulls in React, useState and ReactDOM, never Component. The hooks sample avoids the problem because it uses no classes at all.

### Does building react-filepond touch anything other than the dist folder?

Yes. build:browser pipes the finished bundle through minicat into example/src/react-filepond/index.js, and prepare runs npm run build, so installing from a checkout also rewrites that file inside the example app.

## Sources

- [License: MIT](https://github.com/pqina/react-filepond/blob/master/LICENSE)
- [pqina/react-filepond on GitHub](https://github.com/pqina/react-filepond)
- [Project website](https://pqina.nl/filepond)
- [README](https://github.com/pqina/react-filepond/blob/master/README.md)
- [Releases](https://github.com/pqina/react-filepond/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/pqina-react-filepond
