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

@desource/context7-widget

v0.2.0

Published

Customizable Context7 documentation chat widget with TypeScript helpers and a drop-in script build.

Readme

@desource/context7-widget

Core Coverage SonarCloud License

Style Context7 docs chat to fit your website, with a script tag or TypeScript. This MIT-licensed package adds the control a carefully designed site needs: fonts, colors, spacing, custom buttons, and flexible panel placement.

Why It Exists

Context7 provides its docs widget and hosted AI answers for free. The official widget’s basic visual options leave little room to match your design. We built this version so the assistant can look and feel like part of your product.

Keep Context7’s search and AI answers. Add your own styling, multiline questions, copyable answers and code, Stop and Retry controls, and scrolling that respects what the visitor is reading. Existing widget users can migrate by changing the script URL; their library and allowed-domain settings carry over.

New to Context7? Give visitors a place to ask about your library, find an example, and take the first step toward using it. This project builds on Context7’s free docs chat initiative with an interface you can make your own, also free.

Use the root entry for the custom element and mounting helpers, /core for custom chat interfaces, or /kit to build native framework integrations. ESM imports are tree-shakeable and do not mount anything; only /widget.js starts itself.

Before You Start

Use a library you have claimed on Context7. In its Admin → Chat settings, enable the widget, add your site's domain to the allowed domains, and save. Replace /owner/repo in the examples with that library's id. A library being indexed alone does not enable chat on your site. See Context7's widget setup.

Install

npm install @desource/context7-widget

Drop-In Browser Script

No package manager or build step is required:

<script
  async
  src="https://context7.desourcelabs.com/widget.js"
  data-library="/owner/repo"
  data-position="anchor"
  data-preset="glass"
></script>

The hosted file is a classic, self-starting IIFE. Multiple script tags are supported when a page needs multiple independently configured widgets; give each one a unique data-widget-id.

Mount A Widget

import { mountContext7Widget } from '@desource/context7-widget';

const widget = mountContext7Widget({
  library: '/owner/repo',
  position: 'center',
  preset: 'glass',
  theme: 'auto',
  backdrop: true,
  closeOnOutsideClick: true
});

widget.open();
await widget.send('Show me the recommended setup.');
widget.cancel();
await widget.retry();
widget.reset();

Context7WidgetElement exposes open, close, toggle, send, cancel, retry, reset, isOpen, isBusy, and getMessages. The same operations are available by widgetId through window.Context7Widget.

Imports are SSR-safe. Element creation, mounting, and imperative DOM operations still require a browser document and fail with a focused error when called on the server.

Direct Custom Element

Register the element once when declarative markup fits your application better:

import { defineContext7Widget } from '@desource/context7-widget';

defineContext7Widget();
<context7-widget library="/owner/repo" position="center" preset="minimal" backdrop="true"></context7-widget>

Custom tag names are supported without reusing the same registered constructor:

defineContext7Widget('context7-docs-widget');

Examples

Mount an anchored widget beside an existing help button:

<button id="docs-help">Ask docs</button>
import { mountContext7Widget } from '@desource/context7-widget';

const widget = mountContext7Widget({
  library: '/owner/repo',
  customTrigger: '#docs-help',
  position: 'anchor',
  preset: 'minimal',
  widgetId: 'docs'
});

await widget.send('Show the installation steps.');

Use position: 'center' with backdrop: true for a modal help flow. Use a corner position without customTrigger for the built-in floating launcher. Runnable framework-neutral examples are available in the demo gallery.

Options

JavaScript uses camel-case option names. Direct custom-element attributes use kebab case; script installs prefix those attributes with data-.

| JavaScript option | Custom-element attribute | Type / default | | --------------------- | ------------------------ | ------------------------------------------------------------------ | | library | library | Required Context7 library id | | position | position | bottom-right; also corners, center, or anchor | | preset | preset | default; also minimal, glass, neo, terminal, brutalist | | theme | theme | auto; also light or dark | | color | color | No override; presets own the accent | | customTrigger | custom-trigger | CSS selector, simple element id, or Element in JavaScript | | backdrop | backdrop | true for center, otherwise false | | closeOnOutsideClick | close-on-outside-click | true | | defaultOpen | default-open | false | | initialMessage | initial-message | Built-in greeting; {library} is interpolated | | labels | Property only | Partial localization object merged with English defaults | | launcherLabel | launcher-label | Ask Docs AI | | launcherVariant | launcher-variant | icon; also pill or badge | | linkBaseUrl | link-base-url | Base for relative links; defaults to the Context7 library page | | panelHeight | panel-height | Responsive stylesheet default | | panelWidth | panel-width | Responsive stylesheet default | | placeholder | placeholder | Ask about the docs... | | title | dialog-title | Chat with Documentation | | widgetId | widget-id | default |

For script tags, library becomes data-library, dialog-title becomes data-title, and the remaining attributes follow the same data-* pattern. The custom element intentionally uses dialog-title rather than the native HTML title attribute, avoiding an accidental browser tooltip.

Boolean attributes accept explicit values such as backdrop="false" and close-on-outside-click="false"; their mere presence does not force them to true.

For a mounted element, assign widget.labels = { send: 'Enviar', ... } to localize visible and assistive text, including attribution and missing-library fallbacks. JavaScript helpers accept the same labels option. Script-tag data-* values remain live after connection; a canonical custom-element attribute takes precedence only when the application explicitly authors it.

Generate A Script Tag

import { buildContext7WidgetScriptTag } from '@desource/context7-widget';

const script = buildContext7WidgetScriptTag({
  library: '/owner/repo',
  customTrigger: '#docs-chat',
  position: 'anchor',
  preset: 'minimal'
});

Selector custom triggers hide the built-in launcher only after a matching element binds. Missing or late-rendered selectors keep the launcher available and bind automatically when the target appears.

The script still sends chat requests to https://context7.com. This package does not run a Context7 proxy; it supplies the customizable client layer.

Data Flow

The browser posts the configured library id and current conversation messages directly to https://context7.com/api/v2/widget/chat. DeSource Labs does not proxy chat content. This client adds no analytics, cookies, or persistent browser storage; state remains in the live widget's memory and reset() clears it. Public events expose questions and answers to the host application, so integrators control any additional analytics or persistence. Avoid sending secrets or sensitive personal data and review Context7's policies for backend processing and retention.

Supported Visual Modes

  • Positions: bottom-right, bottom-left, top-right, top-left, center, and anchor
  • Presets: default, minimal, glass, neo, terminal, and brutalist
  • Themes: light, dark, and auto
  • Launcher variants: icon, pill, and badge

If color is omitted, the preset owns the launcher and send-button color. Set color only when your product needs a brand override.

Customization

Choose a preset first, then override public tokens or parts. Internal .c7-* classes are private and can change between releases.

Styling Contract

Style the custom element from the host page. Do not target internal .c7-* classes.

context7-widget[widget-id='docs'] {
  --c7-accent: #7cffb2;
  --c7-accent-contrast: #07120c;
  --c7-font-family: Inter, ui-sans-serif, system-ui, sans-serif;
  --c7-panel-background: #101513;
  --c7-panel-color: #f7f2e8;
  --c7-border-color: rgba(247, 242, 232, 0.18);
  --c7-panel-radius: 8px;
}

context7-widget::part(send-button) {
  min-width: 5rem;
  text-transform: uppercase;
}

Public variables include:

  • Brand/type: --c7-accent, --c7-accent-contrast, --c7-font-family, --c7-muted-color, --c7-focus-ring
  • Panel: --c7-panel-background, --c7-panel-backdrop-filter, --c7-panel-color, --c7-panel-width, --c7-panel-height, --c7-panel-radius, --c7-panel-shadow, --c7-border-color, --c7-spacing, --c7-z-index
  • Launcher: --c7-launcher-background, --c7-launcher-color, --c7-launcher-gap, --c7-launcher-radius, --c7-launcher-shadow, --c7-launcher-size
  • Backdrop: --c7-backdrop, --c7-backdrop-filter
  • Header/footer: --c7-header-background, --c7-footer-background
  • Messages: --c7-message-assistant-background, --c7-message-assistant-color, --c7-message-user-background, --c7-message-user-color, --c7-message-radius, --c7-error-background, --c7-error-color
  • Controls: --c7-control-background, --c7-control-border, --c7-control-color

Stable shadow parts:

backdrop, panel, header, title, close-button, messages, message, assistant-message, user-message, error-message, typing, tool-call, tool-toggle, code-block, composer, input, send-button, footer, powered-by, and launcher.

Events

Listen on the element or at document level:

import type { Context7WidgetQuestionEventDetail } from '@desource/context7-widget';

document.addEventListener('c7:answer-complete', (event) => {
  // `event.detail` is inferred from the event name.
  console.log(event.detail.library, event.detail.answer);
});

function trackQuestion(detail: Context7WidgetQuestionEventDetail) {
  console.log(detail.question);
}

Events: c7:ready, c7:open, c7:close, c7:cancel, c7:question, c7:first-token, c7:answer, c7:answer-complete, c7:tool-call, c7:tool-result, and c7:error.

Cancelling after answer tokens arrive preserves the visible partial assistant message in getMessages() with status: 'cancelled'. send() resolves with a status result such as complete, cancelled, error, busy, or empty. retry() resends the last failed question without duplicating the user message.

The composer is multiline: Enter sends and Shift+Enter inserts a newline. Answers and fenced code blocks have copy actions. Markdown escapes raw HTML, allows safe HTTP(S) and relative links, and supports headings, fenced code with lightweight highlighting, nested lists, tasks, blockquotes, tables, and inline formatting. Streaming text stays escaped plain text and Markdown parsing is deferred until the answer completes, avoiding quadratic work while long responses stream.

Headless Engine Subscriptions

Context7ConversationEngine.subscribe(listener) includes every state snapshot by default. Framework or custom renderers that consume the event stream can use subscribe(listener, { includeTransient: false }) to skip snapshots caused only by partial-answer or tool-frame streaming. Request start, committed messages, cancellation, errors, reset, and final busy state are still delivered; subscribeEvents continues to emit every stream event.

State and event listeners are invoked independently. If consumer code throws, the error is logged and the engine continues the request and invokes the remaining listeners. Both subscription methods return idempotent unsubscribe callbacks.

Exports

  • @desource/context7-widget
  • @desource/context7-widget/core — useful framework-neutral primitives for a custom experience: engine, renderer bridge, transport, Markdown, layout, clipboard and copy-action coordination, modal isolation, defaults, contracts, and labels
  • @desource/context7-widget/kit — rendering-independent conversation engine, renderer bridge, transport, markdown, floating-layout calculation, contracts, defaults, and brand assets for framework packages
  • @desource/context7-widget/widget.js

The root, /core, and /kit are ESM-only and preserve internal module boundaries. Downstream bundlers can therefore omit the custom-element runtime when an application imports only renderMarkdown or resolveContext7AnchorLayout from /core. /kit is intentionally a broader framework-author surface; application integrations should prefer the root or /core. The widget.js subpath is the classic browser script and intentionally has side effects.

The custom element shares one constructable stylesheet across instances when the browser supports it and falls back to an inline shadow stylesheet otherwise. Streamed DOM updates are frame-throttled, while answer events remain available for every received chunk.

The package publishes ESM only. Its root, /core, and /kit declarations are validated for modern Node ESM and TypeScript bundler resolution.

Browser, Accessibility, And Security Notes

The runtime targets ES2020-era modern browsers with Custom Elements, open Shadow DOM, fetch, ReadableStream, and AbortController. Constructable stylesheets are shared when available; older browsers receive an inline <style> fallback. Centered panels use modal dialog semantics, keep keyboard focus inside the panel, make outside content inert, lock page scrolling, close with Escape, and restore both document state and focus to the opener. Corner and anchored panels remain non-modal.

For a restrictive Content Security Policy, allow:

  • the widget origin in script-src;
  • https://context7.com in connect-src;
  • data: in img-src for the embedded DeSource Labs mark.

Browsers using the inline stylesheet fallback also need a compatible style-src policy. Test the final policy in every browser your application supports.

Related packages