npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@outcode/bug-reporter-web

v2.1.1

Published

In-app bug reporter for any web frontend (React, Angular, Vue, Svelte, Solid, vanilla). Framework-agnostic vanilla-DOM widget: a floating button that captures the screen, lets users annotate it (draw, arrow, box, blur, text), set severity/type, and file t

Downloads

553

Readme

@outcode/bug-reporter-web

npm version npm downloads license: MIT types: TypeScript

In-app bug reporter for any web frontend. One floating button that captures the screen, lets users annotate it (draw · arrow · box · blur · text), set a severity and type, and file a report to ClickUp or any custom backend — implemented in framework-agnostic vanilla TypeScript/DOM.

A single package that works in React, Angular, Vue, Svelte, Solid, and plain HTML — mount it with mountBugReporter(). For React Native, see @outcode/bug-reporter-native.

Part of the OutCode Bug Reporter monorepo.

v2 is a framework-agnostic rewrite (breaking). v1 exported a React <BugReporterButton> (peer deps react/react-dom); v2 has no peer deps and exports the vanilla mountBugReporter(). See Framework integration for the ~15-line React equivalent.

Features

  • 🌐 One package, every web framework — React, Angular, Vue, Svelte, Solid, or plain HTML
  • ✏️ Annotate — pen, arrow, box, blur/redact, and text, flattened onto the screenshot
  • 🧭 Auto-captured context (consent-free) — route, browser/OS, viewport, language, connection type, console errors…
  • 🎨 Themeable — Indigo / Noir / Mint presets or your own tokens
  • 📡 Offline retry queue, screenshot size guard
  • 🧩 Pluggable backends — ClickUp out of the box, or any repository / HTTP endpoint
  • 📦 No peer deps; html2canvas-pro installs automatically; ESM + CJS, fully typed

Install

npm install @outcode/bug-reporter-web @outcode/bug-reporter-core

Use

import { mountBugReporter } from '@outcode/bug-reporter-web';
import { OutcodeBackendBugReporterRepository, type BugReporterConfig } from '@outcode/bug-reporter-core';

const handle = mountBugReporter(
  {
    appName: 'My App',
    theme: 'indigo', // 'indigo' | 'noir' | 'mint' | custom tokens
    // Recommended: reports go through the OutCode bug service, so no ClickUp token ships in the client.
    repository: new OutcodeBackendBugReporterRepository({
      endpoint: 'https://your-bug-service.example.com/api/bugs/ingest', // your bug service URL
      apiKey,     // a scoped `bug:create` key
      targetKey,  // the project routing key from the bot's "Bug Trends" tab
    }),
    // Internal/trusted builds can talk to ClickUp directly instead (ships the token in the client):
    // repository: new ClickUpBugReporterRepository({ apiKey, listId }),
  } satisfies BugReporterConfig,
  { label: 'Report a bug' }
);

// Later:
handle.open();                 // start the flow programmatically
handle.update(nextConfig);     // e.g. after a theme change
handle.destroy();              // remove the widget

mountBugReporter(config, options?) appends a floating button to options.target (default document.body) and returns a { open, close, update, destroy } handle. It no-ops safely in non-browser (SSR) environments.

Options

| Option | Type | Default | Description | | --- | --- | --- | --- | | label | string | 'Report a bug' | Accessible label for the floating button. | | target | HTMLElement | document.body | Where to mount. Must have no transformed/filtered ancestor (else the fixed overlays are clamped). Mount-time only. | | buttonStyle | Record<string,string> | — | Inline style overrides for the floating button. Wins over config.fabColor; note it styles the button only, not its halo. |

All configuration types (BugReporterConfig, ClickUpBugReporterRepository, …) come from @outcode/bug-reporter-core.

Reporting a bug while a modal or dropdown is open

This works out of the box, including the awkward cases:

  • Modal libraries that set body{pointer-events:none} while open (Radix, MUI, Headless UI, shadcn) no longer disable the widget.
  • The flow renders in a <dialog> opened with showModal(), so it sits in the browser's top layer — above host modals, popovers, and any z-index. Esc cancels and hands control back to your modal.
  • The floating button is promoted to the top layer too, so a host overlay at z-index:2147483647 can't bury it.
  • When your app opens a modal <dialog> (or marks the page inert), the browser blocks everything outside that one subtree — no stacking trick can win. The widget relocates into that subtree for as long as it's open, then moves back. Your modal's own content stays fully interactive.

If the host modal is transformed (common with animation libraries), position: fixed resolves against the modal rather than the viewport, so the button pins to the modal's corner instead of the screen's. That's the one visible difference.

You can also drive it yourself — useful for a "Report a bug" menu item or a global shortcut:

const handle = mountBugReporter(config);

document.addEventListener('keydown', e => {
  if (e.key === 'b' && (e.metaKey || e.ctrlKey)) handle.open();
});

Framework integration

mountBugReporter is the same call everywhere — place it in your framework's mount hook and call destroy() on teardown (and update(config) if your config is reactive).

React

import { useEffect } from 'react';
import { mountBugReporter } from '@outcode/bug-reporter-web';

function BugReporter({ config }) {
  useEffect(() => {
    const h = mountBugReporter(config);
    return () => h.destroy();
  }, []);
  return null;
}

Angular

import { Component, OnDestroy, OnInit } from '@angular/core';
import { mountBugReporter, type BugReporterHandle } from '@outcode/bug-reporter-web';

@Component({ selector: 'app-bug-reporter', template: '' })
export class BugReporterComponent implements OnInit, OnDestroy {
  private handle?: BugReporterHandle;
  ngOnInit()    { this.handle = mountBugReporter(this.config); }
  ngOnDestroy() { this.handle?.destroy(); }
}

Vue

<script setup>
import { onMounted, onUnmounted } from 'vue';
import { mountBugReporter } from '@outcode/bug-reporter-web';
let h;
onMounted(() => (h = mountBugReporter(config)));
onUnmounted(() => h?.destroy());
</script>

Svelte

<script>
  import { onMount } from 'svelte';
  import { mountBugReporter } from '@outcode/bug-reporter-web';
  onMount(() => {
    const h = mountBugReporter(config);
    return () => h.destroy();
  });
</script>

Solid

import { onMount, onCleanup } from 'solid-js';
import { mountBugReporter } from '@outcode/bug-reporter-web';
onMount(() => {
  const h = mountBugReporter(config);
  onCleanup(() => h.destroy());
});

Vanilla / plain HTML — see Use above; just call mountBugReporter(config).

Theming

The three presets are a starting point — any colour works:

mountBugReporter({ appName: 'My App', theme: 'noir' });                        // preset
mountBugReporter({ appName: 'My App', theme: { accent: '#FF5C00' } });         // custom accent
mountBugReporter({ appName: 'My App', theme: { preset: 'noir', accent: '#FF5C00' } });
mountBugReporter({ appName: 'My App', fabColor: '#FF5C00' });                  // just the button

A custom accent is enough on its own: accentPress, ring and onAccent are derived from it, so the pressed state, focus rings and the button's bug glyph all follow — a light brand colour gets a dark icon rather than white-on-white. Set any of those tokens explicitly to override the derived value. An unparseable colour falls back to the preset instead of rendering an invisible button.

fabColor recolours the floating button and its halo only, leaving the rest of the flow on the theme accent. Precedence for the button is buttonStyle (inline, mount option) → fabColor → theme.accent.

Redaction

The blur tool paints an opaque block rather than a real blur, because a blur can sometimes be inverted and a password or a customer's name deserves better than that.

It also fails closed. Annotations are flattened onto the screenshot before it is submitted, and if that composite fails the original capture — which still shows what was redacted — is not sent as a fallback. The editor stays open with an error instead, and Continue retries. Reports with no redaction still fall back to the unflattened capture, since nothing was hidden to lose.

If you call the exported flattenAnnotations() yourself, note that it now rejects instead of resolving with the original when it cannot composite a redaction — catch it rather than submitting the source image.

License

MIT © OutCode Software