FilePonds default functionality can be extended with, you guessed it, extensions.

We do this by setting the extensions property.

defineFilePond({
    extensions: [
        /* The additional extension we want to load */
    ],
});

defineFilePond will load a default set of extensions, any additionaly extensions will be automatically inserted into that list.

Extension order

By default extensions are executed in order they appear in the internal extensions array.

Default extensions

When we use the defineFilePond function it automatically calls createFilePondExtensionSet which sets the default extensions used by FilePond.

Adding extensions

Extensions are located in the filepond/extensions module.

import { FileExtensionValidator } from 'filepond/extensions/file-extension-validator.js';
import { FormPostStore } from 'filepond/extensions/form-post-store.js';

When we import an extension it automatically extends the FilePondElement types. To add the extension to FilePond we need to add it to the extensions property.

import { defineFilePond } from 'filepond';
import { locale } from 'filepond/locales/en-gb.js';

import { FileExtensionValidator } from 'filepond/extensions/file-extension-validator.js';
import { FormPostStore } from 'filepond/extensions/form-post-store.js';

defineFilePond({
    locale,

    // Load our extension
    extensions: [FileExtensionValidator, FormPostStore],
});

Registering an extension automatically creates a typed property on each FilePond instance with the name of the extension. See Updating properties for additional details.

Setting properties

We can set a configuration object together with the extension like shown below.

import { defineFilePond } from 'filepond';
import { locale } from 'filepond/locales/en-gb.js';
import { FileExtensionValidator } from 'filepond/extensions/file-extension-validator.js';
import { FormPostStore } from 'filepond/extensions/form-post-store.js';

defineFilePond({
    locale,
    extensions: [
        [
            FileExtensionValidator,
            {
                // We can set initial properties for our extension here
                accept: ['.jpg', '.png'],
            },
        ],
        FormPostStore,
    ],
});

Updating properties

If we want to update extension properties we can do so on the <file-pond> element itself.

import { defineFilePond } from 'filepond';
import { locale } from 'filepond/locales/en-gb.js';
import { FileExtensionValidator } from 'filepond/extensions/file-extension-validator.js';
import { FormPostStore } from 'filepond/extensions/form-post-store.js';

const elements = defineFilePond({
    locale,
    extensions: [
        FileExtensionValidator,
        [
            FormPostStore,
            {
                url: '/upload',
                name: 'files',
            },
        ],
    ],
});

elements.forEach((element) => {
    // Update properties
    element.FileExtensionValidator = {
        accept: ['.jpg', '.png', '.gif'],
    };
});

We can also get a reference to a <file-pond> element and then update properties on it.

const element = document.querySelector('file-pond');

// Update properties
element.FileExtensionValidator = {
    accept: ['.jpg', '.png', '.gif'],
};

Running extension actions

To run an extension action we update the state on an Entry.

The example below will run the store action for all currently loaded files.

// An array of all file-pond elements
const elements = defineFilePond({
    locale,
    extensions: [[FormPostStore]],
});

// When an upload button is clicked
triggerAsyncUploadButton.onclick = () => {
    // For each file-pond element
    for (const element of elements) {
        // We update the state of each Entry
        element.entries = element.entries.map((entry) => ({
            ...entry,
            state: {
                // Runs the `store` action of the `FormPostStore`
                store: true,
            },
        }));
    }
};

Custom extensions

We can create custom extensions using the createExtension function, or one of the template extension functions createStoreExtension, createTransformExtension, and createValidatorExtension.

Below we use the createTransformExtension function to create an image editor extension with Pintura.

import { defineFilePond, createTransformExtension } from 'filepond';
import { locale } from 'filepond/locales/en-gb.js';

const PinturaTransform = createTransformExtension(
    // The name of our extension
    'Pintura',
    // Default settings
    {
        actionTransform: 'editMedia',
    },
    // Extension internals, these are used by `createTransformExtension`
    () => ({
        // The `canTransformEntry` is called to test if we can transform an image
        // Here we use a cheap, but not always accurate, method to test if an entry can be transformed
        canTransformEntry: (entry) => /video|image/.test(entry.type),

        // Use the `prepareTransformEntry` hook to preload dependencies
        prepareTransformEntry: async (entry, { onprogress, abortController }) => {
            await import('@pqina/pintura');
        },

        // The `transformEntry` function is called when we're ready to transform an entry
        transformEntry: async (entry, { onprogress, abortController }) => {
            // @pqina/pintura should be cached, so is a quick call now
            const { openDefaultEditor } = await import('@pqina/pintura');

            // Get props
            const { file, extension } = entry;
            const { input, history = [] } = extension.Pintura;

            // Determine which file to edit
            const src = input || file;

            // Open the file in the editor
            const editor = openDefaultEditor({
                src,

                // We require a square crop
                imageCropAspectRatio: 1,
            });

            // Restore any previously stored history state
            if (history.length) {
                editor.on('load', () => {
                    editor.history.write(history.pop());
                });
            }

            // Returns the edited file and its image state
            const { dest, imageState } = await new Promise((resolve) => {
                editor.on('process', resolve);
                editor.on('close', () =>
                    resolve({
                        // We don't return a result
                    })
                );
            });

            editor.destroy();

            // User closed the editor
            if (!dest) {
                return;
            }

            // Return transformed file and optionally update history
            return {
                file: dest,
                history: [...history, imageState],
            };
        },
    })
);

// Now we can use `PinturaTransform` like this
const elements = defineFilePond({
    locale,
    extensions: [PinturaTransform],
});