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

@scopmx/surveybase-widget

v1.3.1

Published

Survey frontend: the admin (<SurveyAdmin />) and the pop-up survey widget.

Readme

@scopmx/surveybase-widget

React frontend for Surveybase:

  • <SurveyAdmin />: the full admin. Create surveys, design them, set targeting, publish, and analyze results.
  • <SurveyWidget />: the pop-up survey shown to visitors on your website.

It talks to the backend package @scopmx/surveybase-server, which you mount in your own Express app.

Options marked (next release) are in the source but not in the published 1.1.0 yet.


Contents

  1. Install
  2. Imports
  3. <SurveyAdmin /> props
  4. <SurveyWidget /> props
  5. initSurvey() (no React)
  6. Visitor traits
  7. Other exports
  8. Browser events and storage
  9. Survey settings reference
  10. Examples

Install

npm install @scopmx/surveybase-widget @mui/material @emotion/react @emotion/styled
# optional: icons in the admin, charts in results
npm install @mui/icons-material chart.js

| Peer dependency | Version | Required | | ----------------------------------- | ------------------ | ------------------------ | | react, react-dom | ^18.3.1 or ^19 | Yes | | @mui/material | ^6, ^7 or ^9 | Yes | | @emotion/react, @emotion/styled | ^11 | Yes | | @mui/icons-material | ^6, ^7 or ^9 | Optional | | chart.js | ^4 | Optional (result charts) |

The admin renders inside its own isolated shadow DOM, so your page styles do not leak into it and its styles do not leak out.


Imports

import { SurveyAdmin } from "@scopmx/surveybase-widget/admin";
import { SurveyWidget, initSurvey, setVisitorTraits } from "@scopmx/surveybase-widget/widget";

// or everything from the root
import { SurveyAdmin, SurveyWidget } from "@scopmx/surveybase-widget";

Use the /admin and /widget entry points so public pages do not load admin code.


<SurveyAdmin /> props

<SurveyAdmin basePath="/admin" getToken={() => session.token} />

| Prop | Type | Default | Description | | -------------------- | --------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | apiBase | string | "/api/survey" | API prefix, or an origin. https://api.example.com becomes https://api.example.com/api/survey. | | basePath | string | detected from the URL | Router prefix for the admin, for example /admin. The admin owns every path below it (/admin/surveys, /admin/surveys/:id). | | getToken | () => string \| Promise<string> | none | Called before every request. The result is sent as Authorization: Bearer <token>. Pair it with the server authorize option. | | getHeaders | () => object \| Promise<object> | none | Called before every request. The returned headers are added. | | secretKey | string | none | Deprecated. Sent as x-survey-key. Anyone who opens the page can read it, and it logs a warning. Use authorize on the server, or add the header in a server proxy. | | height | string \| number | "100vh" | Height of the admin. Pass "100%" to fill the parent (the parent needs a height), or any CSS length. | | loadFonts | boolean | false | Load Plus Jakarta Sans from Google Fonts. Off by default because it sends admin IP addresses to Google. | | results | object | see below | Options for the Analyze (results) pages. | | colors | object | see below | Brand colours for the admin itself. | | theme | MUI Theme | built-in | Replaces the whole admin theme. Prefer colors unless you need full control. | | panelTheme | MUI Theme | built-in | Replaces the theme of the dark builder side panel. | | header | object \| false | see below | Projects page header. false hides it. | | defaultSurveyColor | string (hex) | none | (next release) Colour for surveys created from this admin, for example "#0D447A". Wins over the server's defaultTheme. colors.primary only styles the admin and does not change survey colours. | | previewUrl | string (http/https URL) | none | (next release) Website shown behind the survey in the builder's live preview, for example "https://example.com". Without it the preview shows a sample page. |

Live preview behaviour (previewUrl)

  • The URL fills the preview field and loads in the frame straight away.
  • A URL the admin types is saved in localStorage (key surveybase:preview-url:<survey key>), per survey, and wins over previewUrl after a reload. Clearing the field shows the sample page again.
  • Many sites refuse to be shown inside another page (X-Frame-Options or CSP frame-ancestors). Browsers do not report this, so:
    • a notice "Page blank? Some sites block previews." has a Show sample page button;
    • if the page never loads, the preview falls back to the sample page after 10 seconds, with a Try again button.
  • To show your own site, allow the admin's domain in your site's frame-ancestors policy.
  • The preview uses the survey's own colour. The widget's accentColor applies only on the real website.

results options

<SurveyAdmin
  results={{
    live: false,
    showRespondentId: false,
    traitLabels: { userKey: "Patient", clinicId: "Clinic" },
    attributesHint: "Shared by the app when the patient signs in."
  }}
/>

| Key | Type | Default | Description | | ------------------ | ------------------------ | ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | live | boolean | true | (next release) Keep results up to date over a live connection. With false the admin makes no background requests: data loads when the page opens, when filters change, and when the Refresh button is clicked. The "Live" chip is hidden. | | showRespondentId | boolean | true | Show the "Respondent ID" box (with a copy button) on each response. | | traitLabels | Record<string, string> | {} | Display names for visitor trait keys, for example { userKey: "User" }. Unlisted keys show as is. | | attributesHint | string \| false | "Sent by your website with setVisitorTraits." | (next release) Hint under "Visitor attributes" on a response. false or "" hides it. |

What the results pages do (no configuration needed):

  • Summary tab: KPIs, drop-off, activity chart and per-question results, based on the latest 1,000 matching responses (a note says so when there are more).
  • Responses tab: the list loads 30 responses at a time from the API as you scroll, with loading rows, an inline Retry on errors and "All N responses loaded" at the end. The counter shows the real total, for example "151 of 42,591".
  • Filters: date range (7 days, 30 days, all time), search, device, status, question and answer. All filtering runs on the server.
  • Contact form table: server-side paging (10, 25, 50 or 100 rows), sort, search across name, email, phone and company, and Copy emails (up to 5,000 unique addresses).
  • Export: CSV and Excel of the loaded responses.

colors options

Styles the admin only. Every key is optional; unset keys use the defaults below. Exported as surveyAdminDefaultColors.

| Key | Default | Used for | | ----------------- | ------------------ | ------------------------------- | | primary | #4F46E5 | Buttons, links, selected states | | primaryDark | #4338CA | Hover and pressed primary | | background | #F7F8FC | Page background | | paper | #FFFFFF | Cards, menus | | text | #111827 | Main text | | mutedText | #667085 | Secondary text | | border | #E5E7EB | Dividers, outlines | | success | #059669 | Success states | | warning | #D97706 | Warnings | | error | #DC2626 | Errors | | info | #2563EB | Info notices | | topBar | primary | Builder top bar | | topBarText | #FFFFFF | Text on the top bar | | panelBackground | #1b1f1e | Builder side panel | | panelPaper | #232826 | Surfaces inside the panel | | panelCard | #262b29 | Cards inside the panel | | panelText | #f3f1ec | Panel text | | panelMutedText | 60% of panelText | Secondary panel text | | panelPrimary | #9a8fff | Accent inside the panel | | panelBorder | 10% overlay | Panel dividers | | rail | panelBackground | Icon rail | | railIcon | 60% of panelText | Rail icons | | railIconActive | panelText | Active rail icon |

header options

Controls the header of the projects page. Pass header={false} to hide it completely. Defaults are exported as surveyAdminDefaultHeader.

| Key | Type | Default | Description | | ------------------- | ------------------------------------------ | ------------------------------------------------------------------ | ---------------------------------------------------------------------------- | | show | boolean | true | Show the header. | | title | ReactNode | "Projects" | Title. | | subtitle | ReactNode | "Create a survey, then publish it when the questions are ready." | Subtitle. | | actions | ReactNode | null | Extra content on the right. | | showCreate | boolean | true | Show the create button. | | createLabel | string | "New project" | Create button text. | | showSearch | boolean | true | Show the search box. | | searchPlaceholder | string | "Search surveys" | Search placeholder. | | filters | false \| Array | ["all", "live", "draft", "archived"] | Status filter buttons. Items are ids or { id, label }. false hides them. | | defaultFilter | "all" \| "live" \| "draft" \| "archived" | first filter | Filter selected on load. | | search | string | uncontrolled | Controlled search text. | | filter | status id | uncontrolled | Controlled status filter. | | onSearch | (text) => void | none | Called when the search text changes. | | onFilterChange | (filter) => void | none | Called when the filter changes. | | onCreate | () => boolean \| void | none | Called on create. Return false to stop the built-in "New project" dialog. | | searchFn | (project, query) => boolean | name match | Custom search matcher. |


<SurveyWidget /> props

Shows the right live survey to each visitor, based on the targeting set in the admin. Render it once, near the root of your public layout.

<SurveyWidget
  siteKey="pk_..."
  apiUrl="https://api.example.com"
  traits={{ userKey: user.id, plan: user.plan }}
  accentColor="#0D447A"
/>

| Prop | Type | Default | Description | | --------------- | ----------------------------------------------------- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | siteKey | string | the server's only install | The public pk_ site key. Optional when one API serves one site. | | apiUrl | string | "/api/survey" | API origin or full base. https://api.example.com becomes https://api.example.com/api/survey. | | traits | Record<string, string \| number \| boolean \| Date> | none | Visitor traits for this render, merged with traits set through setVisitorTraits. Used for audience targeting and shown on responses. | | respondentKey | string | the userKey trait | Stable id for the person answering. The server uses it to remember "already answered" across devices. | | fontFamily | string | survey font | Font inside the survey card. "inherit" uses the page font. | | accentColor | string (hex) | survey colour | (next release) Replaces every survey's own colour, for example "#0D447A". An invalid value is ignored. |

Notes:

  • The widget renders in an isolated overlay, so your page CSS does not affect it.
  • It hides itself while <SurveyAdmin /> is mounted on the same page.
  • It follows client-side navigation (pushState, replaceState, back and forward) and re-checks URL targeting on every route change.

initSurvey() (no React)

For pages that are not React apps. Mounts one widget and returns { unmount() }. Calling it again returns the same instance.

import { initSurvey } from "@scopmx/surveybase-widget/widget";

const survey = initSurvey({
  siteKey: "pk_...",
  apiUrl: "https://api.example.com",
  traits: { userKey: "42" },
  accentColor: "#0D447A"
});
// later: survey.unmount();

| Option | Description | | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | | container | Element to render into. Defaults to #surveybase, or a new <div> appended to <body>. | | siteKey, apiUrl, respondentKey, fontFamily, accentColor | Same as the <SurveyWidget /> props. | | traits | Stored with setVisitorTraits before the widget mounts. |

initSurvey also installs the global visitor API on window.surveybase (see below).


Visitor traits

Traits describe the visitor (for example userKey, plan, email). They are used for audience targeting, shown on each response under "Visitor attributes", and searchable in results.

import { setVisitorTraits, getVisitorTraits, resetVisitor } from "@scopmx/surveybase-widget/widget";

setVisitorTraits({ userKey: "42", plan: "pro" }); // merge
setVisitorTraits({ plan: null }); // remove a key
getVisitorTraits(); // { userKey: "42" }
resetVisitor(); // clear all (for example on logout)

| Function | Description | | -------------------------- | ------------------------------------------------------------------------------------------------------------- | | setVisitorTraits(traits) | Merges traits. null, undefined or "" removes a key. Returns the stored traits. | | getVisitorTraits() | Returns the stored traits. | | resetVisitor() | Clears every trait. | | installVisitorApi() | Adds setVisitorTraits, getVisitorTraits and resetVisitor to window.surveybase for non-module scripts. |

Rules:

  • Keys: letters, numbers, _ and -, up to 64 characters. At most 30 keys.
  • Values: strings (trimmed, up to 500 characters), finite numbers, booleans or Date (sent as ISO text). Anything else is dropped.
  • Storage: sessionStorage (surveybase:visitor), so traits last for the browser tab session only. They can hold personal data such as an email.
  • userKey is the default respondentKey.

Global API before the script loads:

<script>
  window.surveybase = { traits: { userKey: "42" } }; // applied on load
  // window.surveybase = { resetRequested: true };   // clear on load
</script>

Other exports

| Export | Entry | Description | | ----------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------- | | resetSurveys() | widget | Clears local "answered" and "dismissed" flags and picks a survey again. Useful for testing. | | DEFAULT_API_URL | widget | "/api/survey". | | resolveApiBase(apiUrl) | widget | The URL rule used by apiUrl. | | SmileyFace | widget | The smiley icon: props mood (1–5), size (px, default 40), muted, title. | | SMILEY_SCALES, smileyScale(scale) | widget | Labels for 3- and 5-face scales. | | surveyAdminTheme, surveyAdminPanelTheme | admin | The default MUI themes. | | createSurveyAdminTheme(colors), createSurveyAdminPanelTheme(colors) | admin | Build themes from a colors object, to extend and pass as theme / panelTheme. | | surveyAdminDefaultColors | admin | The colour defaults table above. | | surveyAdminDefaultHeader | admin | The header defaults table above. |

TypeScript definitions ship in types/ for every export.


Browser events and storage

| Name | Kind | Purpose | | ----------------------------------------- | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | surveybase:open | window event (default name) | Opens a survey whose trigger is "event". The name is set per survey (targeting.eventName). Fire it with window.dispatchEvent(new Event("surveybase:open")). | | surveybase:reset | window event | Fired by resetSurveys(). | | surveybase:location | window event | Fired internally after pushState / replaceState. | | surveybase:visitor | sessionStorage | Visitor traits. | | surveybase:freq:<survey>[:<respondent>] | localStorage | "Answered" and "dismissed" timestamps used by show-again rules. | | surveybase:preview-url:<survey> | localStorage | Last preview URL typed in the builder. |


Survey settings reference

These are set in the admin builder. They are listed so you know what the widget will do.

Question types

welcome (intro screen), single, multi, dropdown, text, rating (1–5 stars), nps (0–10), opinion (1–10 scale), ces (1–7 effort), smiley (3 or 5 faces), yes_no, ranking, matrix, contact (name, email, phone, company) and date.

Questions can have logic rules that jump to another question, end the survey, or open a URL.

Design (theme)

| Key | Values | Default | | --------------------- | -------------------------------- | ---------------------------------------- | | primaryColor | hex | #3d2ec5 (or the server defaultTheme) | | position | popup, button, modal | popup | | fontFamily | font list | Inter, system-ui, sans-serif | | cardSize | compact, regular, large | regular | | borderRadius | 0–32 | 20 | | buttonStyle | filled, outline, pill | filled | | animation | slide, fade | slide | | enterFrom, exitTo | left, right, top, bottom | bottom |

Targeting

| Key | Values | Default | | -------------------- | ---------------------------------------------------------------------- | ----------------- | | trigger | immediate, delay, scroll, exit, event | immediate | | delaySeconds | 0–600 | 5 | | scrollPercent | 1–100 | 50 | | urlPatterns | up to 20 paths starting with / | all pages | | device | all, mobile, desktop | all | | traits | up to 10 rules { key, operator: eq \| neq \| contains \| in, value } | none | | traitsMatch | all, any | all | | hideAfterAnswer | boolean | true | | showAgainDays | 0–3650 (0 = never) | 0 | | dismissDays | 0–365 | 7 | | eventName | letters, numbers, : _ - | surveybase:open | | responseLimit | 0–1,000,000 (0 = no limit) | 0 | | startsAt, endsAt | YYYY-MM-DD | none | | timezone | IANA zone, for example Asia/Kolkata | browser zone |


Examples

Admin behind your own login

// Frontend
<SurveyAdmin
  basePath="/admin/surveys-tool"
  getToken={() => auth.getAccessToken()}
  colors={{ primary: "#0D447A" }}
  defaultSurveyColor="#0D447A"
  previewUrl="https://clinic.example.com"
  results={{
    live: false,
    traitLabels: { userKey: "Patient" },
    attributesHint: "Shared by the app when the patient signs in."
  }}
/>
// Backend: see @scopmx/surveybase-server
app.use("/api/survey", surveyApi({ authorize: (req) => verifyAdmin(req) }));

Widget on a public site

<SurveyWidget siteKey="pk_..." traits={{ userKey: user?.id }} accentColor="#0D447A" />

Open a survey from a button

Set the survey trigger to Event in the admin, then:

button.addEventListener("click", () => window.dispatchEvent(new Event("surveybase:open")));