@birdie-so/snippet
v1.3.1
Published
Helper for integrating the Birdie screen recording snippet into modern JavaScript apps. Requires a Birdie account.
Maintainers
Readme
@birdie-so/snippet
Easily integrate the Birdie screen recording snippet into your application using modern frameworks like React, Vue, Angular, or plain JavaScript — with full control over initialization, metadata, and event hooks.
⚡ This package is a helper utility for integrating Birdie.
You must have a Birdie account and a validclientIdto use this package.
It does not work on its own — visit https://app.birdie.so to get started.
⚡ The core logic is still loaded from our CDN. This package is just a light wrapper for easier integration and customization.
🚀 Installation
npm install @birdie-so/snippet
# or
yarn add @birdie-so/snippet✨ Quick Start
React / Vue / Angular / JS
import { initBirdie } from "@birdie-so/snippet";
initBirdie({
clientId: "YOUR_CLIENT_ID", // required
contact: {
email: "[email protected]" // required to get logs capture
}
// Optional metadata available to recordings
metadata: {
user: {
id: "123",
email: "[email protected]",
},
},
// Optional setting to remove response bodies when collecting logs
settings: {
privacy: {
mask_response_body: true
}
},
// Optional hook once Birdie is ready
onReady(birdie) {
// you can register for the following events
birdie.on("recorderOpen", (data) => {
console.log("Recorder tab is opened", data);
});
birdie.on("start", (data) => {
console.log("Recording started", data);
birdie.metadata = { dynamicKey: "value" };
});
birdie.on("pause", (data) => {
console.log("Recording paused", data);
});
birdie.on("resume", (data) => {
console.log("Recording resumed", data);
});
birdie.on("stop", (data) => {
console.log("Recording stopped", data);
});
birdie.on("restart", (data) => {
console.log("Recording restarted", data);
});
birdie.on("captureStarted", (data) => {
console.log("Capturing logs started", data);
});
birdie.on("captureStopped", (data) => {
console.log("Capturing logs stopped", data);
});
birdie.on("recordingSent", (data) => {
// data.link contains the link to the video
console.log("A new recording has been sent", data);
});
birdie.on("recorderClose", (data) => {
console.log("Recorder tab was closed", data);
});
birdie.on("error", (error) => {
console.log("Something went wrong", error);
});
// you can also update data after initialization
birdie.update({
contact: {
email: "[email protected]"
},
metadata: {
parameter: "value"
}
});
}
});🧠 How It Works
This package:
- Injects the Birdie CDN snippet dynamically using your
clientId. - Sets global
window.birdieSettingsbefore loading. - Registers event callbacks once the Birdie SDK is ready (
window.birdieis available).
Your original snippet like this:
<script>
window.birdieSettings = {
/* ... */
};
</script>
<script src="https://cf.birdie.so/widget/embed/v1/YOUR_CLIENT_ID"
integrity="sha384-UpHM1+1SRGw5d/lkhatup8ShhdrItyJ50qC2whsmcKcl9Xzubnhc+Vq5TXcP8VnJ"
crossorigin="anonymous"></script>Is now handled via code with initBirdie().
🎬 Recorder launcher (Web SDK)
To open the Birdie recorder from a button, link or element of your own — a
support form, a help center — add the launcher option. It loads the Web SDK
(a separate, signed script from cdn.birdie.so/websdk/) next to the snippet.
Nothing launcher-related is loaded when the option is absent.
initBirdie({
clientId: "YOUR_CLIENT_ID",
contact: { email: "[email protected]" },
launcher: true,
// or: launcher: { trigger: "#record-button", tag: "support-form", autoClose: true, chip: true }
onReady(birdie) {
birdie.on("recorderOpen", () => console.log("recorder window opened"));
birdie.on("start", () => console.log("recording started"));
birdie.on("stop", () => console.log("recording stopped"));
birdie.on("recordingSent", (d) => console.log("sharing link:", d.link));
birdie.on("recorderClose", () => console.log("recorder window closed"));
birdie.launcher.open(); // also close(), toggle(), opened(), closeRecorder()
},
});Any element with the class birdieWebsdk_open opens the recorder when clicked;
trigger adds a selector of your own. While the recorder window is open, a small
chip bottom right ("Recorder open", "Recording", "Sending") brings it back to
front; it appears only in browsers where the recorder's floating controls cannot
open (Safari), chip: true shows it everywhere, chip: false never.
onReady fires as soon as either the snippet runtime or the launcher is ready,
so it works on a page where only the launcher ends up running (a help center on a
domain the snippet is not authorized for). Events from the launcher carry
source: "websdk"; on a page running both, the same recording can be reported
by each, a second apart.
With launcher set, window.birdie exists from the moment initBirdie()
returns, so birdie.on(...) can be called at once; getBirdieInstance() returns
null until a runtime is actually ready. Without it, nothing changes: the
snippet runtime defines window.birdie as before.
📱 Mobile devices
Birdie does not record on phones or tablets. Since 1.2.3, initBirdie() detects
those devices up front (same rule as the Birdie runtime: iPhone/iPod, Android phones,
iPad, Windows Phone/BlackBerry, and browsers announcing userAgentData.mobile) and
does not load the snippet at all — a mobile page view makes zero requests to Birdie.
Your integration code needs no device check of its own: on mobile, window.birdie
is still provided as an inert object with the same surface (on, off, emit,
update, setContact, setMetadata, contact, metadata, settings, and a
launcher whose open() emits an error event), and
onReady / getBirdieInstance() callbacks still fire. Events simply never occur,
because nothing records. The inert object carries a non-enumerable
__birdieMobileStub: true if you ever need to tell it apart.
Android tablets and desktop browsers behave exactly as before.
🔒 Subresource Integrity (SRI)
initBirdie() loads the Birdie loader from a versioned, immutable URL and pins
it with Subresource Integrity. The browser hashes the file it receives and refuses
to execute it unless the hash matches, so the loader — which carries the public key
that verifies Birdie's signed manifest — cannot be swapped, even by someone who
controls the host serving it.
The pin ships inside this package, which your lockfile already pins by content, so the trust anchor does not come from the host it protects.
| Loader version | URL | Integrity (sha384) |
| --- | --- | --- |
| v1 | https://cf.birdie.so/widget/embed/v1/{CLIENT_ID} | sha384-UpHM1+1SRGw5d/lkhatup8ShhdrItyJ50qC2whsmcKcl9Xzubnhc+Vq5TXcP8VnJ |
Verify it yourself at any time:
curl -s https://cf.birdie.so/widget/embed/v1/YOUR_CLIENT_ID \
| openssl dgst -sha384 -binary | openssl base64 -ASnippet updates do not require re-pinning. New Birdie versions ship through the
signed manifest that the loader verifies at runtime; the loader itself changes
rarely, and when it does we publish a new version (v2, …) while v1 keeps being
served — so an application pinned to v1 keeps working until it upgrades this
package.
SRI is optional: if your setup cannot use it, remove the integrity and
crossorigin attributes from the tag above (or load the unversioned URL). The
manifest signature check still applies either way.
🧩 Advanced
Get Birdie instance later
import { getBirdieInstance } from "@birdie-so/snippet";
getBirdieInstance((birdie) => {
birdie.on("start", () => {
console.log("Recording started!");
});
});Or synchronously:
const birdie = getBirdieInstance();
if (birdie) {
birdie.metadata = { key: "value" };
}📘 Docs
For full documentation and integration examples, visit our docs page
🛠 Support
Need help? Reach out to us at [email protected]
📄 License
MIT
