Custom Integrations

Integrate the widget with custom triggers, frameworks, and analytics.

Custom trigger buttons

By default, the SDK renders a floating button in the corner of your page. If you want full control over the trigger, set trigger: 'custom' during initialization. The built-in button will not be rendered.

1window.uj.init('proj_a1b2c3d4', {
2 widget: true,
3 trigger: 'custom', // Disables the default floating button
4});

Then attach your own trigger to any element:

1document.getElementById('feedback-btn').addEventListener('click', () => {
2 window.uj.showWidget({ section: 'feedback' });
3});

You can open directly to any section depending on where in your app the user triggers the widget:

1// From a "Report a bug" link

// From a "See what's new" link changelogLink.addEventListener('click', () => window.uj.showWidget({ section: 'updates' })); `

React hook

Encapsulate the SDK in a custom hook to make it easy to use throughout your React application.

1// hooks/useJotReview.ts
declare global {
uj: {
init: (id: string, opts?: Record<string, unknown>) => void;
identify: (user: { id: string; email?: string }null) => void;
showWidget: (opts?: { section?: string }) => void;
hideWidget: () => void;
};
}
}

export function useJotReview(projectId: string) { useEffect(() => { window.uj.init(projectId, { widget: true, trigger: 'custom' }); }, [projectId]);

const identify = useCallback((user: { id: string; email?: string }null) => {
}, []);

      return { identify, openFeedback, openRoadmap }; } `

      Usage in a component:

      1export function AppHeader({ user }: { user: User }) {

        return ( <header> <button onClick={openFeedback}>Give Feedback</button> </header> ); } `

        Vue component

        Use a composable and a setup script for clean Vue 3 integration.

        1// composables/useJotReview.ts

        export function useJotReview(projectId: string) { onMounted(() => { window.uj.init(projectId, { widget: true, trigger: 'custom' }); });

        function identify(user: { id: string; email?: string }null) {
        }
        function showWidget(section?: 'feedback''roadmap''updates') {
        }

        return { identify, showWidget }; } `

        1<!-- FeedbackButton.vue -->
        2<script setup lang="ts">
        3import { useJotReview } from '@/composables/useJotReview';

        const auth = useAuthStore(); const { identify, showWidget } = useJotReview('proj_a1b2c3d4');

        identify({ id: auth.user.id, email: auth.user.email }); </script>

        <template> <button @click="showWidget('feedback')" class="feedback-btn"> Share Feedback </button> </template> `

        Angular component

        Create an Angular service to wrap the SDK and inject it where needed.

        1// jotreview.service.ts

        declare global { interface Window { uj: Record<string, (...args: unknown[]) => void>; } }

        @Injectable({ providedIn: 'root' }) export class JotReviewService { init(projectId: string) { window.uj.init(projectId, { widget: true, trigger: 'custom' }); }

        identify(user: { id: string; email?: string }null) {
        }
        showWidget(section?: 'feedback''roadmap''updates') {
        }
        }
        `
        1// app.component.ts
        2import { Component, OnInit } from '@angular/core';
        3import { JotReviewService } from './jotreview.service';

        @Component({ selector: 'app-root', templateUrl: './app.component.html' }) export class AppComponent implements OnInit { constructor( private jr: JotReviewService, private auth: AuthService ) {}

          openFeedback() { this.jr.showWidget('feedback'); } } `

          Analytics integration

          Track widget interactions alongside your existing analytics by combining JotReview with tools like Segment, Mixpanel, or PostHog.

          The recommended pattern is to fire your analytics event first, then open the widget:

          1document.getElementById('feedback-btn').addEventListener('click', () => {
          2 // Track in your analytics platform
          3 analytics.track('Feedback Widget Opened', {
          4 page: window.location.pathname,
          5 userId: currentUser.id,

          // Open the widget window.uj.showWidget({ section: 'feedback' }); }); `

          For Segment users, you can combine identification:

          1analytics.identify(user.id, {
          2 email: user.email,
          3 name: user.name,

          // Mirror the identity to JotReview window.uj.identify({ id: user.id, email: user.email, firstName: user.firstName, lastName: user.lastName, }); `

          Keyboard shortcut

          Add a keyboard shortcut (e.g., Cmd+Shift+F) to open the widget without interrupting the user's flow.

          1window.addEventListener('keydown', (e) => {
          2 // Cmd+Shift+F on Mac, Ctrl+Shift+F on Windows/Linux
          3 if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === 'F') {
          4 e.preventDefault();
          5 window.uj.showWidget({ section: 'feedback' });
          6 }
          7});

          You can also expose a Cmd+K command palette entry:

          1// Example with cmdk or kbar
          2const feedbackCommand = {
          3 id: 'feedback',
          4 name: 'Share Feedback',
          5 shortcut: ['$mod', 'shift', 'f'],
          6 perform: () => window.uj.showWidget({ section: 'feedback' }),
          7};

          Conditional display by user segment

          Show or hide the widget based on user properties, such as their plan, role, or account age.

          1

          function initForUser(user) { window.uj.identify({ id: user.id, email: user.email, });

          // Only show the widget to paying customers
          const isEligible = user.plan !== 'free'new Date(user.createdAt) < thirtyDaysAgo;
          window.uj.setWidgetEnabled(isEligible);
          }
          `

          You can also show different sections based on the user's role:

          1document.getElementById('feedback-trigger').addEventListener('click', () => {
          2 // Admins skip directly to the roadmap
          3 const section = user.role === 'admin' ? 'roadmap' : 'feedback';
          4 window.uj.showWidget({ section });
          5});