@ewjdev/anyclick-reactReact Provider
Drop-in React provider and context menu UI for feedback capture. Handles all event management, UI rendering, and screenshot capture.
Installation
npm install @ewjdev/anyclick-reactRequires React 19+ and includes @ewjdev/anyclick-core as a dependency.
Basic Usage
'use client';
import { AnyclickProvider } from '@ewjdev/anyclick-react';import { createHttpAdapter } from '@ewjdev/anyclick-github';
const adapter = createHttpAdapter({ endpoint: '/api/feedback',});
export function Providers({ children }: { children: React.ReactNode }) { return ( <AnyclickProvider adapter={adapter}> {children} </AnyclickProvider> );}Note: FeedbackProvider is still exported for backward compatibility but is deprecated.
AnyclickProvider Props
adapterrequiredFeedbackAdapterThe adapter to use for submitting feedback. See adapters documentation for available options.
childrenrequiredReactNodeChild components to wrap with the feedback provider.
scopedbooleandefault: falseWhen true, only captures events within this provider's children. Useful for section-specific feedback.
themeAnyclickTheme | nullTheme configuration. Themes inherit from parent providers. Set to null or { disabled: true } to disable feedback.
menuItemsFeedbackMenuItem[]default: [issue, feature, like]Custom menu items to show in the context menu. Each item can have type, label, icon, and children for submenus.
metadataRecord<string, unknown>Additional metadata to include with every feedback submission. Useful for user ID, session ID, etc.
targetFilter(event: MouseEvent, target: Element) => booleanFilter function to determine if feedback should be captured for a target element. Return false to ignore.
highlightConfigHighlightConfigConfiguration for element highlighting behavior and colors.
screenshotConfigScreenshotConfigConfiguration for screenshot capture settings (quality, format, etc.).
maxInnerTextLengthnumberdefault: 500Maximum length for captured inner text content.
maxOuterHTMLLengthnumberdefault: 2000Maximum length for captured outer HTML.
maxAncestorsnumberdefault: 5Maximum number of ancestor elements to capture.
cooldownMsnumberdefault: 1000Cooldown in milliseconds between submissions (rate limiting).
stripAttributesstring[]HTML attributes to strip from captured outerHTML for privacy.
onSubmitSuccess(payload: FeedbackPayload) => voidCallback fired after successful submission.
onSubmitError(error: Error, payload: FeedbackPayload) => voidCallback fired after failed submission.
menuStyleCSSPropertiesCustom inline styles for the context menu.
menuClassNamestringCustom CSS class name for the context menu.
disabledbooleandefault: falseDisable feedback capture entirely.
Scoped Providers
Use the scoped prop to limit feedback capture to a specific section of your app:
import { AnyclickProvider } from '@ewjdev/anyclick-react';
export function Providers({ children }) { return ( <AnyclickProvider adapter={globalAdapter}> <Header /> {/* Scoped provider - only captures events in Dashboard */} <AnyclickProvider adapter={dashboardAdapter} scoped menuItems={dashboardMenuItems} > <Dashboard /> </AnyclickProvider> <Footer /> </AnyclickProvider> );}Nested Theming
Child providers inherit and can override parent themes. Use this for section-specific styling:
<AnyclickProvider adapter={adapter} theme={{ highlightConfig: { colors: { targetColor: '#3b82f6' } // Blue } }}> <MainContent /> {/* Uses blue highlights */} <AnyclickProvider scoped theme={{ highlightConfig: { colors: { targetColor: '#ef4444' } // Red } }} > <WarningSection /> {/* Uses red highlights */} </AnyclickProvider> {/* Disable feedback in sensitive areas */} <AnyclickProvider scoped theme={{ disabled: true }}> <PaymentForm /> </AnyclickProvider></AnyclickProvider>Custom Menu Items
Customize the feedback menu with your own items, icons, and submenus:
import { AnyclickProvider } from '@ewjdev/anyclick-react';import { Bug, Lightbulb, ThumbsUp, Code, Monitor, Cloud } from 'lucide-react';
const menuItems = [ { type: 'bug', label: 'Report Bug', icon: <Bug className="w-4 h-4" />, showComment: true, }, { type: 'feature', label: 'Suggest Feature', icon: <Lightbulb className="w-4 h-4" />, showComment: true, }, { type: 'praise', label: 'This is great!', icon: <ThumbsUp className="w-4 h-4" />, showComment: false, }, // Submenu example { type: 'dev_menu', label: 'Developer Tools', icon: <Code className="w-4 h-4" />, requiredRoles: ['developer', 'admin'], // Role-based visibility children: [ { type: 'cursor_local', label: 'Fix with Cursor (Local)', icon: <Monitor className="w-4 h-4" /> }, { type: 'cursor_cloud', label: 'Fix with Cursor (Cloud)', icon: <Cloud className="w-4 h-4" /> }, ], },];
<AnyclickProvider adapter={adapter} menuItems={menuItems}> {children}</AnyclickProvider>Role-Based Menu Filtering
Show different menu items based on user roles:
import { AnyclickProvider, filterMenuItemsByRole } from '@ewjdev/anyclick-react';import type { FeedbackMenuItem, FeedbackUserContext } from '@ewjdev/anyclick-react';
const allMenuItems: FeedbackMenuItem[] = [ { type: 'issue', label: 'Report Issue', showComment: true }, { type: 'feature', label: 'Request Feature', showComment: true }, // Only visible to developers { type: 'debug', label: 'Debug Info', requiredRoles: ['developer'], },];
function Providers({ children, user }) { const userContext: FeedbackUserContext = { roles: user.roles, id: user.id, email: user.email, }; const menuItems = filterMenuItemsByRole(allMenuItems, userContext); return ( <AnyclickProvider adapter={adapter} menuItems={menuItems} metadata={userContext} > {children} </AnyclickProvider> );}Highlight Configuration
Customize how elements are highlighted when the context menu is open:
<AnyclickProvider adapter={adapter} theme={{ highlightConfig: { enabled: true, colors: { targetColor: '#3b82f6', // Blue for target element containerColor: '#8b5cf6', // Purple for container targetShadowOpacity: 0.25, containerShadowOpacity: 0.1, }, containerSelectors: [ '[data-component]', '[data-section]', '.card', 'section', ], minChildrenForContainer: 2, }, }}> {children}</AnyclickProvider>Screenshot Configuration
Control screenshot capture behavior:
<AnyclickProvider adapter={adapter} theme={{ screenshotConfig: { enabled: true, quality: 0.9, // Selectors to mask for privacy sensitiveSelectors: [ '[data-sensitive]', '.credit-card-input', 'input[type="password"]', ], }, }}> {children}</AnyclickProvider>useAnyclick Hook
Access anyclick context from child components:
import { useAnyclick } from '@ewjdev/anyclick-react';
function MyComponent() { const { isEnabled, // Whether feedback is enabled isSubmitting, // Whether submission is in progress submitFeedback, // Submit feedback programmatically openMenu, // Open menu programmatically closeMenu, // Close menu theme, // Current merged theme scoped, // Whether provider is scoped providerId, // Unique provider ID } = useAnyclick(); // Open menu programmatically const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => { openMenu( event.currentTarget, { x: event.clientX, y: event.clientY } ); }; // Submit feedback programmatically const handleSubmit = async () => { await submitFeedback( document.querySelector('#my-element')!, 'issue', 'This element has a problem' ); }; return ( <button onClick={handleClick} disabled={isSubmitting}> {isSubmitting ? 'Submitting...' : 'Open Feedback'} </button> );}Note: useFeedback is still exported for backward compatibility.
All Exports
// Components (new)export { AnyclickProvider } from './AnyclickProvider';export { ContextMenu } from './ContextMenu';export { ScreenshotPreview } from './ScreenshotPreview';
// Components (deprecated, for backward compatibility)export { FeedbackProvider } from './AnyclickProvider';
// Context & Hooks (new)export { AnyclickContext, useAnyclick } from './context';
// Context & Hooks (deprecated)export { FeedbackContext, useFeedback } from './context';
// Store (for advanced use cases)export { useProviderStore, generateProviderId } from './store';
// Utilitiesexport { filterMenuItemsByRole } from './types';
// Typesexport type { AnyclickProviderProps, AnyclickContextValue, AnyclickTheme, FeedbackMenuItem, FeedbackUserContext, ContextMenuProps, ScreenshotPreviewProps, HighlightConfig, HighlightColors,} from './types';