@ewjdev/anyclick-react

React 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-react

Requires React 19+ and includes @ewjdev/anyclick-core as a dependency.

Basic Usage

app/providers.tsx
'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

adapterrequiredFeedbackAdapter

The adapter to use for submitting feedback. See adapters documentation for available options.

childrenrequiredReactNode

Child components to wrap with the feedback provider.

scopedbooleandefault: false

When true, only captures events within this provider's children. Useful for section-specific feedback.

themeAnyclickTheme | null

Theme 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) => boolean

Filter function to determine if feedback should be captured for a target element. Return false to ignore.

highlightConfigHighlightConfig

Configuration for element highlighting behavior and colors.

screenshotConfigScreenshotConfig

Configuration for screenshot capture settings (quality, format, etc.).

maxInnerTextLengthnumberdefault: 500

Maximum length for captured inner text content.

maxOuterHTMLLengthnumberdefault: 2000

Maximum length for captured outer HTML.

maxAncestorsnumberdefault: 5

Maximum number of ancestor elements to capture.

cooldownMsnumberdefault: 1000

Cooldown in milliseconds between submissions (rate limiting).

stripAttributesstring[]

HTML attributes to strip from captured outerHTML for privacy.

onSubmitSuccess(payload: FeedbackPayload) => void

Callback fired after successful submission.

onSubmitError(error: Error, payload: FeedbackPayload) => void

Callback fired after failed submission.

menuStyleCSSProperties

Custom inline styles for the context menu.

menuClassNamestring

Custom CSS class name for the context menu.

disabledbooleandefault: false

Disable feedback capture entirely.

Scoped Providers

Use the scoped prop to limit feedback capture to a specific section of your app:

app/layout.tsx
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:

app/providers.tsx
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';
// Utilities
export { filterMenuItemsByRole } from './types';
// Types
export type {
AnyclickProviderProps,
AnyclickContextValue,
AnyclickTheme,
FeedbackMenuItem,
FeedbackUserContext,
ContextMenuProps,
ScreenshotPreviewProps,
HighlightConfig,
HighlightColors,
} from './types';

Related