Build a Real-time Speech Recognition Search Box with Next.js and Tailwind CSS

Building a real-time speech recognition search box using Next.js, Tailwind CSS, and the react-speech-recognition library

Algogenz logo

2m · 6min read

Let's build a real-time speech recognition search box using Next.js and Tailwind CSS. We'll leverage the react-speech-recognition library for managing speech recognition. This guide will cover setting up the project, writing the code, and understanding the logic behind it.


Prerequisites

Before you begin, ensure you have the following:

  • Node.js installed
  • npm or yarn as your package manager
  • Basic knowledge of React
  • Familiarity with Tailwind CSS


Step 1: Set Up the Project

Create a new Next.js project:

npx create-next-app@latest speech-recognition-search-box
cd speech-recognition-search-box

Install necessary dependencies:

npm install react-speech-recognition @mui/icons-material react-icons

Set up Tailwind CSS:

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

Configure tailwind.config.js:

module.exports = {
  content: [
    './pages/**/*.{js,ts,jsx,tsx}',
    './components/**/*.{js,ts,jsx,tsx}',
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};

Create styles/globals.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

Step 2: Create the Search Box Component

Create components/SearchBox.js:

'use client';
import React, { useState, useEffect, useRef } from'react';
import SearchIcon from '@mui/icons-material/Search';
import Mic from '@mui/icons-material/Mic';
import { RiEqualizerLine } from'react-icons/ri';
import SpeechRecognition, { useSpeechRecognition } from'react-speech-recognition';

This part imports necessary libraries and components:

  • React for creating the component.
  • useState, useEffect, useRef hooks from React for managing component state and lifecycle.
  • SearchIcon, Mic from Material-UI for icons.
  • RiEqualizerLine from react-icons for another icon.
  • SpeechRecognition, useSpeechRecognition from react-speech-recognition for managing speech recognition.


Define the Icon component:

const Icon = ({ children, onClick, ariaLabel, className }) => (
    <div onClick={onClick} aria-label={ariaLabel} className={`cursor-pointer ${className}`}>
        {children}
    </div>
);

This component renders an icon with common styles and behavior. It accepts:

  • children: The icon component to render.
  • onClick: Click event handler.
  • ariaLabel: Accessibility label.
  • className: Additional CSS classes.


Define the SearchBox component:

export const SearchBox = () => {
    const [isModalOpen, setIsModalOpen] = useState(false);
    const [inputValue, setInputValue] = useState('');
    const { transcript, listening, resetTranscript } = useSpeechRecognition();
    const timeoutRef = useRef(null);

    const startListening = () => {
        resetTranscript();
        SpeechRecognition.startListening({ continuous: true });
        setIsModalOpen(true);
        resetTimeout();
    };

    const stopListening = () => {
        SpeechRecognition.stopListening();
        setIsModalOpen(false);
        clearTimeout(timeoutRef.current);
    };

    const handleMicClick = () => {
        if (listening) {
            stopListening();
        } else {
            startListening();
        }
    };

    const handleCloseModal = () => {
        stopListening();
    };

    const handleInputChange = (event) => {
        setInputValue(event.target.value);
    };

    const resetTimeout = () => {
        clearTimeout(timeoutRef.current);
        if (listening) {
            stopListening();
        }, [transcript]);

    useEffect(() => {
        if (!isModalOpen && transcript) {
            setInputValue(transcript.trim());
            resetTranscript();
        }, [isModalOpen, transcript, resetTranscript]);

    if (!SpeechRecognition.browserSupportsSpeechRecognition()) {
        return <span>Browser doesn't support speech recognition.</span>;
    };

This checks if the browser supports speech recognition and displays a message if it doesn't.


Render the component:

<div>
    <div className="flex text-center w-full max-w-80 items-center border dark:border-none dark:bg-slate-700 shadow-slate-400 bg-slate-700 p-6 rounded-full px-4 py-1 mr-1 mr-2 mb-4 bg-green-500 animate-pulse' : ''});

This code renders the search box and modal:

  • Search Icon: Opens the modal and starts listening.
  • Microphone Icon: Toggles speech recognition.
  • Filter Icon: Placeholder for additional functionality.


Handle unsupported browsers:

if (!SpeechRecognition.browserSupportsSpeechRecognition()) {
    return <span>Browser doesn't support speech recognition.</span>;
}

This checks if the browser supports speech recognition and displays a message if it doesn't.


Render the component:

    return (
        <div>
            <div className="flex text-center w-full max-w-80 items-center border dark:border-none dark:bg-slate-700 shadow-slate-400 bg-slate-100 rounded-full px-4 py-1">
                <Icon onClick={handleMicClick} ariaLabel={'Search icon'} className="text-gray-400 mr-2">
                    <SearchIcon />
                </Icon>
                <input
                    type="text"
                    placeholder="Search"
                    value={inputValue}
                    onChange={handleInputChange}
                    className="outline-none bg-transparent text-gray-700 dark:text-gray-200 w-full"
                />
                <Icon onClick={handleMicClick} ariaLabel={'Microphone icon'} className="text-gray-400 mr-1">
                    <Mic />
                </Icon>
                <Icon onClick={handleMicClick} ariaLabel={'Filter icon'} className="text-gray-400 mr-1">
                    <RiEqualizerLine />
                </Icon>
            </div>
            {isModalOpen && (
                <div className="fixed inset-0 flex items-center justify-center z-50">
                    <div className="bg-white dark:bg-slate-700 p-6 rounded-lg shadow-lg w-4/5 max-w-md">
                        <h2 className="text-lg font-bold mb-4">Listening...</h2>
                        <div className="flex items-center justify-center space-x-2 mb-4">
                            <div className={`w-2 h-10 bg-green-500 ${listening ? 'animate-pulse' : ''}`}></div>
                            <div className={`w-2 h-10 bg-green-500 ${listening ? 'animate-pulse' : ''}`}></div>
                            <div className={`w-2 h-10 bg-green-500 ${listening ? 'animate-pulse' : ''}`}></div>
                            <div className={`w-2 h-10 bg-green-500 ${listening ? 'animate-pulse' : ''}`}></div>
                            <div className={`w-2 h-10 bg-green-500 ${listening ? 'animate-pulse' : ''}`}></div>
                        </div>
                        <p className="text-gray-700 dark:text-gray-200">{transcript}</p>
                        <button
                            className="mt-4 bg-red-500 text-white px-4 py-2 rounded-full"
                            onClick={handleCloseModal}
                        >
                            Close
                        </button>
                    </div>
                </div>
            )}
        </div>
    );
};

This code renders the search box and modal:

  • Search Box: Contains the input field and icons.
  • Search Icon: Opens the modal and starts listening.
  • Microphone Icon: Toggles speech recognition.
  • Filter Icon: Placeholder for additional functionality.
  • Modal: Displays when isModalOpen is true.
  • Listening Indicator: Shows animated bars when listening.
  • Transcript: Displays the recognized speech.
  • Close Button: Closes the modal and stops speech recognition.


Here is the complete code for reference:

'use client';
import React, { useState, useEffect, useRef } from 'react';
import SearchIcon from '@mui/icons-material/Search';
import Mic from '@mui/icons-material/Mic';
import { RiEqualizerLine } from 'react-icons/ri';
import SpeechRecognition, { useSpeechRecognition } from 'react-speech-recognition';

const Icon = ({ children, onClick, ariaLabel, className }) => (
    <div onClick={onClick} aria-label={ariaLabel} className={`cursor-pointer ${className}`}>
        {children}
    </div>
);

export const SearchBox = () => {
    const [isModalOpen, setIsModalOpen] = useState(false);
    const [inputValue, setInputValue] = useState('');
    const { transcript, listening, resetTranscript } = useSpeechRecognition();
    const timeoutRef = useRef(null);

    const startListening = () => {
        resetTranscript();
        SpeechRecognition.startListening({ continuous: true });
        setIsModalOpen(true);
        resetTimeout();
    };

    const stopListening = () => {
        SpeechRecognition.stopListening();
        setIsModalOpen(false);
        clearTimeout(timeoutRef.current);
    };

    const handleMicClick = () => {
        if (listening) {
            stopListening();
        } else {
            startListening();
        }
    };

    const handleCloseModal = () => {
        stopListening();
    };

    const handleInputChange = (event) => {
        setInputValue(event.target.value);
    };

    const resetTimeout = () => {
        clearTimeout(timeoutRef.current);
        timeoutRef.current = setTimeout(() => {
            if (listening) {
                stopListening();
            }
        }, 3000);
    };

    useEffect(() => {
        if (listening) {
            resetTimeout();
        }
    }, [transcript]);

    useEffect(() => {
        if (!isModalOpen && transcript) {
            setInputValue(transcript.trim());
            resetTranscript();
       

 }
    }, [isModalOpen, transcript, resetTranscript]);

    if (!SpeechRecognition.browserSupportsSpeechRecognition()) {
        return <span>Browser doesn't support speech recognition.</span>;
    }

    return (
        <div>
            <div className="flex text-center w-full max-w-80 items-center border dark:border-none dark:bg-slate-700 shadow-slate-400 bg-slate-100 rounded-full px-4 py-1">
                <Icon onClick={handleMicClick} ariaLabel={'Search icon'} className="text-gray-400 mr-2">
                    <SearchIcon />
                </Icon>
                <input
                    type="text"
                    placeholder="Search"
                    value={inputValue}
                    onChange={handleInputChange}
                    className="outline-none bg-transparent text-gray-700 dark:text-gray-200 w-full"
                />
                <Icon onClick={handleMicClick} ariaLabel={'Microphone icon'} className="text-gray-400 mr-1">
                    <Mic />
                </Icon>
                <Icon onClick={handleMicClick} ariaLabel={'Filter icon'} className="text-gray-400 mr-1">
                    <RiEqualizerLine />
                </Icon>
            </div>
            {isModalOpen && (
                <div className="fixed inset-0 flex items-center justify-center z-50">
                    <div className="bg-white dark:bg-slate-700 p-6 rounded-lg shadow-lg w-4/5 max-w-md">
                        <h2 className="text-lg font-bold mb-4">Listening...</h2>
                        <div className="flex items-center justify-center space-x-2 mb-4">
                            <div className={`w-2 h-10 bg-green-500 ${listening ? 'animate-pulse' : ''}`}></div>
                            <div className={`w-2 h-10 bg-green-500 ${listening ? 'animate-pulse' : ''}`}></div>
                            <div className={`w-2 h-10 bg-green-500 ${listening ? 'animate-pulse' : ''}`}></div>
                            <div className={`w-2 h-10 bg-green-500 ${listening ? 'animate-pulse' : ''}`}></div>
                            <div className={`w-2 h-10 bg-green-500 ${listening ? 'animate-pulse' : ''}`}></div>
                        </div>
                        <p className="text-gray-700 dark:text-gray-200">{transcript}</p>
                        <button
                            className="mt-4 bg-red-500 text-white px-4 py-2 rounded-full"
                            onClick={handleCloseModal}
                        >
                            Close
                        </button>
                    </div>
                </div>
            )}
        </div>
    );
};

We have now built a real-time speech recognition search box using Next.js, Tailwind CSS, and the react-speech-recognition library. This component enhances user interaction by providing an accessible and interactive search feature.

Recommended

TypeScript Types vs Interfaces

4m · 5min read

Web Development

TypeScript Types vs Interfaces

Types vs Interfaces: Types in TypeScript are more flexible and can define a wider range of data types, including primitives, unions, intersections, tuples, and more, while interfaces are primarily used to describe the shape of objects and support declaration merging and extending, making them ideal for object-oriented programming and complex data structures

The this Keyword in JavaScript

5m · 3min read

Web Development

The this Keyword in JavaScript

The this keyword in JavaScript is a reference to the object that a function is a property of. It's a fundamental concept in JavaScript, especially in object-oriented programming. Unlike in languages like Java, C#, or PHP, where this typically refers to the current instance of the class, JavaScript's this behaves differently and can be quite versatile.

Next pages: