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
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.
Related Tags
Recommended