Edit Video and Images With React

In this short step by step tutorial we’ll set up Pintura Image Editor with its Video Editor extension to edit images and videos in the browser with React.

We’re going to use Pintura because building a complete video editor from scratch is going to take ages. It let’s us skip over all the drama of dealing with various browser quirks and device differences to the fun part of integrating an image editor.

Let’s set up a basic JSX file and then we’ll add Pintura.

function App() {
    return <div>Hai!</div>;
}

export default App;

I did say basic.

Adding The Pintura Editor Component

Let’s download Pintura and add the React <PinturaEditor> component to the mix.

We’ll also need to load the Pintura stylesheet, and to keep things concise we’ll import getEditorDefaults, a helper function that contains all the default editor settings.

// Import the editor styles
import '@pqina/pintura/pintura.css';

// Import the editor default configuration
import { getEditorDefaults } from '@pqina/pintura';

// Import the editor component from `react-pintura`
import { PinturaEditor } from '@pqina/react-pintura';

// get default properties
const editorConfig = getEditorDefaults();

function App() {
    return (
        <div>
            <PinturaEditor
                {...editorConfig}
                src="./my-image.jpeg"
            ></PinturaEditor>
        </div>
    );
}

export default App;

One final thing we need to do is set a height to the PinturaEditor parent element, by default Pintura will try to fill all the available vertical space.

const divStyle = {
    height: 600,
};

function App() {
    // Assign height style to div
    return (
        <div style={divStyle}>
            <PinturaEditor
                {...editorConfig}
                src="./my-image.jpeg"
            ></PinturaEditor>
        </div>
    );
}

When we now add an image to our project public folder and call it my-image.jpeg it loads in Pintura.

Getting the resulting image

We’ll set the imageCropAspectRatio to 1 so Pintura enforces a square image crop and we get an obviously different output image. If your input image is square already set it to 16/9 which will require a 16:9 crop.

Now to get the resulting image file we add the onProcess callback.

function App() {
    // called when user is done editing the image
    const handleProcessMedia = (res) => {
        console.log(res);
        // logs: { src:…, dest:… , imageState:…, store:… }
    };

    return (
        <div style={divStyle}>
            <PinturaEditor
                {...editorConfig}
                src="./my-image.jpeg"
                imageCropAspectRatio={1}
                onProcess={handleProcessMedia}
            ></PinturaEditor>
        </div>
    );
}

Let’s add an <img> element so we can preview the edited image.

function App() {
    const [editorResult, setEditorResult] = useState(undefined);

    const handleProcessMedia = (res) => {
        // Get a reference to the file object
        const { dest } = res;

        // Set an ObjectURL to the file object
        setEditorResult(URL.createObjectURL(dest));
    };

    return (
        <div style={divStyle}>
            {editorResult && <img src={editorResult} alt="" />}

            <PinturaEditor
                {...editorConfig}
                src="./my-image.jpeg"
                imageCropAspectRatio={1}
                onProcess={handleProcessMedia}
            ></PinturaEditor>
        </div>
    );
}

That’s it!

With image editing implemented it’s finally time to move on to video editing.

Video Editing

To enable video editing we load the Pintura Video extension and import the relevant functionality.

But first we need to decide which video encoder to use.

Choosing a VideoEncoder

The Pintura Video extension ships with a MediaStreamEncoder and an FFmpegEncoder.

Using MediaStreamEncoder

The MediaStreamEncoder uses browser APIs to generate the output video. This results in a very lightweight package.

Unfortunately the MediaStream API is still a bit limited and speeding up the video generation process is not possible. This means the time it takes to generate a video is equal to the total video clip play time. Video clip time means the amount of time remaining after trimming the video in Pintura.

For short videos this is fine, for longer video’s it will take too long to generate a video resulting in a sub par user experience.

Using FFmpegEncoder

When using the FFmpegEncoder we rely on the FFmpeg WASM library. It’s a versatile client-side video encoder. For longer video’s it’s a lot faster than using the MediaStreamEncoder.

The downside of using the FFmpegEncoder is that it needs to download the FFmpeg core package which clocks in at 25MB, depending on the user connection, this can be quite a lot of data to transfer. Consider however that uploading a big video file to the server will also take a while so this might not be a huge problem.

Adjusting the template

Let’s first zoom in on the non-template part of the JSX file.

We need to import the video editor specific functionality and load the video editor plugin styles.

Then we adjust the default configuration so Pintura knows how to handle image input and video input.

import '@pqina/pintura/pintura.css';

// Add video editor styles
import '@pqina/pintura-video/pinturavideo.css';

// Add video editor trim plugin and export logic
import {
    createDefaultVideoWriter,
    createMediaStreamEncoder,
    plugin_trim,
    plugin_trim_locale_en_gb,
} from '@pqina/pintura-video';

// We need to import the default media writer
// this allows us to handle images and videos
import {
    getEditorDefaults,
    setPlugins,
    createDefaultMediaWriter,
    createDefaultImageWriter,
    imageStateToCanvas,
} from '@pqina/pintura';

import { PinturaEditor } from '@pqina/react-pintura';

// Add the video trim plugin to Pintura
setPlugins(plugin_trim);

// Configure the editor to output videos and images
const editorConfig = getEditorDefaults({
    locale: {
        ...plugin_trim_locale_en_gb,
    },
    imageWriter: createDefaultMediaWriter(
        {
            // Images and videos will be resized down to 200 width,
            // aspect ratio is maintained
            targetSize: {
                width: 200,
            },
        },
        [
            // Used for writing output images
            createDefaultImageWriter({
                // Convert all input images to JPEGs
                mimeType: 'image/jpeg',

                // Compress image
                quality: 0.75,
            }),
            // Used for writing output videos
            createDefaultVideoWriter({
                encoder: createMediaStreamEncoder({
                    // Required for drawing video output
                    imageStateToCanvas,

                    // Compress video
                    videoBitrate: 384 * 1024,
                }),
            }),
        ]
    ),
});

The next step is to adjust the template so it loads an MP4 file and can preview a generated video.

function App() {
    const [editorResult, setEditorResult] = useState(undefined);

    const handleProcessMedia = (res) => {
        // Get a reference to the file object
        const { dest } = res;

        // Set an ObjectURL to the file object
        setEditorResult({
            url: URL.createObjectURL(dest),
            type: /video/.test(dest.type) ? 'video' : 'image',
        });
    };

    return (
        <div style={divStyle}>
            {editorResult &&
                (editorResult.type === 'image' ? (
                    <img src={editorResult.url} alt="" />
                ) : (
                    <video src={editorResult.url} />
                ))}

            <PinturaEditor
                {...editorConfig}
                util="trim"
                src="./my-video.mp4"
                imageCropAspectRatio={1}
                onProcess={handleProcessMedia}
            ></PinturaEditor>
        </div>
    );
}

Let’s add a video called my-video.mp4 to the public folder, when we refresh the page Pintura automatically loads the video and shows the trim utility.

That’s all there is to it. Pintura can now be used to edit both images and video’s.

To test the video editor functionality please visit either edit.video or try this demo on the Pintura product page

I share web dev tips on Twitter, if you found this interesting and want to learn more, follow me there

Or join my newsletter

More articles More articles