@clueline/react
v0.2.0
Published
React error boundary, error capture, and the user-facing Clueline error UI.
Maintainers
Readme
@clueline/react
React SDK for Clueline — catches errors, ships them to your Clueline project, and shows your end users a calm, plain-language error UI (with an optional "what were you doing?" prompt) instead of a raw crash.
- Light/dark/auto theming — matches the host page via
prefers-color-scheme, or set it explicitly. - Style-isolated — renders inside a Shadow DOM, so your page's CSS can never leak into (or break) the widget, and the widget's styles can never leak out.
Install
npm install @clueline/reactUsage
Wrap your app in the provider — that's it. It installs global handlers for uncaught errors and unhandled promise rejections, and wraps your app in a root-level error boundary, so a crash anywhere is caught and shown the calm fallback UI by default:
import { CluelineProvider } from "@clueline/react";
export function Root() {
return (
<CluelineProvider apiKey={import.meta.env.VITE_CLUELINE_API_KEY}>
<App />
</CluelineProvider>
);
}Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| apiKey | string | Yes | — | Your Clueline API key |
| apiEndpoint | string | No | Clueline's hosted backend | Override for self-hosted/proxied setups |
| environment | 'development' \| 'production' | No | 'production' | Current environment |
| colorMode | 'light' \| 'dark' \| 'auto' | No | 'auto' | Widget color mode. 'auto' follows the host page's prefers-color-scheme once at mount |
| userId | string | No | — | Optional user identifier attached to reports |
| sessionId | string | No | generated + persisted in sessionStorage | Optional session identifier |
| onError | (report) => void | No | — | Called with the full report whenever an error is captured |
| ui.logo | ReactNode | No | — | Custom logo shown in the fallback boundary UI |
| sanitize | (text: string) => string | No | — | Extra redaction rules applied on top of the built-in PII scrubber |
ui also carries the existing copy overrides — title, description, showPrompt, promptLabel, retryLabel:
<CluelineProvider
apiKey={import.meta.env.VITE_CLUELINE_API_KEY}
ui={{
title: "Hmm, that didn't work",
description: "We've logged the problem. Mind telling us what you were doing?",
retryLabel: "Reload",
}}
>"What happened?" explanations
The default fallback shows a "What happened?" button once an AI-generated, plain-language explanation is ready for that error — no code required. It's generated once per distinct error (not per user or per click) and cached, so it's usually there immediately except on a brand-new error's very first occurrence, where the button just doesn't render rather than showing a dead end.
Or replace the UI entirely with a render prop:
<CluelineBoundary
fallback={({ error, retry, submitNote, explanation }) => <MyErrorScreen error={error} onRetry={retry} />}
>Manual Error Reporting
Report caught errors that don't crash the app:
import { useCluelineReport } from "@clueline/react";
function MyComponent() {
const { reportError } = useCluelineReport();
const handleClick = async () => {
try {
await fetch("/api/action");
} catch (error) {
reportError(error, { action: "button_click" });
}
};
return <button onClick={handleClick}>Action</button>;
}You can also tag the error with an errorType so the AI explanation and dashboard reflect what actually happened, instead of a generic message:
reportError(error, { action: "checkout" }, "payment_integration");Available types: 'api_error' | 'network_timeout' | 'payment_integration' | 'render_crash' | 'component_crash' | 'unhandled_rejection' | 'global_error' | 'generic'.
Identify a Known User
If you already know who the user is (e.g. right after login), attach their identity so any incident created afterward already has an email and name — the agent won't need to ask for contact info it already has:
import { useCluelineIdentify } from "@clueline/react";
function Dashboard({ user }) {
const { identify } = useCluelineIdentify();
useEffect(() => {
identify({ email: user.email, name: user.name });
}, [user]);
// ...
}Automatic Fetch Classification
useCluelineFetch is a drop-in fetch wrapper that automatically detects and reports API failures with the real HTTP status code and request host — so a failed Stripe call gets a payment-specific explanation, and a 500 gets framed as "on our end," without you writing any classification logic yourself:
import { useCluelineFetch } from "@clueline/react";
function Checkout() {
const { cluelineFetch } = useCluelineFetch();
const handlePay = async () => {
const res = await cluelineFetch("https://api.stripe.com/v1/charges", { method: "POST" });
// non-ok responses and thrown errors are reported automatically;
// the response/error is still returned/thrown as normal.
};
return <button onClick={handlePay}>Pay</button>;
}Isolated Component Recovery
By default, a crash anywhere in your app is caught by the root CluelineProvider boundary and the whole page shows the fallback overlay. Wrap a specific risky section in <CluelineBoundary> to isolate a crash to just that section instead — the rest of the page keeps working, and "Try Again" remounts just that piece with fresh state:
import { CluelineBoundary } from "@clueline/react";
<CluelineBoundary label="checkout-form">
<CheckoutForm />
</CluelineBoundary>If you don't wrap anything, nothing changes — the app-wide boundary still catches everything as before.
PII Redaction
Before anything leaves the browser, Clueline automatically redacts common PII — emails, and credit-card-shaped digit runs — from error messages, stack traces, the feedback box, and any additionalContext you pass to reportError(). No configuration required.
To add your own rules on top, pass sanitize to CluelineProvider:
<CluelineProvider
apiKey={import.meta.env.VITE_CLUELINE_API_KEY ?? ""}
sanitize={(text) => text.replace(/acct_[a-zA-Z0-9]+/g, "[redacted-account]")}
>
<YourApp />
</CluelineProvider>The built-in redaction is a safety net, not a substitute for care — avoid putting PII directly in error messages in the first place. It also never touches identity you attach on purpose via userId/sessionId/identify() — redacting those back out would defeat the point.
Exports
| Export | Description |
|---|---|
| CluelineProvider | Creates the client, installs global handlers, provides context, wraps children in a root boundary. |
| CluelineBoundary | Catches render errors in its subtree, reports them, renders the fallback UI in an isolated Shadow DOM. |
| ErrorFallback | The default user-facing UI (calm copy + note prompt + retry). |
| ShadowHost | Renders children inside a Shadow DOM subtree. Used internally by CluelineBoundary; exported for custom fallback UIs that want the same isolation. |
| useClueline() | Access the client for manual capture() calls. |
| useCluelineReport() | { reportError } — report a caught error with optional context + errorType. |
| useCluelineIdentify() | { identify } — attach a known user's identity. |
| useCluelineFetch() | { cluelineFetch } — drop-in fetch wrapper with automatic failure classification. |
Configuration options (apiKey, apiEndpoint, redact, …) are documented in @clueline/core.
