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 🙏

© 2025 – Pkg Stats / Ryan Hefner

react-voice-recorder-kit

v1.1.4

Published

React voice recorder component and hook with waveform visualization

Readme

react-voice-recorder-kit

A lightweight React library for voice recording with audio waveform visualization and no UI framework dependencies

  • No UI framework dependencies (Pure React + Inline CSS)
  • Animated audio waveform visualization (40 bars)
  • Ready-to-use component
  • Fully customizable hook
  • TypeScript support
  • Compatible with Next.js, Vite, CRA, and more

Screenshots

Initial State (Ready to Record)

Recording in Progress

Recording

Paused State

Paused

Recorded Audio Ready to Play (Custom Styled)

Completed Recording Custom


Installation

npm install react-voice-recorder-kit
# or
pnpm add react-voice-recorder-kit
# or
yarn add react-voice-recorder-kit

Requires React 18+


Quick Start (Using Component)

'use client'

import { useState } from 'react'
import { VoiceRecorder } from 'react-voice-recorder-kit'

export default function Page() {
  const [file, setFile] = useState<File | null>(null)
  const [url, setUrl] = useState<string | null>(null)

  return (
    <div style={{ padding: 20, maxWidth: 600, margin: '0 auto' }}>
      <h1>React Voice Recorder Kit</h1>

      <VoiceRecorder
        autoStart={false}
        onStop={(audioFile, audioUrl) => {
          setFile(audioFile)
          setUrl(audioUrl)
        }}
        onDelete={() => {
          setFile(null)
          setUrl(null)
        }}
      />

      {url && (
        <div style={{ marginTop: 16 }}>
          <audio controls src={url} style={{ width: '100%' }} />
          {file && (
            <p style={{ fontSize: 12, marginTop: 8 }}>
              File name: {file.name} | Size: {file.size} bytes
            </p>
          )}
        </div>
      )}
    </div>
  )
}

Usage in Next.js (App Router)

'use client'

import { VoiceRecorder } from 'react-voice-recorder-kit'

export default function VoicePage() {
  return (
    <div style={{ padding: 24 }}>
      <VoiceRecorder autoStart={true} />
    </div>
  )
}

Component API

Main Props

| Prop | Type | Default | Description | | --------- | --------------------------------- | --------- | ---------------------------------------------- | | autoStart | boolean | true | Auto-start recording on mount | | onStop | (file: File, url: string) => void | undefined | Callback after recording stops | | onDelete | () => void | undefined | Callback after recording is deleted | | width | string | number | '100%' | Component width | | height | string | number | undefined | Component height | | style | CSSProperties | undefined | Additional styles for container |

Styling Props

| Prop | Type | Default | Description | | --------------------------- | --------------------------------------- | ------------------------------------------------------------ | ------------------------------------ | | backgroundColor | string | '#ffffff' | Background color | | borderColor | string | '#e5e7eb' | Border color | | borderRadius | string | number | 4 | Border radius | | padding | string | number | '6px 10px' | Internal padding | | gap | string | number | 8 | Gap between elements | | recordingIndicatorColor | string | '#ef4444' | Recording indicator color | | idleIndicatorColor | string | '#9ca3af' | Idle indicator color | | timeTextColor | string | undefined | Time text color | | timeFontSize | string | number | 12 | Time font size | | timeFontWeight | string | number | 500 | Time font weight | | timeFontFamily | string | 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif' | Time font family | | visualizerBarColor | string | (level: number, index: number) => string | '#4b5563' | Waveform bar color | | visualizerBarWidth | number | 3 | Waveform bar width | | visualizerBarGap | number | 4 | Gap between bars | | visualizerBarHeight | number | 40 | Waveform bar height | | visualizerHeight | number | 40 | Total waveform height | | buttonSize | number | 28 | Button size | | buttonBackgroundColor | string | '#ffffff' | Button background color | | buttonBorderColor | string | '#e5e7eb' | Button border color | | buttonBorderRadius | string | number | 999 | Button border radius | | buttonHoverBackgroundColor | string | undefined | Button hover background color | | buttonGap | number | 4 | Gap between buttons | | errorTextColor | string | '#dc2626' | Error text color | | errorFontSize | string | number | 10 | Error font size | | errorFontFamily | string | 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif' | Error font family | | iconSize | number | 18 | Icon size | | iconColor | string | undefined | Icon color |

Custom Icon Props

| Prop | Type | Default | Description | | --------------- | --------- | --------- | ------------------------ | | customPlayIcon | ReactNode | undefined | Custom play icon | | customPauseIcon | ReactNode | undefined | Custom pause icon | | customStopIcon | ReactNode | undefined | Custom stop icon | | customResumeIcon| ReactNode | undefined | Custom resume icon | | customDeleteIcon| ReactNode | undefined | Custom delete icon | | customRepeatIcon| ReactNode | undefined | Custom repeat icon |


Component Usage Examples

Example 1: Simple Usage

import { VoiceRecorder } from 'react-voice-recorder-kit'

function SimpleRecorder() {
  return <VoiceRecorder />
}

Example 2: Custom Styling

import { VoiceRecorder } from 'react-voice-recorder-kit'

function CustomStyledRecorder() {
  return (
    <VoiceRecorder
      backgroundColor="#f3f4f6"
      borderColor="#d1d5db"
      borderRadius={12}
      recordingIndicatorColor="#10b981"
      visualizerBarColor="#6366f1"
      buttonBackgroundColor="#ffffff"
      buttonHoverBackgroundColor="#f9fafb"
    />
  )
}

Example 3: Using with Callbacks

import { useState } from 'react'
import { VoiceRecorder } from 'react-voice-recorder-kit'

function RecorderWithCallbacks() {
  const [audioFile, setAudioFile] = useState<File | null>(null)

  return (
    <VoiceRecorder
      autoStart={false}
      onStop={(file, url) => {
        console.log('Recording stopped:', file.name)
        setAudioFile(file)
      }}
      onDelete={() => {
        console.log('Recording deleted')
        setAudioFile(null)
      }}
    />
  )
}

Example 4: Dynamic Color Waveform

import { VoiceRecorder } from 'react-voice-recorder-kit'

function DynamicColorRecorder() {
  return (
    <VoiceRecorder
      visualizerBarColor={(level, index) => {
        const hue = (level * 120).toString()
        return `hsl(${hue}, 70%, 50%)`
      }}
    />
  )
}

Using the Hook (useVoiceRecorder)

For full control over the UI, you can use the hook directly.

Import

import { useVoiceRecorder } from 'react-voice-recorder-kit'

Options

type UseVoiceRecorderOptions = {
  autoStart?: boolean
  onStop?: (file: File, url: string) => void
  onDelete?: () => void
}

Return Values

type UseVoiceRecorderReturn = {
  state: RecorderState
  isRecording: boolean
  isStopped: boolean
  isTemporaryStopped: boolean
  isPlaying: boolean
  isPaused: boolean
  seconds: number
  levels: number[]
  error: string | null
  audioUrl: string | null
  audioFile: File | null
  start: () => void
  handlePause: () => void
  handleStopTemporary: () => void
  handleStop: () => void
  handleResume: () => void
  handlePreviewPlay: () => void
  handlePlay: () => void
  handleRestart: () => void
  handleDelete: () => void
  handleRecordAgain: () => void
}

| Property | Type | Description | | ----------------- | -------------- | ---------------------------------------------- | | state | RecorderState | Current state: 'idle' | 'recording' | 'paused' | 'reviewing' | 'playing' | | isRecording | boolean | Is currently recording | | isStopped | boolean | Is recording stopped | | isTemporaryStopped| boolean | Is recording temporarily stopped | | isPlaying | boolean | Is currently playing | | isPaused | boolean | Is recording paused | | seconds | number | Time in seconds | | levels | number[] | Array of 40 audio levels (0 to 1) | | error | string | null | Error message if any | | audioUrl | string | null | URL of recorded audio file | | audioFile | File | null | Recorded audio file | | start | () => void | Start recording | | handlePause | () => void | Pause recording | | handleStopTemporary| () => void | Temporary stop and review | | handleStop | () => void | Stop and save recording | | handleResume | () => void | Resume recording after pause | | handlePreviewPlay | () => void | Play preview (in paused state) | | handlePlay | () => void | Play recorded file | | handleRestart | () => void | Restart recording | | handleDelete | () => void | Delete recording and return to initial state | | handleRecordAgain | () => void | Record again (same as handleRestart) |


Complete Hook Usage Example

'use client'

import { useVoiceRecorder } from 'react-voice-recorder-kit'

export default function CustomRecorder() {
  const {
    state,
    isRecording,
    isPaused,
    isStopped,
    isPlaying,
    seconds,
    levels,
    audioUrl,
    audioFile,
    error,
    start,
    handlePause,
    handleResume,
    handleStop,
    handlePlay,
    handleDelete,
    handleRestart
  } = useVoiceRecorder({ autoStart: false })

  const formatTime = (secs: number) => {
    const minutes = Math.floor(secs / 60)
    const sec = secs % 60
    return `${minutes}:${sec.toString().padStart(2, '0')}`
  }

  return (
    <div style={{ padding: 16, maxWidth: 600 }}>
      <h2>Custom Voice Recorder</h2>

      <div style={{ marginBottom: 8 }}>
        Status: {state} | Time: {formatTime(seconds)}
      </div>

      <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
        {!isRecording && !isStopped && (
          <button onClick={start}>Start</button>
        )}

        {isRecording && !isPaused && (
          <>
            <button onClick={handlePause}>Pause</button>
            <button onClick={handleStop}>Stop & Save</button>
            <button onClick={handleRestart}>Restart</button>
          </>
        )}

        {isPaused && (
          <>
            <button onClick={handleResume}>Resume</button>
            <button onClick={handleRestart}>Restart</button>
            <button onClick={handleStop}>Stop & Save</button>
          </>
        )}

        {isStopped && audioUrl && (
          <>
            <button onClick={handlePlay}>
              {isPlaying ? 'Stop Playback' : 'Play'}
            </button>
            <button onClick={handleDelete}>Delete</button>
            <button onClick={handleRestart}>Record Again</button>
          </>
        )}
      </div>

      {error && (
        <div style={{ color: 'red', marginTop: 8 }}>
          {error}
        </div>
      )}

      <div
        style={{
          marginTop: 20,
          height: 40,
          display: 'flex',
          alignItems: 'flex-end',
          gap: 4,
          overflow: 'hidden'
        }}
      >
        {levels.map((level, index) => {
          const height = 5 + level * 35
          return (
            <div
              key={index}
              style={{
                width: 4,
                height,
                borderRadius: 4,
                background: '#444'
              }}
            />
          )
        })}
      </div>

      {audioUrl && (
        <div style={{ marginTop: 16 }}>
          <audio controls src={audioUrl} style={{ width: '100%' }} />
        </div>
      )}
    </div>
  )
}

Recording States (RecorderState)

The component and hook have 5 different states:

  • idle: Initial state, ready to start
  • recording: Currently recording
  • paused: Recording paused (can be resumed)
  • reviewing: Recording completed and under review
  • playing: Playing recorded file

Features

  • Voice recording using MediaRecorder API
  • Animated audio waveform visualization during recording and playback
  • Support for pause and resume
  • Support for playing recorded files
  • Time display in MM:SS format
  • Error handling and error message display
  • Ready-to-use UI with control buttons
  • Fully customizable styling and sizing
  • No external dependencies
  • Support for custom icons
  • Dynamic color waveforms

Important Notes

  1. Requires microphone access in the browser
  2. Recorded files are saved in WebM format
  3. In paused state, you can play a preview of the recording
  4. You can dynamically set bar colors using visualizerBarColor
  5. All created URLs are automatically cleaned up

License

MIT