Getting started
The loomSDK is an umbrella term which refers to the recordSDK and embedSDK. The embedSDK can be used independently of the recordSDK.
For the recordSDK, we offer two types of pricing & packaging: SDK Standard and SDK Custom. SDK Standard is ideal for Loom integrations and is free. SDK Custom provides infrastructure to add asynchronous video into your own application. It is a paid offering.
This guide walks you through setup for both SDK Standard and SDK Custom.
Installation​
Install the recordSDK and embedSDK.
$ npm install @loomhq/record-sdk @loomhq/loom-embed
Set up your recordSDK application​
To obtain an SDK Standard or SDK Custom application set up your developer account.
Learn more here about the differences between the two types of apps.
SDK Standard​
If you choose SDK Standard, two applications will be created:
- A “sandbox” app for development—this application can be used on localhost domains
- A “live” app for production
These apps can only run on domains you provide in the input form (plus localhost for the sandbox app). Optionally, you can also implement key-pair authentication by following the steps here.
SDK Custom​
If you choose SDK Custom, only one application will be created. This app can run against any domain because it requires key-pair authentication—follow the steps here for set up instructions.
Implement​
The code below is all you need to set up an SDK Standard app. SDK Custom and SDK Standard apps using key-pair auth will require server side setup and a slightly different client side implementation. Follow the steps here for instructions.
- React
- TypeScript
- JavaScript
import { setup } from "@loomhq/record-sdk";
import { isSupported } from "@loomhq/record-sdk/is-supported";
import { oembed } from "@loomhq/loom-embed";
import { useEffect, useState } from "react";
const PUBLIC_APP_ID = "public-app-id-obtained-from-developer-portal";
const BUTTON_ID = "loom-record-sdk-button";
export default function App() {
const [videoHTML, setVideoHTML] = useState("");
useEffect(() => {
async function setupLoom() {
const { supported, error } = await isSupported();
if (!supported) {
console.warn(`Error setting up Loom: ${error}`);
return;
}
const button = document.getElementById(BUTTON_ID);
if (!button) {
return;
}
const { configureButton } = await setup({
publicAppId: PUBLIC_APP_ID,
});
const sdkButton = configureButton({ element: button });
sdkButton.on("insert-click", async (video) => {
const { html } = await oembed(video.sharedUrl, { width: 400 });
setVideoHTML(html);
});
}
setupLoom();
}, []);
return (
<>
<button id={BUTTON_ID}>Record</button>
<div dangerouslySetInnerHTML={{ __html: videoHTML }}></div>
</>
);
}
import { setup } from "@loomhq/record-sdk";
import { isSupported } from "@loomhq/record-sdk/is-supported";
import { oembed } from "@loomhq/loom-embed";
const PUBLIC_APP_ID = "public-app-id-obtained-from-developer-portal";
const BUTTON_ID = "loom-record-sdk-button";
function insertEmbedPlayer(html: string) {
const target = document.getElementById("target");
if (target) {
target.innerHTML = html;
}
}
async function init() {
const { supported, error } = await isSupported();
if (!supported) {
console.warn(`Error setting up Loom: ${error}`);
return;
}
const root = document.getElementById("app");
if (!root) {
return;
}
root.innerHTML = `<button id="${BUTTON_ID}">Record</button>`;
const button = document.getElementById(BUTTON_ID);
if (!button) {
return;
}
const { configureButton } = await setup({
publicAppId: PUBLIC_APP_ID,
});
const sdkButton = configureButton({ element: button });
sdkButton.on("insert-click", async (video) => {
const { html } = await oembed(video.sharedUrl, { width: 400 });
insertEmbedPlayer(html);
});
}
init();
import { setup } from "@loomhq/record-sdk";
import { isSupported } from "@loomhq/record-sdk/is-supported";
import { oembed } from "@loomhq/loom-embed";
const PUBLIC_APP_ID = "public-app-id-obtained-from-developer-portal";
const BUTTON_ID = "loom-record-sdk-button";
function insertEmbedPlayer(html) {
const target = document.getElementById("target");
if (target) {
target.innerHTML = html;
}
}
async function init() {
const { supported, error } = await isSupported();
if (!supported) {
console.warn(`Error setting up Loom: ${error}`);
return;
}
const root = document.getElementById("app");
if (!root) {
return;
}
root.innerHTML = `<button id="${BUTTON_ID}">Record</button>`;
const button = document.getElementById(BUTTON_ID);
if (!button) {
return;
}
const { configureButton } = await setup({
publicAppId: PUBLIC_APP_ID,
});
const sdkButton = configureButton({ element: button });
sdkButton.on("insert-click", async (video) => {
const { html } = await oembed(video.sharedUrl, { width: 400 });
insertEmbedPlayer(html);
});
}
init();
Code walkthrough​
Imports​
Import setup
from @loomhq/record-sdk
, isSupported
from @loomhq/record-sdk/is-supported
, and oembed
from @loomhq/loom-embed
. setup
is used to instantiate the SDK and isSupported
detects if a user’s browser is compatible with the SDK. oembed
is used for handling video inserts.
note
The recordSDK should be loaded asynchronously and is not supported for SSR. Read more here.
- React
- TypeScript
- JavaScript
import { setup } from "@loomhq/record-sdk";
import { isSupported } from "@loomhq/record-sdk/is-supported";
import { oembed } from "@loomhq/loom-embed";
import { setup } from "@loomhq/record-sdk";
import { isSupported } from "@loomhq/record-sdk/is-supported";
import { oembed } from "@loomhq/loom-embed";
import { setup } from "@loomhq/record-sdk";
import { isSupported } from "@loomhq/record-sdk/is-supported";
import { oembed } from "@loomhq/loom-embed";
note
The recordSDK depends on the MediaRecorder API and enablement of third-party cookies so it will not work in all browsers. This is why isSupported
should be used as a check before setting up the SDK.
Create a button​
Create one or more button elements with a unique ID to trigger the recordSDK flow.
- React
- TypeScript
- JavaScript
const BUTTON_ID = "loom-record-sdk-button";
// ...
<button id={BUTTON_ID}>Record</button>;
const BUTTON_ID = "loom-record-sdk-button";
// ...
const root = document.getElementById("app");
if (!root) {
return;
}
root.innerHTML = `<button id="${BUTTON_ID}">Record</button>`;
const BUTTON_ID = "loom-record-sdk-button";
// ...
const root = document.getElementById("app");
if (!root) {
return;
}
root.innerHTML = `<button id="${BUTTON_ID}">Record</button>`;
Check if supported​
The recordSDK depends on the MediaRecorder API and enablement of third-party cookies so it will not work in all browsers. This is why isSupported
should be used as a check before setting up the SDK. If the browser is not supported, an error
will exist.
- React
- TypeScript
- JavaScript
const { supported, error } = await isSupported();
if (!supported) {
console.warn(`Error setting up Loom: ${error}`);
return;
}
const { supported, error } = await isSupported();
if (!supported) {
console.warn(`Error setting up Loom: ${error}`);
return;
}
const { supported, error } = await isSupported();
if (!supported) {
console.warn(`Error setting up Loom: ${error}`);
return;
}
Set up the recordSDK​
If the recordSDK is supported and the button you’ll attach it to exists in the DOM, call the setup
function with a reference to your public app id.
If you are using an SDK Custom app or key-pair auth with an SDK Standard app, follow the steps here.
The response will be a Promise object which resolves to a configureButton
function.
- React
- TypeScript
- JavaScript
const button = document.getElementById(BUTTON_ID);
if (!button) {
return;
}
const { configureButton } = await setup({
publicAppId: PUBLIC_APP_ID,
});
const sdkButton = configureButton({ element: button });
const button = document.getElementById(BUTTON_ID);
if (!button) {
return;
}
const { configureButton } = await setup({
publicAppId: PUBLIC_APP_ID,
});
const sdkButton = configureButton({ element: button });
const button = document.getElementById(BUTTON_ID);
if (!button) {
return;
}
const { configureButton } = await setup({
publicAppId: PUBLIC_APP_ID,
});
const sdkButton = configureButton({ element: button });
Adding event listeners​
The configureButton
function returns an SDKButton
object which fulfills the NodeJS event emitter definition. In this implementation, the "insert-click"
event is invoked which will be called when the insert CTA is clicked in the post-recording view. Other event listeners include "recording-start"
, "cancel"
, and "complete"
—see full list of listenable events.
- React
- TypeScript
- JavaScript
sdkButton.on("insert-click", async (video) => {
// handle inserting video
});
sdkButton.on("insert-click", async (video) => {
// handle inserting video
});
sdkButton.on("insert-click", async (video) => {
// handle inserting video
});
Inserting the recording​
Use the oembed
method from @loomhq/loom-embed
to fetch a freshly recorded video’s metadata. This object includes an html
attribute of stringified HTML of the iFrame embed player with Loom stylings. When a user completes a recording, the share URL will also be added to their clipboard.
- React
- TypeScript
- JavaScript
const [videoHTML, setVideoHTML] = useState("");
// ...
sdkButton.on("insert-click", async (video) => {
const { html } = await oembed(video.sharedUrl, { width: 400 });
setVideoHTML(html);
});
// ...
<div dangerouslySetInnerHTML={{ __html: videoHTML }}></div>;
function insertEmbedPlayer(html: string) {
const target = document.getElementById("target");
if (target) {
target.innerHTML = html;
}
}
// ...
sdkButton.on("insert-click", async (video) => {
const { html } = await oembed(video.sharedUrl, { width: 400 });
insertEmbedPlayer(html);
});
function insertEmbedPlayer(html) {
const target = document.getElementById("target");
if (target) {
target.innerHTML = html;
}
}
// ...
sdkButton.on("insert-click", async (video) => {
const { html } = await oembed(video.sharedUrl, { width: 400 });
insertEmbedPlayer(html);
});
note
dangerouslySetInnerHTML
is React’s replacement for using innerHTML
in the browser DOM and is named as such because in some cases it can expose users to cross-site scripting (XSS). You don’t need to worry about this when parsing the HTML returned from oembed.html
since this HTML does not contain user input.