Iframe Embed

Embed the JotReview board directly inside your application.

Basic setup

The embed mode renders your full JotReview board inline inside any element on your page, without a floating button or overlay panel. This is ideal for dedicated feedback pages inside your application.

First, add a container element where the embed should appear:

1<div
2 id="jotreview-embed"
3 data-jotreview-embed
4 style="height: 600px; width: 100%;"
5></div>

Then initialize the SDK and call embed():

1window.uj.init('proj_a1b2c3d4', {
2 widget: false, // Disable the floating button

window.uj.embed({ container: '#jotreview-embed', theme: 'auto', navbar: true, }); `

Initialize inside onReady

If you need to pass dynamic data (like the current user's identity) before rendering the embed, initialize inside the onReady callback to ensure the SDK is fully loaded:

1window.uj.init('proj_a1b2c3d4', {
2 widget: false,
3 onReady: () => {
4 // Identify the user first
5 window.uj.identify({
6 id: currentUser.id,
7 email: currentUser.email,

// Then mount the embed window.uj.embed({ container: '#jotreview-embed', path: '/roadmap', }); }, }); `

Configuration options

Full reference for the embed() options object:

OptionTypeDefaultDescription
container`HTMLElement \string`Required. Target element or CSS selector
pathstring'/'Initial route to load (e.g. '/roadmap', '/updates')
theme`'light' \'dark' \'auto'`'auto'Color theme
navbarbooleantrueShow or hide the board's internal navigation bar
basePathstringundefinedPath prefix in your app (for URL syncing, see below)
onReady(ctrl) => voidundefinedFires once the embed is mounted
onError(err) => voidundefinedFires if the embed fails to load

Example with all options:

1const embed = window.uj.embed({
2 container: document.getElementById('jotreview-embed'),
3 path: '/roadmap',
4 theme: 'light',
5 navbar: false,
6 basePath: '/app/feedback',
7 onReady: (ctrl) => console.log('Embed ready'),
8 onError: (err) => console.error('Embed error:', err),
9});

URL syncing with basePath

When a user navigates within the embedded board (e.g., from Feedback to Roadmap), you can keep your app's URL in sync by providing a basePath.

For example, if your app hosts the embed at /app/feedback, set:

1window.uj.embed({
2 container: '#jotreview-embed',
3 basePath: '/app/feedback',
4});

The SDK will push navigation events to the browser's history API: - /app/feedback/ — Feedback tab - /app/feedback/roadmap — Roadmap tab - /app/feedback/updates — Updates tab

On page load, the embed reads the current URL and restores the correct tab automatically.

Programmatic navigation

The EmbedController returned by embed() has a navigate() method to change tabs or sections from outside the embed:

1const embed = window.uj.embed({
2 container: '#jotreview-embed',

// Navigate to roadmap document.getElementById('go-to-roadmap').addEventListener('click', () => { embed.navigate('/roadmap'); });

// Navigate to a specific feedback post embed.navigate('/posts/feature-dark-mode');

// Navigate back to the root embed.navigate('/'); `

User authentication in embed

Identify users before or after mounting the embed. If you call identify() after the embed is already mounted, it will update the session in place without a full reload.

1// Identify before mounting
2window.uj.identify({ id: user.id, email: user.email });

// Or update identity after the user logs in authButton.addEventListener('click', async () => { const user = await loginUser(); window.uj.identify({ id: user.id, email: user.email }); });

// Log out logoutButton.addEventListener('click', () => { window.uj.identify(null); // Returns to anonymous mode }); `

Server configuration for catch-all routes

If you are using URL syncing with basePath, your server must serve the same page for all sub-paths under the base path. Otherwise, a direct visit to /app/feedback/roadmap will return a 404.

Next.js App Router — Create a catch-all route:

1src/app/app/feedback/[[...path]]/page.tsx
1// This page renders for /app/feedback, /app/feedback/roadmap, etc.
2export default function FeedbackPage() {
3 return <div id="jotreview-embed" style={{ height: '100%' }} />;
4}

Vite / React Router — Add a wildcard route:

1// router.tsx

const routes = [ { path: '/app/feedback/*', element: <FeedbackPage /> }, ]; `

Express — Serve your SPA for all sub-paths:

1app.get('/app/feedback/*', (req, res) => {
2 res.sendFile(path.join(__dirname, 'public', 'index.html'));
3});