Getting started
The loomSDK is an umbrella term which refers to the recordSDK and embedSDK. The embedSDK can be used independently of the recordSDK.
Installation​
Install the recordSDK and embedSDK.
$ npm install @loomhq/record-sdk @loomhq/loom-embed
Set up your recordSDK application​
To obtain an SDK application set up your developer account.
SDK Standard​
For the 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.
Implement​
The code below is all you need to set up an SDK Standard app. SDK 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 a key-pair with your SDK 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.