React Installation

Install Notedis feedback widget in your React application.

Perfect For

  • Create React App
  • Vite + React
  • Next.js (see Next.js guide)
  • Custom React setups
  • React + TypeScript

Installation Methods

Method 1: Script Tag in index.html (Recommended)

The simplest method - add widget to your public/index.html file.

Step 1: Get Your Site Key

  1. Log in to your Notedis dashboard
  2. Navigate to Sites
  3. Click the Edit button on your site (or create a new one)
  4. Copy your Site Key (starts with site_)

Step 2: Add Widget to index.html

Open public/index.html and add the widget code before the closing </body> tag:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>My React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>

    <!-- Notedis Feedback Widget - Add before </body> -->
    <script>
      window.notedisConfig = {
        siteKey: 'your-unique-site-key-here',
        apiUrl: 'https://notedis.com',
        position: 'bottom-right',
        color: '#3B82F6'
      };
    </script>
    <script src="https://notedis.com/js/widget.js" defer></script>

  </body>
</html>

Done! The widget will now appear on all pages.


Method 2: React Component (Advanced)

Create a reusable React component for the widget.

Step 1: Create Widget Component

Create src/components/NotedisFeedback.jsx:

import { useEffect } from 'react';

export default function NotedisFeedback({ siteKey, position = 'bottom-right', color = '#3B82F6' }) {
  useEffect(() => {
    // Set configuration
    window.notedisConfig = {
      siteKey: siteKey,
      apiUrl: 'https://notedis.com',
      position: position,
      color: color
    };

    // Load widget script
    const script = document.createElement('script');
    script.src = 'https://notedis.com/js/widget.js';
    script.defer = true;
    document.body.appendChild(script);

    // Cleanup function
    return () => {
      // Remove script on unmount
      const existingScript = document.querySelector('script[src="https://notedis.com/js/widget.js"]');
      if (existingScript) {
        existingScript.remove();
      }

      // Remove widget button if it exists
      const widgetButton = document.getElementById('notedis-feedback-button');
      if (widgetButton) {
        widgetButton.remove();
      }
    };
  }, [siteKey, position, color]);

  return null; // This component doesn't render anything
}

Step 2: Add Component to App

In your main src/App.js or src/App.jsx:

import NotedisFeedback from './components/NotedisFeedback';

function App() {
  return (
    <div className="App">
      {/* Your app content */}
      <h1>My React App</h1>

      {/* Notedis Feedback Widget */}
      <NotedisFeedback
        siteKey="your-unique-site-key-here"
        position="bottom-right"
        color="#3B82F6"
      />
    </div>
  );
}

export default App;

Method 3: React Helmet (SEO-Friendly)

If you're using react-helmet or react-helmet-async for managing document head:

Step 1: Install React Helmet (if needed)

npm install react-helmet

Step 2: Create Component

Create src/components/NotedisFeedback.jsx:

import { Helmet } from 'react-helmet';

export default function NotedisFeedback({ siteKey, position = 'bottom-right', color = '#3B82F6' }) {
  return (
    <Helmet>
      <script>
        {`
          window.notedisConfig = {
            siteKey: '${siteKey}',
            apiUrl: 'https://notedis.com',
            position: '${position}',
            color: '${color}'
          };
        `}
      </script>
      <script src="https://notedis.com/js/widget.js" defer />
    </Helmet>
  );
}

Step 3: Use in App

import NotedisFeedback from './components/NotedisFeedback';

function App() {
  return (
    <div className="App">
      <NotedisFeedback siteKey="your-site-key-here" />
      {/* Your app content */}
    </div>
  );
}

export default App;

TypeScript Support

If using TypeScript, create src/components/NotedisFeedback.tsx:

import { useEffect } from 'react';

interface NotedisFeedbackProps {
  siteKey: string;
  position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
  color?: string;
  buttonText?: string;
  onOpen?: () => void;
  onClose?: () => void;
  onSubmit?: (feedback: any) => void;
}

declare global {
  interface Window {
    notedisConfig?: {
      siteKey: string;
      apiUrl: string;
      position?: string;
      color?: string;
      buttonText?: string;
      onOpen?: () => void;
      onClose?: () => void;
      onSubmit?: (feedback: any) => void;
    };
    notedisWidget?: {
      open: () => void;
      close: () => void;
    };
  }
}

export default function NotedisFeedback({
  siteKey,
  position = 'bottom-right',
  color = '#3B82F6',
  buttonText,
  onOpen,
  onClose,
  onSubmit
}: NotedisFeedbackProps) {
  useEffect(() => {
    // Set configuration
    window.notedisConfig = {
      siteKey,
      apiUrl: 'https://notedis.com',
      position,
      color,
      ...(buttonText && { buttonText }),
      ...(onOpen && { onOpen }),
      ...(onClose && { onClose }),
      ...(onSubmit && { onSubmit })
    };

    // Load widget script
    const script = document.createElement('script');
    script.src = 'https://notedis.com/js/widget.js';
    script.defer = true;
    document.body.appendChild(script);

    // Cleanup
    return () => {
      const existingScript = document.querySelector('script[src="https://notedis.com/js/widget.js"]');
      if (existingScript) {
        existingScript.remove();
      }

      const widgetButton = document.getElementById('notedis-feedback-button');
      if (widgetButton) {
        widgetButton.remove();
      }
    };
  }, [siteKey, position, color, buttonText, onOpen, onClose, onSubmit]);

  return null;
}

Usage:

<NotedisFeedback
  siteKey="your-site-key"
  position="bottom-right"
  color="#3B82F6"
  onSubmit={(feedback) => console.log('Feedback submitted:', feedback)}
/>

Environment Variables

Store your site key in environment variables for security.

Step 1: Create .env File

Create .env in your project root:

REACT_APP_NOTEDIS_SITE_KEY=your-site-key-here

Important: Never commit your .env file to version control. Add to .gitignore:

.env
.env.local

Step 2: Use Environment Variable

<NotedisFeedback
  siteKey={process.env.REACT_APP_NOTEDIS_SITE_KEY}
/>

Step 3: Different Keys for Dev/Prod

Create multiple env files:

.env.development:

REACT_APP_NOTEDIS_SITE_KEY=site_dev_key

.env.production:

REACT_APP_NOTEDIS_SITE_KEY=site_prod_key

React will automatically use the correct file based on NODE_ENV.


Custom Trigger Button

Hide the default button and use your own custom trigger.

import { useEffect } from 'react';

export default function CustomFeedbackButton() {
  useEffect(() => {
    window.notedisConfig = {
      siteKey: 'your-site-key',
      apiUrl: 'https://notedis.com',
      hideButton: true // Hide default button
    };

    const script = document.createElement('script');
    script.src = 'https://notedis.com/js/widget.js';
    script.defer = true;
    document.body.appendChild(script);
  }, []);

  const openFeedback = () => {
    if (window.notedisWidget) {
      window.notedisWidget.open();
    }
  };

  return (
    <button
      onClick={openFeedback}
      className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700"
    >
      Send Feedback
    </button>
  );
}

React Router Integration

Widget works seamlessly with React Router - button persists across route changes.

import { BrowserRouter, Routes, Route } from 'react-router-dom';
import NotedisFeedback from './components/NotedisFeedback';

function App() {
  return (
    <BrowserRouter>
      <NotedisFeedback siteKey="your-site-key" />

      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/contact" element={<Contact />} />
      </Routes>
    </BrowserRouter>
  );
}

Conditional Loading

Only Show for Authenticated Users

import { useAuth } from './auth'; // Your auth hook
import NotedisFeedback from './components/NotedisFeedback';

function App() {
  const { isAuthenticated } = useAuth();

  return (
    <div className="App">
      {/* Only show widget for logged-in users */}
      {isAuthenticated && (
        <NotedisFeedback siteKey="your-site-key" />
      )}

      {/* Your app content */}
    </div>
  );
}

Only Show in Development

function App() {
  const isDevelopment = process.env.NODE_ENV === 'development';

  return (
    <div className="App">
      {/* Only show in development */}
      {isDevelopment && (
        <NotedisFeedback
          siteKey="your-dev-site-key"
          color="#ef4444" // Red color for dev
          buttonText="🚧 Dev Feedback"
        />
      )}
    </div>
  );
}

Hide on Specific Routes

import { useLocation } from 'react-router-dom';
import NotedisFeedback from './components/NotedisFeedback';

function App() {
  const location = useLocation();

  // Hide on checkout or admin pages
  const hideWidget = ['/checkout', '/admin'].some(path =>
    location.pathname.startsWith(path)
  );

  return (
    <div className="App">
      {!hideWidget && (
        <NotedisFeedback siteKey="your-site-key" />
      )}
    </div>
  );
}

Analytics Integration

Track feedback events with your analytics:

import { useEffect } from 'react';

export default function NotedisFeedback({ siteKey }) {
  useEffect(() => {
    window.notedisConfig = {
      siteKey: siteKey,
      apiUrl: 'https://notedis.com',

      onOpen: () => {
        // Track with Google Analytics
        if (window.gtag) {
          window.gtag('event', 'feedback_opened', {
            event_category: 'engagement'
          });
        }
      },

      onSubmit: (feedback) => {
        // Track with Google Analytics
        if (window.gtag) {
          window.gtag('event', 'feedback_submitted', {
            event_category: 'engagement',
            category: feedback.category,
            priority: feedback.priority
          });
        }
      }
    };

    const script = document.createElement('script');
    script.src = 'https://notedis.com/js/widget.js';
    script.defer = true;
    document.body.appendChild(script);
  }, [siteKey]);

  return null;
}

Testing

The widget won't interfere with your tests. However, if you want to mock it:

// src/setupTests.js
window.notedisConfig = {};
window.notedisWidget = {
  open: jest.fn(),
  close: jest.fn()
};

Troubleshooting

Widget doesn't appear

  1. Check browser console (F12) for JavaScript errors
  2. Verify site key is correct
  3. Clear cache and hard refresh (Ctrl+F5 or Cmd+Shift+R)
  4. Check component mounting - ensure component is rendering

Widget appears multiple times

Cause: Component mounting multiple times in development mode.

Solution: This is expected with React 18 Strict Mode in development. It won't happen in production.

To disable in development, add this check:

useEffect(() => {
  // Prevent duplicate loading
  if (document.querySelector('script[src="https://notedis.com/js/widget.js"]')) {
    return;
  }

  // Load widget...
}, []);

Widget not working with hot reload

Cause: Hot module replacement may not reload widget properly.

Solution: Full page refresh (F5) to reload widget.


Performance Notes

  • Widget loads asynchronously with defer attribute
  • No impact on initial page load or PageSpeed scores
  • Lazy loads only when needed
  • Minimal bundle size impact (CDN-based)

Next Steps