@unhingged/vizu-react
v0.2.0
Published
React bindings for @unhingged/vizu-core — VizuProvider, useVizu, useComments, useVizuUser, useVizuEvent.
Downloads
179
Maintainers
Readme
@vizu/react
React bindings for @vizu/core. Drop a provider in your tree, use hooks to read comments / set identity / register actions.
npm install @vizu/core @vizu/reactUse it
'use client';
import { VizuProvider, useVizu, useComments, useVizuUser, useVizuEvent, useVizuAction } from '@vizu/react';
export default function App() {
return (
<VizuProvider options={{
workspace: 'my-team', // required — created in the Vizu dashboard
pageVersion: 'v1',
shortcut: 'mod+shift+e',
startEnabled: true,
}}>
<YourPage />
</VizuProvider>
);
}
function YourPage() {
// 1. Read current user (set automatically after workspace sign-in)
const [user] = useVizuUser();
// 2. Read live comments (re-renders on add/remove/clear/set)
const comments = useComments();
// 3. Listen to events — mirror activity into your own systems
useVizuEvent('comment:added', ({ comment }) => {
analytics.track('vizu_comment', { id: comment.id });
});
// 4. Register a pill action
useVizuAction({
id: 'copy-prompt',
label: 'Copy as prompt',
variant: 'primary',
onClick: (ctx) => ctx.copyToClipboard(buildPrompt(ctx.comments)),
visibleWhen: ({ commentsCount }) => commentsCount > 0,
});
// 5. Imperative access
const vizu = useVizu();
useEffect(() => {
vizu.enable(); // or leave it to the shortcut / <VizuLauncher />
}, [vizu]);
return <div>Your page content…</div>;
}Floating launcher pill
Not everyone knows the keyboard shortcut. Drop <VizuLauncher /> anywhere inside the provider and visitors get a floating pill that turns Vizu on with one click (kicking the workspace sign-in popup, same as the shortcut):
import { VizuProvider, VizuLauncher } from '@vizu/react';
<VizuProvider options={{ workspace: 'my-team' }}>
<YourPage />
<VizuLauncher />
</VizuProvider>By default it sits bottom-right (Vizu's own toolbar takes bottom-center), shows the saved-comment count as a badge, and hides itself while Vizu is active — reappearing when Vizu is disabled. Everything is tweakable:
<VizuLauncher
label="Leave feedback" // pill text, default "Feedback"
position="bottom-left" // bottom-right | bottom-left | top-right | top-left
offset={32} // px from the viewport edges, default 20
showCount={false} // hide the comment-count badge
hideWhenActive={false} // keep it visible as an on/off toggle
accent="#7C5CFF" // dot + active border color
onToggle={(enabled) => track('vizu_toggled', { enabled })}
/>Or replace the content entirely while keeping the pill shell and behavior:
<VizuLauncher>💬 Comments</VizuLauncher>What you get
<VizuProvider>— mounts a single Vizu instance for the subtree (lazy, destroyed on unmount)<VizuLauncher>— floating pill that enables Vizu with a click (see above)useVizu()— the instance itself for imperative calls (vizu.enable(),vizu.clearAll(), etc.)useComments()— subscribes to all comment-list events, returnsVizuComment[]useVizuUser()—[user, setUser]tuple, re-renders onuser:changeduseVizuEvent('event', handler)— subscribe to any Vizu event for the component lifetimeuseVizuAction({...})— register an action while mounted; auto-removed on unmount
Cloud workspace
Vizu is cloud-only: every provider connects to a workspace created in the Vizu dashboard, and options.workspace is required. Comments are private to the workspace — the UI mounts after a member signs in (a popup handled by Vizu; kick it early with <VizuLauncher />, the keyboard shortcut, or vizu.requestAuth()).
<VizuProvider options={{ workspace: 'my-team' }}>...
<VizuProvider options={{ workspace: 'my-team', autoSignIn: false }}>... // never auto-open the popupIdentity comes from the workspace sign-in — you no longer set it yourself.
Next.js (App Router)
VizuProvider is a client component (it uses refs + effects) and is StrictMode-safe — dev-mode's mount→cleanup→remount cycle recreates the instance instead of leaving a destroyed one in context. Put it inside a layout that opts in:
// app/(commentable)/layout.tsx
'use client';
import { VizuProvider } from '@vizu/react';
export default function Layout({ children }) {
return <VizuProvider options={{ workspace: 'my-team' }}>{children}</VizuProvider>;
}Page-level usage stays as Server Components; only the provider and the components that call its hooks need the 'use client' directive.
