Add shadcn registry component#445
Conversation
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
commit: |
| ...props.options, | ||
| }); | ||
|
|
||
| reactGrab.setGlobalApi(api); |
There was a problem hiding this comment.
Concurrent init overwrites global API
High Severity
Overlapping loadReactGrab runs can both pass the getGlobalApi() check before either calls setGlobalApi. The second init() then returns a noop API because hasInited is already true, and setGlobalApi may replace the working global instance with that noop, leaving React Grab inactive.
Reviewed by Cursor Bugbot for commit 094ba5b. Configure here.
| ...props.options, | ||
| }); | ||
|
|
||
| reactGrab.setGlobalApi(api); |
There was a problem hiding this comment.
Pending plugins never flushed
Medium Severity
After manual init() and setGlobalApi(), the component never flushes plugins that were queued via registerPlugin() while the global API was still null. The default package import path runs flushPendingPlugins on auto-init, but this lazy-init path skips that step.
Reviewed by Cursor Bugbot for commit 094ba5b. Configure here.
| if (!isActive) return; | ||
|
|
||
| const existingApi = reactGrab.getGlobalApi(); | ||
| if (existingApi) return; |
There was a problem hiding this comment.
Updated options prop ignored
Medium Severity
The effect depends on props.options, but when getGlobalApi() is already set it returns early and never merges new options. Re-renders with different options therefore leave the original configuration in place.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 094ba5b. Configure here.
| }); | ||
|
|
||
| reactGrab.setGlobalApi(api); | ||
| window.dispatchEvent(new CustomEvent("react-grab:init", { detail: api })); |
There was a problem hiding this comment.
Noop init published globally
High Severity
When init() returns a noop API (for example options.enabled === false), the component still calls setGlobalApi and dispatches react-grab:init. That noop is truthy, so later runs bail on getGlobalApi() and never install a real instance even if options change.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 094ba5b. Configure here.
|
|
||
| export const ReactGrab = (props: ReactGrabProps) => { | ||
| useEffect(() => { | ||
| if (props.enabled === false) return; |
There was a problem hiding this comment.
Enabled prop never deactivates
Medium Severity
Setting enabled={false} only skips starting a new load; it does not disable or dispose an already-initialized React Grab instance. After the prop becomes false, the overlay and listeners keep running.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 094ba5b. Configure here.
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
There was a problem hiding this comment.
5 issues found across 3 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="registry/react-grab.tsx">
<violation number="1" location="registry/react-grab.tsx:29">
P2: When `enabled` becomes false, disable the existing global API instead of returning immediately; otherwise an already initialized instance keeps running.</violation>
<violation number="2" location="registry/react-grab.tsx:35">
P2: The `import("react-grab")` call is inside a `try` block but there is no `catch` clause. If the dynamic import fails (e.g., package not installed, network error in dev), the error is silently swallowed with no console warning or user feedback, making debugging difficult.</violation>
<violation number="3" location="registry/react-grab.tsx:41">
P2: Existing global instance short-circuits the effect, so `enabled`/`options` prop changes are ignored after first init.</violation>
<violation number="4" location="registry/react-grab.tsx:43">
P1: Guard against `options.enabled === false` before calling `init()`. Initializing with disabled options can produce a no-op API and block later creation of a real instance.</violation>
<violation number="5" location="registry/react-grab.tsx:60">
P3: `props.options` is an object included in the `useEffect` dependency array. If the parent passes an inline object literal (e.g., `options={{ activationMode: 'toggle' }}`), the reference changes every render, causing the effect to fire on every render — re-importing the module, checking the global API, and running through the try/finally block each time.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| const api = reactGrab.init({ | ||
| ...reactGrabOptions, | ||
| ...props.options, | ||
| }); |
There was a problem hiding this comment.
P1: Guard against options.enabled === false before calling init(). Initializing with disabled options can produce a no-op API and block later creation of a real instance.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At registry/react-grab.tsx, line 43:
<comment>Guard against `options.enabled === false` before calling `init()`. Initializing with disabled options can produce a no-op API and block later creation of a real instance.</comment>
<file context>
@@ -0,0 +1,63 @@
+ const existingApi = reactGrab.getGlobalApi();
+ if (existingApi) return;
+
+ const api = reactGrab.init({
+ ...reactGrabOptions,
+ ...props.options,
</file context>
| const api = reactGrab.init({ | |
| ...reactGrabOptions, | |
| ...props.options, | |
| }); | |
| if (props.options?.enabled === false) return; | |
| const api = reactGrab.init({ | |
| ...reactGrabOptions, | |
| ...props.options, | |
| }); |
|
|
||
| const loadReactGrab = async () => { | ||
| window.__REACT_GRAB_DISABLED__ = true; | ||
| try { |
There was a problem hiding this comment.
P2: The import("react-grab") call is inside a try block but there is no catch clause. If the dynamic import fails (e.g., package not installed, network error in dev), the error is silently swallowed with no console warning or user feedback, making debugging difficult.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At registry/react-grab.tsx, line 35:
<comment>The `import("react-grab")` call is inside a `try` block but there is no `catch` clause. If the dynamic import fails (e.g., package not installed, network error in dev), the error is silently swallowed with no console warning or user feedback, making debugging difficult.</comment>
<file context>
@@ -0,0 +1,63 @@
+
+ const loadReactGrab = async () => {
+ window.__REACT_GRAB_DISABLED__ = true;
+ try {
+ const reactGrab = await import("react-grab");
+
</file context>
| if (!isActive) return; | ||
|
|
||
| const existingApi = reactGrab.getGlobalApi(); | ||
| if (existingApi) return; |
There was a problem hiding this comment.
P2: Existing global instance short-circuits the effect, so enabled/options prop changes are ignored after first init.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At registry/react-grab.tsx, line 41:
<comment>Existing global instance short-circuits the effect, so `enabled`/`options` prop changes are ignored after first init.</comment>
<file context>
@@ -0,0 +1,63 @@
+ if (!isActive) return;
+
+ const existingApi = reactGrab.getGlobalApi();
+ if (existingApi) return;
+
+ const api = reactGrab.init({
</file context>
|
|
||
| export const ReactGrab = (props: ReactGrabProps) => { | ||
| useEffect(() => { | ||
| if (props.enabled === false) return; |
There was a problem hiding this comment.
P2: When enabled becomes false, disable the existing global API instead of returning immediately; otherwise an already initialized instance keeps running.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At registry/react-grab.tsx, line 29:
<comment>When `enabled` becomes false, disable the existing global API instead of returning immediately; otherwise an already initialized instance keeps running.</comment>
<file context>
@@ -0,0 +1,63 @@
+
+export const ReactGrab = (props: ReactGrabProps) => {
+ useEffect(() => {
+ if (props.enabled === false) return;
+
+ let isActive = true;
</file context>
| return () => { | ||
| isActive = false; | ||
| }; | ||
| }, [props.enabled, props.options]); |
There was a problem hiding this comment.
P3: props.options is an object included in the useEffect dependency array. If the parent passes an inline object literal (e.g., options={{ activationMode: 'toggle' }}), the reference changes every render, causing the effect to fire on every render — re-importing the module, checking the global API, and running through the try/finally block each time.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At registry/react-grab.tsx, line 60:
<comment>`props.options` is an object included in the `useEffect` dependency array. If the parent passes an inline object literal (e.g., `options={{ activationMode: 'toggle' }}`), the reference changes every render, causing the effect to fire on every render — re-importing the module, checking the global API, and running through the try/finally block each time.</comment>
<file context>
@@ -0,0 +1,63 @@
+ return () => {
+ isActive = false;
+ };
+ }, [props.enabled, props.options]);
+
+ return null;
</file context>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
There are 7 total unresolved issues (including 5 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 3634587. Configure here.
| if (!isActive) return; | ||
|
|
||
| const existingApi = reactGrab.getGlobalApi(); | ||
| if (existingApi) return; |
There was a problem hiding this comment.
Disposed API blocks reinitialization
Medium Severity
After dispose() on the global API, window.__REACT_GRAB__ can still hold the old instance while hasInited is reset, so ReactGrab treats getGlobalApi() as already initialized and never runs init again.
Reviewed by Cursor Bugbot for commit 3634587. Configure here.
|
|
||
| return () => { | ||
| isActive = false; | ||
| }; |
There was a problem hiding this comment.
Unmount leaves grab running
Medium Severity
The effect cleanup only clears a local isActive flag and never calls dispose on the initialized global API, so removing ReactGrab from the tree leaves listeners, overlay, and other grab resources active.
Reviewed by Cursor Bugbot for commit 3634587. Configure here.
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/react-grab/README.md">
<violation number="1" location="packages/react-grab/README.md:36">
P3: The new `### shadcn Registry` heading accidentally nests the existing manual framework sections under the registry subsection.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
|
|
||
| ## Manual Installation | ||
|
|
||
| ### shadcn Registry |
There was a problem hiding this comment.
P3: The new ### shadcn Registry heading accidentally nests the existing manual framework sections under the registry subsection.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/react-grab/README.md, line 36:
<comment>The new `### shadcn Registry` heading accidentally nests the existing manual framework sections under the registry subsection.</comment>
<file context>
@@ -33,6 +33,29 @@ The copied context includes the selected element and its component stack with so
## Manual Installation
+### shadcn Registry
+
+Install the React Grab component from this GitHub registry:
</file context>
| ### shadcn Registry | |
| #### shadcn Registry |


Summary
registry.jsonwith areact-grabregistry item.ReactGrabinstaller component with commented optional config.Verification
pnpm dlx shadcn@latest registry validate registry.jsonpnpm buildpnpm testpnpm lintpnpm typecheckpnpm formatSummary by cubic
Adds a
shadcnGitHub registry entry and aReactGrabinstaller component so apps can install and initializereact-grabwith one command and a single render. Also expands install docs and examples across all READMEs.registry.jsonforshadcnwith areact-grabitem targeting@components/react-grab.tsx.ReactGrabcomponent that lazy-loadsreact-grab, sets a global API, and supports optionalOptions.npx shadcn@latest add aidenybai/react-grab/react-graband a dev-only root render example.Written for commit 3634587. Summary will update on new commits.