Video to GIF Converter with React

Banner for a MediaJam post

Christian Nwamba

Sometimes we make videos but want them as an image, or sometimes we make videos but are obliged to upload images instead. The solution to this is converting the video to GIF (Graphics Interchange Format). GIF is a lossless format for image files that supports both animated and static images. In this tutorial, we will be showing how to convert a video into an GIF with React.

Prerequisites

To follow this tutorial, you need the following pre-requisites:

  • Basic knowledge of JavaScript.
  • Understanding of Reactjs.

The complete code is here on Codesandbox.

https://codesandbox.io/embed/cranky-ptolemy-rggwtt?autoresize=1&fontsize=14&hidenavigation=1&theme=dark

Getting Started

Firstly, setup a react app. To do so, paste the command below; ensure you have npm and node installed.

1npx create-react-app video-to-gif

Next is to run the application; run the command below to run the app.

1npm start

Navigate to your browser on localhost:3000, and you should have the default react page displayed.

Installing Packages

Now that react is set up, navigate to your terminal and run the command below to install the packages used in converting and downloading the video to gif.

1npm install @ffmpeg/ffmpeg@0.9.8 @ffmpeg/core@0.9.0 file-saver

Ensure you install these specific versions of @ffmpeg/ffmpeg and @ffmpeg/core as the latest versions has issues we won’t deal with in this tutorial.

Implementing the Video to GIF converter

Next is to make changes to App.js file. Open the file and add the code snippet below.

1//App.js
2import React, { useEffect,useState } from "react"
3import './App.css';
4import { createFFmpeg, fetchFile } from "@ffmpeg/ffmpeg";
5import { saveAs } from "file-saver";
6
7const ffmpeg = createFFmpeg({ log: true });
8
9function App() {
10 const [video, setVideo] = useState();
11 const [name, setName] = useState()
12
13 const init= async () => {
14 await ffmpeg.load();
15 };
16 const setvid = (e)=>{
17 setVideo(e.target.files?.item(0))
18 setName(e.target.files[0].name)
19 }
20useEffect(() => {
21 init();
22}, []);
23
24return (
25 <div className="App">
26 <header className="App-header">
27 {video && (
28 <video controls width="250" src={URL.createObjectURL(video)}></video>
29 )}
30 <input type="file" onChange={(e)=>setvid(e)}/>
31 </header>
32 </div>
33)
34}
35export default App;

We first declare the ffmpeg using the createffmpeg method and set the log to true to allow us to log the processes. We then make use of the following :

  • useEffect ****to call the init(), which loads the ffmpeg using the FF``mpeg.load() function.
  • the onChange event handler is triggered when you select the video to convert. It also triggers the setvid(), which sets the state for the name of the file and sets the video to be rendered within the return method.

Note: sometimes, we might run into an error “S**haredArrayBufffer** is not defined" don't panic, as this is due to SharedArrayBuffer being only available to pages that are cross-origin isolated.

To fix that, click the link below.

https://github.com/gzuidhof/coi-serviceworker

  • Download coi-serviceworker.js (or coi-serviceworker.min.js).
  • Put it in the public folder
  • Add this to the index.html file:
1<script src="coi-serviceworker.min.js"></script>

Save and start the app server with npm run start . You should get something like this:

Click on the Choose file button and upload a video. You should get something like this:

Now that the video is rendered let's update the App.js file with the conversion functionality.

First, add this button below header . The button will trigger the function that converts the video to a gif

1//App.js
2...
3const init = async () => {
4 await ffmpeg.load();
5};
6
7const convertToGif = async () => {
8 await ffmpeg.FS('writeFile', name, await fetchFile(video));
9 await ffmpeg.run('-i', name, '-t', '2.5', '-ss', '2.0', '-f', 'gif', 'output.gif');
10 const data = ffmpeg.FS('readFile', 'output.gif');
11 const url = URL.createObjectURL(
12 new Blob([data.buffer], { type: 'image/gif' }),
13 );
14 saveAs(url, "output.gif");
15};
16 ...
17return(
18 <header>
19 ...
20 </header>
21 <button onClick={convertToGif}>Download and Convert to GIF</button>
22 ...
23)

Let’s break the code snippet above into chunks.

**convertToGif()**: this function is triggered after uploading the video. It allows you to download the converted file. Let’s go through some of the methods in this function.

One thing to keep in mind is the use of Web Assembly, which is what the package is built on.

**FFmpeg.FS**: Web Assembly always handles its memory, and in other to run FFmpeg, we need to make it known to that file system by writing the file to the memory of the FFmpeg. This is what this function does. It accepts the video as parameter and saves it in temporary memory.

N/B**:** It only stays in memory until the browser is refreshed

Next, is the FFmpeg.run() function. We pass the following parameters:

  • **-i**: is the input file
  • **-t**: is the time or length of the video
  • **-ss**: is the starting second
  • **-f**: is for encoding, in our case, gif
  • **output.gif**: is the name of the file we want to write to.

Once this is complete, it saves the file in memory;

Next, is to read the file by using the FFmpeg.Fs(readFile,``" the file in memory``"``)

After that, we need to convert the file to a URL for our file-saver package to be able to make it downloadable. To do that we use the blob method.

1const url = URL.createObjectURL(
2 new Blob([data.buffer], { type: 'image/gif' }),
3);

And lastly, we pass the URL to the file-saver and specify the download name.

1saveAs(url, "output.gif");

Now we should have our A``pp.j``s file like the one below.

1import { useEffect, useState } from "react"
2import './App.css';
3import { createFFmpeg, fetchFile } from "@ffmpeg/ffmpeg";
4import { saveAs } from "file-saver";
5
6const ffmpeg = createFFmpeg({ log: true });
7
8function App() {
9 const [video, setVideo] = useState();
10 const [name, setName] = useState()
11 const init = async () => {
12 await ffmpeg.load();
13 };
14
15 const setVid = (e) => {
16 setVideo(e.target.files?.item(0))
17 setName(e.target.files[0].name)
18 }
19
20 const convertToGif = async () => {
21 await ffmpeg.FS('writeFile', name, await fetchFile(video));
22 await ffmpeg.run('-i', name, '-t', '2.5', '-ss', '2.0', '-f', 'gif', 'output.gif');
23 const data = ffmpeg.FS('readFile', 'output.gif');
24 const url = URL.createObjectURL(
25 new Blob([data.buffer], { type: 'image/gif' }),
26 );
27 saveAs(url, "output.gif");
28 };
29
30useEffect(() => {
31 init();
32}, []);
33
34return (
35 <div className="App">
36 <header className="App-header">
37 {video && (
38 <video controls width="250" src={URL.createObjectURL(video)}></video>
39 )}
40 <input type="file" onChange={(e) => setVid(e)} />
41 <button onClick={convertToGif}>Download and Convert to GIF</button>
42 </header>
43 </div>
44)
45}
46export default App;

Conclusion

In this tutorial, we implemented conversion of videos to GIFs with React, and ffmpeg.wasm JavaScirpt wrappers @ffmpeg/ffmpeg and @ffmpeg/core.

Further Reading

FFmpeg File-saver

Christian Nwamba

Developer Advocate at AWS

A software engineer and developer advocate. I love to research and talk about web technologies and how to delight customers with them.