Skip to main content

Web Integration

The easiest way to add live classrooms to your web app is through iframe embedding. You get the full classroom experience—video, controls, everything—with minimal code.

Basic Embedding

Once you have a join URL (from the Create Room API), embed it like this:

<iframe
id="verriflo-classroom"
src="https://live.verriflo.com/iframe/live?token=YOUR_TOKEN"
allow="camera; microphone; fullscreen; display-capture"
allowfullscreen
style="width: 100%; height: 600px; border: none;"
></iframe>

That's it. The classroom will load with all controls and functionality.

Required Permissions

The allow attribute is important. Here's what each permission does:

PermissionPurpose
cameraFor instructor video
microphoneFor instructor audio
fullscreenFor fullscreen mode
display-captureFor screen sharing

Without these, some features won't work.

Handling Events

The classroom sends events to the parent page via postMessage. Set up a listener:

window.addEventListener("message", (event) => {
// Security check
if (!event.origin.includes("verriflo.com")) return;

const { type, action } = event.data;

// Handle classroom lifecycle
if (type === "verriflo_classroom") {
switch (action) {
case "class_ended":
// Teacher ended the class
handleClassEnded();
break;
case "participant_removed":
// User was kicked
handleKicked();
break;
case "participant_left":
case "close":
// User left voluntarily
handleUserLeft();
break;
}
}

// Handle fullscreen requests
if (type === "verriflo_fullscreen") {
const iframe = document.getElementById("verriflo-classroom");
if (action === "enter") {
iframe.requestFullscreen();
} else if (action === "exit") {
document.exitFullscreen();
}
}
});

See Iframe Events for the complete event reference.

Responsive Sizing

Fixed Aspect Ratio (16:9)

For video content, maintaining aspect ratio looks best:

.classroom-container {
position: relative;
width: 100%;
padding-bottom: 56.25%; /* 16:9 aspect ratio */
}

.classroom-container iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: none;
}
<div class="classroom-container">
<iframe
src="https://live.verriflo.com/iframe/live?token=YOUR_TOKEN"
allow="camera; microphone; fullscreen; display-capture"
allowfullscreen
></iframe>
</div>

Full Height

Fill the available viewport:

.classroom-fullheight {
width: 100%;
height: calc(100vh - 64px); /* Account for your navbar */
border: none;
}

Mobile Responsive

Adjust for different screen sizes:

.classroom-responsive {
width: 100%;
height: 400px;
border: none;
}

@media (min-width: 768px) {
.classroom-responsive {
height: 500px;
}
}

@media (min-width: 1024px) {
.classroom-responsive {
height: 600px;
}
}

Modal/Dialog Pattern

Common pattern: open the classroom in a modal when a user clicks "Join Class":

<!-- Trigger button -->
<button onclick="openClassroom()">Join Live Class</button>

<!-- Modal -->
<div id="classroom-modal" class="modal hidden">
<div class="modal-content">
<button onclick="closeClassroom()" class="close-btn">×</button>
<iframe
id="classroom-iframe"
allow="camera; microphone; fullscreen; display-capture"
allowfullscreen
></iframe>
</div>
</div>

<style>
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}

.modal.hidden {
display: none;
}

.modal-content {
position: relative;
width: 90%;
max-width: 1200px;
height: 80vh;
background: #000;
border-radius: 12px;
overflow: hidden;
}

.modal-content iframe {
width: 100%;
height: 100%;
border: none;
}

.close-btn {
position: absolute;
top: 10px;
right: 10px;
z-index: 10;
background: rgba(0, 0, 0, 0.5);
color: white;
border: none;
width: 36px;
height: 36px;
border-radius: 50%;
font-size: 24px;
cursor: pointer;
}
</style>

<script>
async function openClassroom() {
// Get token from your backend
const response = await fetch("/api/join-class", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
roomId: "current-class-id",
studentName: "Current User",
studentEmail: "user@example.com",
}),
});

const { iframeUrl } = data.data;

// Set iframe source and show modal
document.getElementById("classroom-iframe").src = iframeUrl;
document.getElementById("classroom-modal").classList.remove("hidden");
}

function closeClassroom() {
document.getElementById("classroom-modal").classList.add("hidden");
document.getElementById("classroom-iframe").src = "";
}

// Listen for classroom events
window.addEventListener("message", (event) => {
if (!event.origin.includes("verriflo.com")) return;

if (event.data.type === "verriflo_classroom") {
// Auto-close modal when class ends or user leaves
closeClassroom();

if (event.data.action === "class_ended") {
alert("Class has ended!");
}
}
});
</script>

React Integration

import { useEffect, useRef, useState } from "react";

function VerrifloClassroom({ token, onClassEnded, onKicked }) {
const iframeRef = useRef(null);

useEffect(() => {
function handleMessage(event) {
if (!event.origin.includes("verriflo.com")) return;

const { type, action } = event.data;

if (type === "verriflo_classroom") {
if (action === "class_ended") onClassEnded?.();
if (action === "participant_removed") onKicked?.();
}

if (type === "verriflo_fullscreen") {
if (action === "enter") {
iframeRef.current?.requestFullscreen();
} else {
document.exitFullscreen();
}
}
}

window.addEventListener("message", handleMessage);
return () => window.removeEventListener("message", handleMessage);
}, [onClassEnded, onKicked]);

return (
<iframe
ref={iframeRef}
src={`https://live.verriflo.com/iframe/live?token=${token}`}
allow="camera; microphone; fullscreen; display-capture"
allowFullScreen
style={{
width: "100%",
height: "600px",
border: "none",
borderRadius: "8px",
}}
/>
);
}

// Usage
function ClassPage() {
const [token, setToken] = useState(null);

useEffect(() => {
// Fetch token from your backend
fetchJoinToken().then(setToken);
}, []);

if (!token) return <div>Loading...</div>;

return (
<VerrifloClassroom
token={token}
onClassEnded={() => {
alert("Class ended!");
window.location.href = "/feedback";
}}
onKicked={() => {
alert("You were removed from the class");
window.location.href = "/dashboard";
}}
/>
);
}

Next.js Integration

"use client";

import { useEffect, useRef } from "react";
import { useRouter } from "next/navigation";

interface ClassroomProps {
token: string;
}

export function Classroom({ token }: ClassroomProps) {
const iframeRef = useRef<HTMLIFrameElement>(null);
const router = useRouter();

useEffect(() => {
const handleMessage = (event: MessageEvent) => {
if (!event.origin.includes("verriflo.com")) return;

const { type, action } = event.data;

if (type === "verriflo_classroom") {
switch (action) {
case "class_ended":
router.push("/class-ended");
break;
case "participant_removed":
router.push("/dashboard?removed=true");
break;
case "participant_left":
case "close":
router.back();
break;
}
}

if (type === "verriflo_fullscreen") {
if (action === "enter") {
iframeRef.current?.requestFullscreen();
} else {
document.exitFullscreen();
}
}
};

window.addEventListener("message", handleMessage);
return () => window.removeEventListener("message", handleMessage);
}, [router]);

return (
<div className="aspect-video w-full">
<iframe
ref={iframeRef}
src={`https://live.verriflo.com/iframe/live?token=${token}`}
allow="camera; microphone; fullscreen; display-capture"
allowFullScreen
className="w-full h-full border-0 rounded-lg"
/>
</div>
);
}

Troubleshooting

Iframe Blocked by Browser

Some browsers block iframes with camera/microphone access if they're on a different origin. Make sure you're serving your page over HTTPS.

Fullscreen Not Working

The fullscreen API requires user interaction. It won't work if triggered automatically on page load.

Events Not Firing

  1. Check your origin verification logic
  2. Make sure the iframe has loaded before listening
  3. Check browser console for errors

Safari Issues

Safari is more restrictive. Ensure:

  • You're on HTTPS
  • allow attribute includes camera; microphone
  • User has granted permissions at the OS level

Next: Backend Integration — Server-side setup and best practices.