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

@tacksdk/js

v0.5.0

Published

Tack feedback SDK — vanilla JS client

Readme

@tacksdk/js

Vanilla JS / TS core for Tack — embeddable in-app feedback. Framework-agnostic. No dependencies. ~12 KB gzipped (widget); the headless subpath is ~3 KB.

Install

npm install @tacksdk/js

Quick start (widget)

import { Tack } from '@tacksdk/js'

const handle = Tack.init({ projectId: 'proj_your_project_id' })

document.querySelector('#feedback-button')!
  .addEventListener('click', () => handle.open())

The widget mounts inside a closed shadow root, so host-page CSS can't bleed in. The dialog uses the native <dialog> element for top-layer rendering and focus trap.

Floating launcher

TackLauncher.mount() ships a floating button that handles open/close for you:

import { TackLauncher } from '@tacksdk/js'

TackLauncher.mount({
  projectId: 'proj_your_project_id',
  position: 'bottom-right',
  variant: 'circle',
})

Headless

For server-side or non-DOM contexts (or if you have your own UI):

import { submit } from '@tacksdk/js/headless'

await submit({
  projectId: 'proj_your_project_id',
  body: 'The export button is broken',
})

The /headless subpath does NOT include the widget — bundlers tree-shake the DOM code out.

Tack.init(config) options

| Option | Type | Default | Description | |---|---|---|---| | projectId | string | — | Required. Public project id (proj_...). | | endpoint | string | https://tacksdk.com | Override the API endpoint. | | user | TackUser | | Default user attached to every submission. | | metadata | object | | Default metadata attached to every submission. | | container | HTMLElement | document.body | Where the dialog's shadow host mounts. | | preset | 'default' \| 'midnight' \| 'paper' \| TackThemePreset | 'default' | Curated theme bundle (~30 design tokens). | | theme | 'auto' \| 'light' \| 'dark' | 'dark' | Color scheme (legacy — prefer preset). | | injectStyles | boolean | true | When false, host owns all styling. | | title | string | 'Send feedback' | Dialog title. | | submitLabel | string | 'Send' | Submit button label. | | cancelLabel | string | 'Cancel' | Cancel button label. | | placeholder | string | 'What can we improve?' | Textarea placeholder. | | hotkey | string | | Toggle shortcut, e.g. 'mod+alt+f'. | | placement | 'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left' \| 'custom' | 'bottom-right' | Launcher placement (no effect when trigger: 'none'). | | trigger | 'auto' \| 'none' | 'auto' | 'none' skips the launcher; host calls handle.open(). | | zIndex | number | 2147483600 | Dialog stacking context. | | modal | boolean | true | false calls dialog.show() (no focus trap, no backdrop). | | scrollLock | boolean | true | Lock body scroll while the dialog is open. | | debug | boolean | false | Verbose console.debug lifecycle logs. | | fetch | typeof fetch | globalThis.fetch | Inject a custom fetch (for proxies, tracing). | | headers | Record<string, string> | | Extra request headers (cannot override X-Tack-SDK-Version). | | captureScreenshot | false \| ((el: Element) => Promise<string>) | enabled | Renders an "Add screenshot" button in the dialog. Clicking captures and attaches the host page; clicking again removes it. false removes the button entirely; a function overrides the default html-to-image path. The capture module is lazy-loaded only on first click. | | appVersion | string | | Host app version, e.g. "1.4.2" or a git SHA. Sent on every submission so feedback can be bucketed by release. See Bundler patterns. | | rating | false \| 'thumbs' \| 'stars' \| 'emoji' | false | Rating UI variant. When set, renders a row of buttons above the textarea; sends the selected value as rating and auto-attaches metadata.ratingScale so the dashboard can label the value unambiguously (4 of 5 stars vs 4 of 4 emoji). | | captureConsole | boolean \| CaptureConsoleConfig | false | Capture host console output and ship it in metadata.console on submit. Privacy footgun — read Console capture before enabling. | | onSubmit | (result, request) => void | | Called after a successful submit. Receives both the server response and the full request payload (handy for firing your own analytics on rating/screenshot inclusion). | | onError | (err: TackError) => void | | Called on submit failure. | | onOpen | () => void | | Called when the dialog opens. | | onClose | () => void | | Called when the dialog closes. |

Tack.init() returns a handle: { open, close, toggle, isOpen, destroy, update, getCapturedConsole }.

appVersion bundler patterns

Most apps surface their version through the bundler's environment plumbing. Pick the one that matches yours:

// Next.js
<TackLauncher appVersion={process.env.NEXT_PUBLIC_APP_VERSION} />

// Vite
<TackLauncher appVersion={import.meta.env.VITE_APP_VERSION} />

// webpack / rollup with DefinePlugin
declare const __APP_VERSION__: string
<TackLauncher appVersion={__APP_VERSION__} />

The dashboard treats this as an opaque string. SemVer, git SHAs, datestamps — anything that uniquely identifies a release works.

Console capture (privacy)

⚠️ Privacy warning. When captureConsole is enabled, the SDK ships your app's console output (errors, warnings, optionally info/log) to your Tack dashboard alongside the submission. This often includes user-visible PII: emails, IDs, request bodies, debug dumps. Test in dev mode before enabling in production.

Inspect the buffer at any time:

const handle = Tack.init({ projectId: '...', captureConsole: true })
// ... user does stuff, errors happen ...
console.log(handle.getCapturedConsole())
// → [{ level: 'error', ts: 1745... , msg: 'Failed to fetch /api/...' }, ...]

Configuration:

captureConsole: true
// Same as: { levels: ['error', 'warn'], maxEntries: 20 }

captureConsole: { levels: ['error', 'warn', 'info'], maxEntries: 50 }

The capture module is lazy-loaded only when captureConsole is set — there's no bundle cost for consumers who leave it off. Each widget instance has its own buffer (no cross-widget leakage). The serializer is hardened against circular references, errors, DOM nodes, and oversized payloads — it will never throw and break your console.

If your app initializes Sentry or another error monitor AFTER the Tack widget mounts, the wrapper-identity check on uninstall preserves their patch — Tack won't restore over them.

Errors

Both the widget's onError and headless submit() surface a TackError:

class TackError extends Error {
  type: 'invalid_request' | 'unauthorized' | 'forbidden' | 'not_found'
       | 'payload_too_large' | 'rate_limited' | 'internal_error'
       | 'network_error'
  docUrl: string
  status: number | null
}

Each type has a docs page at tacksdk.com/docs/errors#<type>.

Stability

Pre-1.0 — pin your version. See STABILITY.md before upgrading.

License

MIT