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