# Render videos in JavaScript

> Render a video template as an MP4 and follow its progress with the JavaScript SDK.

Canonical: https://bannerify.co/docs/sdk/js/apis/render-video

A video template renders as an MP4. Each second of the video counts as one generation, and as two at 60 fps. [Generate videos](https://bannerify.co/docs/essentials/generate-videos) explains how to design one.

`renderAndWaitVideo` and `renderVideo` take a template id and an options object.

<ParamField body="templateId" type="string" required>
  The id of a video template, for example `tpl_xxx`.
</ParamField>

<ParamField body="modifications" type="Modification[]">
  The values to put into the template's layers. A video layer takes a `src`, and an audio track takes a `src` with its name. [Modifications](https://bannerify.co/docs/essentials/modifications) lists the fields each layer accepts.
</ParamField>

## Render and wait for the URL

`renderAndWaitVideo` returns `{ result }` with the URL of the MP4, or `{ error }`.

```ts
const { result: url, error } = await bannerify.renderAndWaitVideo("tpl_xxx", {
  modifications: [
    { name: "headline", text: "Summer sale" },
    { name: "music", src: "https://example.com/song.mp3" },
  ],
})

if (error) throw new Error(error.message)

console.log(url)
```

## Follow the progress

`renderVideo` returns an async iterator of events. The last event is `done` or `error`.

```ts
for await (const event of await bannerify.renderVideo("tpl_xxx", {
  modifications: [{ name: "headline", text: "Summer sale" }],
})) {
  if (event.type === "created") console.log(`video ${event.id}`)
  if (event.type === "queued") console.log(`${event.ahead} videos before this one`)
  if (event.type === "progress") console.log(`${Math.round(event.progress * 100)}%`)
  if (event.type === "done") console.log(event.url)
  if (event.type === "error") console.error(event.error.message)
}
```

| Event | Fields |
| --- | --- |
| `created` | `id`: the id of the video |
| `queued` | `ahead`: the number of videos before this one |
| `progress` | `frame`, `totalFrames`, and `progress` from 0 to 1 |
| `done` | `url`: the URL of the MP4 |
| `error` | `error`: `code`, `message`, and `requestId` |

A refused request, such as an invalid API key or a used-up quota, gives one `error` event.

When the connection drops, `renderVideo` reads the same video again by its id, up to four times, and goes on with its events. It never renders the video twice.

## Start a video without waiting

`createVideo` returns `{ result }` with the id of the video at once, or `{ error }`. Read the video later with `getVideo`.

```ts
const { result, error } = await bannerify.createVideo("tpl_xxx", {
  modifications: [{ name: "headline", text: "Summer sale" }],
})

if (error) throw new Error(error.message)

console.log(result.id)
```

## Read a video again

The video renders to its end also when your process stops. Keep its `id` from the `created` event to read it later.

`getVideo` returns `{ result }` with the status of the video, or `{ error }`:

```ts
const { result: video, error } = await bannerify.getVideo("vid_xxx")

if (error) throw new Error(error.message)

if (video.status === "done") console.log(video.url)
if (video.status === "failed") console.error(video.error)
```

| Field | Value |
| --- | --- |
| `id` | The id of the video |
| `status` | `queued`, `rendering`, `done`, or `failed` |
| `url` | The URL of the MP4, once `done` |
| `error` | Why the video `failed` |

`watchVideo` returns the events of the video again, from the latest one, as `renderVideo` does:

```ts
for await (const event of await bannerify.watchVideo("vid_xxx")) {
  if (event.type === "done") console.log(event.url)
}
```

## Related

- [Previous: Create images and PDFs in JavaScript](https://bannerify.co/docs/sdk/js/apis/create-image)
- [Next: Signed URLs in JavaScript](https://bannerify.co/docs/sdk/js/apis/signed-url)
- [Documentation index](https://bannerify.co/docs/llms.txt)
