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

voice-feedback-widget

v1.0.4

Published

A beautiful, accessible voice feedback widget for React applications

Readme

Voice Feedback Widget

A beautiful, accessible voice feedback widget for React applications. This package provides a complete feedback form with voice recording capabilities, built with modern React patterns and accessibility in mind.

Features

  • 🎤 Voice Recording: Record audio feedback with live waveform visualization
  • 📝 Text Input: Optional text feedback alongside voice
  • 🎨 Beautiful UI: Modern, minimal design with smooth animations
  • Accessible: Full keyboard navigation and screen reader support
  • 📱 Responsive: Works on desktop and mobile devices
  • 🎯 TypeScript: Fully typed for better developer experience
  • 🔧 Customizable: Extensive customization options
  • Performance: Optimized with TanStack React Query and efficient rendering
  • 🎵 Live Waveform: Real-time audio visualization during recording
  • 🎛️ Microphone Selection: Choose from available audio input devices
  • ⏱️ Configurable Duration: Set custom maximum recording duration
  • 📦 Minimizable: Collapsible widget interface

Installation

npm install voice-feedback-widget
# or
yarn add voice-feedback-widget
# or
pnpm add voice-feedback-widget

Quick Start

import { VoiceFeedbackWidget } from "voice-feedback-widget";

function App() {
  return (
    <div>
      <h1>My App</h1>

      <VoiceFeedbackWidget
        className="dark"
        options={{
          apiEndpoint: "/api/feedback",
          onSuccess: (response) => {
            console.log("Feedback submitted:", response);
          },
          onError: (error) => {
            console.error("Submission failed:", error);
          },
        }}
      />
    </div>
  );
}

Props

VoiceFeedbackWidget

The component accepts two props:

| Prop | Type | Description | | ----------- | -------------------------- | ---------------------------- | | className | string | Custom CSS class name | | options | VoiceFeedbackWidgetProps | Configuration options object |

VoiceFeedbackWidgetProps

| Prop | Type | Default | Description | | ---------------------- | ------------------------------------------------ | -------------------------------------------------------- | ------------------------------------------------ | | apiEndpoint | string | "https://voice-feedback-agent.vercel.app/api/feedback" | API endpoint URL for submitting feedback | | onSuccess | (response: FeedbackSubmissionResponse) => void | - | Callback when feedback is successfully submitted | | onError | (error: Error) => void | - | Callback when feedback submission fails | | title | string | "Feedback Form" | Widget title | | description | string | "Share your feedback with us" | Widget description | | defaultMinimized | boolean | false | Whether to show the widget minimized by default | | maxRecordingDuration | number | 90 | Maximum recording duration in seconds | | form | FormFieldConfig | See below | Form field configuration |

FormFieldConfig

| Prop | Type | Default | Description | | --------------- | ------------------- | ------------------- | ----------------------------------------------------------- | | show | FormField[] | ["name", "email"] | Array of fields to display: "name", "email", "phone" | | defaultValues | FormDefaultValues | - | Default values to prefill (works even if fields are hidden) | | required | boolean | true | Whether visible fields are required |

FormField Type

Available field options: "name" | "email" | "phone"

Form Configuration Examples

Hide All User Fields (Feedback Only)

<VoiceFeedbackWidget
  className="dark"
  options={{
    apiEndpoint: "/api/feedback",
    form: {
      show: [], // No fields shown, only feedback input
    },
  }}
/>

Show Specific Fields

<VoiceFeedbackWidget
  className="dark"
  options={{
    apiEndpoint: "/api/feedback",
    form: {
      show: ["name", "email"], // Only show name and email
      required: true, // Make them required
    },
  }}
/>

Prefill Values (Including Hidden Fields)

<VoiceFeedbackWidget
  className="dark"
  options={{
    apiEndpoint: "/api/feedback",
    form: {
      show: ["name"], // Only show name
      defaultValues: {
        name: "John Doe",
        email: "[email protected]", // Hidden but will be sent with form
        phone: "+1234567890", // Hidden but will be sent with form
      },
    },
  }}
/>

Make Fields Optional

<VoiceFeedbackWidget
  className="dark"
  options={{
    apiEndpoint: "/api/feedback",
    form: {
      show: ["name", "email", "phone"],
      required: false, // All fields are optional
    },
  }}
/>

Complete Example with All Options

<VoiceFeedbackWidget
  className="dark"
  options={{
    apiEndpoint: "/api/feedback",
    title: "Voice Feedback",
    description: "Share your thoughts with us",
    maxRecordingDuration: 60,
    defaultMinimized: false,
    onSuccess: (response) => {
      console.log("Feedback submitted successfully:", response);
    },
    onError: (error) => {
      console.error("Failed to submit feedback:", error);
    },
    form: {
      show: ["name", "phone"],
      defaultValues: {
        name: "John Doe",
        email: "[email protected]",
      },
      required: true,
    },
  }}
/>

API Integration

The widget sends data to your API endpoint using FormData with the following fields:

  • name (string, optional): User's name
  • email (string, optional): User's email
  • phone (string, optional): User's phone number
  • message (string, optional): Text feedback
  • audio (Blob, optional): Audio recording as WebM file

Important Notes:

  • All user fields (name, email, phone) are optional
  • Either message or audio must be provided (or both)
  • The widget includes a 30-second timeout for API requests
  • Audio files are sent as WebM format with filename feedback.webm

Expected API Response

Your API should return a JSON response with this structure:

interface FeedbackSubmissionResponse {
  success: boolean;
  message: string;
  feedbackId?: string; // Optional unique identifier
}

Example API Handler (Next.js)

// app/api/feedback/route.ts
import { NextRequest, NextResponse } from "next/server";

export async function POST(request: NextRequest) {
  try {
    const formData = await request.formData();

    const name = formData.get("name") as string | null;
    const email = formData.get("email") as string | null;
    const phone = formData.get("phone") as string | null;
    const message = formData.get("message") as string | null;
    const audio = formData.get("audio") as File | null;

    // Validate that either message or audio is provided
    if (!message?.trim() && !audio) {
      return NextResponse.json(
        { success: false, error: "Either text or audio feedback is required" },
        { status: 400 }
      );
    }

    // Process the feedback data
    // Save to database, send notifications, etc.
    console.log({
      name,
      email,
      phone,
      message: message || "[Audio feedback]",
      audioReceived: !!audio,
      audioSize: audio?.size,
    });

    return NextResponse.json({
      success: true,
      message: "Feedback submitted successfully",
      feedbackId: `feedback_${Date.now()}`,
    });
  } catch (error) {
    console.error("Feedback submission error:", error);
    return NextResponse.json(
      { success: false, error: "Failed to submit feedback" },
      { status: 500 }
    );
  }
}

Advanced Usage

Custom Styling

The widget uses Tailwind CSS with CSS custom properties for theming. You can customize the appearance by:

  1. Using the className prop to apply custom styles:
<VoiceFeedbackWidget
  className="custom-widget-theme"
  options={{ apiEndpoint: "/api/feedback" }}
/>
  1. Overriding CSS custom properties in your global styles:
:root {
  --background: 0 0% 100%;
  --foreground: 222.2 84% 4.9%;
  --card: 0 0% 100%;
  --card-foreground: 222.2 84% 4.9%;
  --primary: 222.2 47.4% 11.2%;
  --primary-foreground: 210 40% 98%;
  --border: 214.3 31.8% 91.4%;
  --input: 214.3 31.8% 91.4%;
  --ring: 222.2 84% 4.9%;
  --muted: 210 40% 98%;
  --muted-foreground: 215.4 16.3% 46.9%;
  --destructive: 0 84.2% 60.2%;
  --destructive-foreground: 210 40% 98%;
}

Widget Positioning

The widget is positioned as a fixed element in the bottom-right corner by default. You can customize its position:

.voice-feedback-widget {
  position: fixed;
  bottom: 1rem;
  right: 1rem;
  z-index: 50;
}

/* Custom positioning */
.custom-position .voice-feedback-widget {
  bottom: 2rem;
  left: 2rem;
  right: auto;
}

Audio Recording Features

The widget includes advanced audio recording capabilities:

  • Live waveform visualization during recording
  • Microphone device selection from available audio inputs
  • Configurable recording duration (default: 90 seconds)
  • Audio playback to review recordings before submission
  • Automatic timeout to prevent overly long recordings
  • WebM format for optimal browser compatibility

Form Validation

The widget includes comprehensive form validation:

  • Dynamic schema generation based on visible fields
  • Real-time validation with error messages
  • Required field validation (configurable)
  • Email format validation
  • Character limits for all text fields
  • Audio or text requirement (at least one must be provided)

Requirements

  • React 18+
  • Modern browser with Web Audio API support
  • Microphone permissions
  • HTTPS connection (required for microphone access in production)

Dependencies

The widget uses the following key dependencies:

  • TanStack React Query - For API state management
  • React Hook Form - For form handling and validation
  • Zod - For schema validation
  • Motion - For smooth animations
  • Lucide React - For icons
  • Tailwind CSS - For styling

Browser Support

  • Chrome 66+ (recommended)
  • Firefox 60+
  • Safari 11+
  • Edge 79+

Note: Audio recording requires HTTPS in production environments.

TypeScript Support

The package is fully typed with TypeScript definitions included. All props, callbacks, and return types are properly typed for excellent developer experience.

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Changelog

v1.0.0

  • Initial release
  • Voice recording with live waveform
  • Configurable form fields
  • Minimizable widget interface
  • Full TypeScript support
  • Comprehensive accessibility features