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
- Log in to your Notedis dashboard
- Navigate to Sites
- Click the Edit button on your site (or create a new one)
- 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
- Check browser console (F12) for JavaScript errors
- Verify site key is correct
- Clear cache and hard refresh (Ctrl+F5 or Cmd+Shift+R)
- 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
deferattribute - No impact on initial page load or PageSpeed scores
- Lazy loads only when needed
- Minimal bundle size impact (CDN-based)
Next Steps
- Configuration - Customize widget appearance and behavior
- Customization - Advanced styling and custom triggers
- Features - Explore all widget features
- Troubleshooting - Common issues and solutions