Skip to main content

Development

Implementing a Fuzzy Search in React JS Using Fuse.JS

Istock 1435220822

When setting up client search, we usually rely on string comparison methods like indexOf, contains, etc. These methods work fine, but in real-life situations, users may search using incorrect spellings, jumbled sentences, and so on. Fuzzy Search helps us solve these issues.

Fuzzy Search

Fuzzy Search, also known as Approximate String Matching, is a technique for finding strings that are similar but not exactly the same.

Now, let’s get started with building the application.

We’ll set up a basic page that includes text and a search box.

Fuzz View

Basic React App for Search Demo

A simple React application to demonstrate the search functionality.

Regular Search

Typically, we use string comparison methods like indexOf, contains, etc. These work well, but they fail to provide results if the search query contains spelling mistakes.

Item.js

Displays individual items with their logo, name, and tags.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import React from "react";
const Item = (props) =>{
return(
<div className="item">
<div className="logo">
<img src={props.logo} alt={props.name} />
</div>
<div className="name">
<p>{props.name}</p>
<div className="tags">{props.tags.join(", ")}</div>
</div>
</div>
);
};
export default Item;
import React from "react"; const Item = (props) => { return ( <div className="item"> <div className="logo"> <img src={props.logo} alt={props.name} /> </div> <div className="name"> <p>{props.name}</p> <div className="tags">{props.tags.join(", ")}</div> </div> </div> ); }; export default Item;
import React from "react";

const Item = (props) => {
  return (
    <div className="item">
      <div className="logo">
        <img src={props.logo} alt={props.name} />
      </div>
      <div className="name">
        <p>{props.name}</p>
        <div className="tags">{props.tags.join(", ")}</div>
      </div>
    </div>
  );
};

export default Item;

Data.js

Contains various objects representing different technologies with their names, logos, and tags.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
[
{
"name": "Booty Bounce",
"logo": "https://cdn.worldvectorlogo.com/logos/bootstrap-4.svg",
"tags": ["dancing-css", "boot-stomping", "html-hop", "sass-swing"]
},
{
"name": "React-o-Rama",
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/1280px-React-icon.svg.png",
"tags": ["spinny-js", "state-masters", "fronty-mcfrontend"]
},
{
"name": "Angu-latte",
"logo": "https://angular.io/assets/images/logos/angular/angular.png",
"tags": ["typescript-tornado", "web-zest", "framework-feels"]
},
{
"name": "Vue-tiful Bliss",
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Vue.js_Logo_2.svg/1200px-Vue.js_Logo_2.svg.png",
"tags": ["zen-js", "pretty-view", "framework-bae"]
},
{
"name": "Ant Party",
"logo": "https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg",
"tags": ["system-sass", "fancy-ui", "antd-vibes"]
},
{
"name": "Git Giggle",
"logo": "https://git-scm.com/images/logos/downloads/Git-Icon-1788C.png",
"tags": ["commit-fun", "repo-riff", "c-git-jig"]
},
{
"name": "Gruntastic",
"logo": "https://gruntjs.com/img/og.png",
"tags": ["js-run-wild", "task-mania"]
},
{
"name": "jQuirky",
"logo": "https://w7.pngwing.com/pngs/265/442/png-transparent-jquery-octos-global-javascript-library-document-object-model-ajax-framework-text-trademark-logo-thumbnail.png",
"tags": ["selecto-magic", "fun-dome", "libra-party"]
},
{
"name": "D3-Licious",
"logo": "https://raw.githubusercontent.com/d3/d3-logo/master/d3.png",
"tags": ["data-candy", "viz-buzz", "chart-bliss"]
}
]
[ { "name": "Booty Bounce", "logo": "https://cdn.worldvectorlogo.com/logos/bootstrap-4.svg", "tags": ["dancing-css", "boot-stomping", "html-hop", "sass-swing"] }, { "name": "React-o-Rama", "logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/1280px-React-icon.svg.png", "tags": ["spinny-js", "state-masters", "fronty-mcfrontend"] }, { "name": "Angu-latte", "logo": "https://angular.io/assets/images/logos/angular/angular.png", "tags": ["typescript-tornado", "web-zest", "framework-feels"] }, { "name": "Vue-tiful Bliss", "logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Vue.js_Logo_2.svg/1200px-Vue.js_Logo_2.svg.png", "tags": ["zen-js", "pretty-view", "framework-bae"] }, { "name": "Ant Party", "logo": "https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg", "tags": ["system-sass", "fancy-ui", "antd-vibes"] }, { "name": "Git Giggle", "logo": "https://git-scm.com/images/logos/downloads/Git-Icon-1788C.png", "tags": ["commit-fun", "repo-riff", "c-git-jig"] }, { "name": "Gruntastic", "logo": "https://gruntjs.com/img/og.png", "tags": ["js-run-wild", "task-mania"] }, { "name": "jQuirky", "logo": "https://w7.pngwing.com/pngs/265/442/png-transparent-jquery-octos-global-javascript-library-document-object-model-ajax-framework-text-trademark-logo-thumbnail.png", "tags": ["selecto-magic", "fun-dome", "libra-party"] }, { "name": "D3-Licious", "logo": "https://raw.githubusercontent.com/d3/d3-logo/master/d3.png", "tags": ["data-candy", "viz-buzz", "chart-bliss"] } ]
[
  {
    "name": "Booty Bounce",
    "logo": "https://cdn.worldvectorlogo.com/logos/bootstrap-4.svg",
    "tags": ["dancing-css", "boot-stomping", "html-hop", "sass-swing"]
  },
  {
    "name": "React-o-Rama",
    "logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/1280px-React-icon.svg.png",
    "tags": ["spinny-js", "state-masters", "fronty-mcfrontend"]
  },
  {
    "name": "Angu-latte",
    "logo": "https://angular.io/assets/images/logos/angular/angular.png",
    "tags": ["typescript-tornado", "web-zest", "framework-feels"]
  },
  {
    "name": "Vue-tiful Bliss",
    "logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Vue.js_Logo_2.svg/1200px-Vue.js_Logo_2.svg.png",
    "tags": ["zen-js", "pretty-view", "framework-bae"]
  },
  {
    "name": "Ant Party",
    "logo": "https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg",
    "tags": ["system-sass", "fancy-ui", "antd-vibes"]
  },
  {
    "name": "Git Giggle",
    "logo": "https://git-scm.com/images/logos/downloads/Git-Icon-1788C.png",
    "tags": ["commit-fun", "repo-riff", "c-git-jig"]
  },
  {
    "name": "Gruntastic",
    "logo": "https://gruntjs.com/img/og.png",
    "tags": ["js-run-wild", "task-mania"]
  },
  {
    "name": "jQuirky",
    "logo": "https://w7.pngwing.com/pngs/265/442/png-transparent-jquery-octos-global-javascript-library-document-object-model-ajax-framework-text-trademark-logo-thumbnail.png",
    "tags": ["selecto-magic", "fun-dome", "libra-party"]
  },
  {
    "name": "D3-Licious",
    "logo": "https://raw.githubusercontent.com/d3/d3-logo/master/d3.png",
    "tags": ["data-candy", "viz-buzz", "chart-bliss"]
  }
]

Setting.js

The code implements a search feature in a React app where users can search tools and technologies by name or tags. It filters the data in real time, making the search case-insensitive, and updates the displayed results as the user types in the search box.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import React, { useState } from "react";
import data from "./data.json";
import Item from "./item";
const SettingsPage = () =>{
const [searchData, setSearchData] = useState(data);
const searchItem = (query) =>{
if(!query){
setSearchData(data);
return;
}
query = query.toLowerCase();
const finalResult = [];
data.forEach((item) =>{
if(
item.name.toLowerCase().indexOf(query) !== -1||
item.tags.includes(query)
){
finalResult.push(item);
}
});
setSearchData(finalResult);
};
return(
<div>
<p className="title">Tools & Technologies</p>
<div className="search-container">
<input
type="search"
onChange={(e) =>searchItem(e.target.value)}
placeholder="Search Technologies"
/>
</div>
<div className="item-container">
{searchData.map((item) =>(
<Item {...item} key={item.name} />
))}
</div>
</div>
);
};
export default SettingsPage;
import React, { useState } from "react"; import data from "./data.json"; import Item from "./item"; const SettingsPage = () => { const [searchData, setSearchData] = useState(data); const searchItem = (query) => { if (!query) { setSearchData(data); return; } query = query.toLowerCase(); const finalResult = []; data.forEach((item) => { if ( item.name.toLowerCase().indexOf(query) !== -1 || item.tags.includes(query) ) { finalResult.push(item); } }); setSearchData(finalResult); }; return ( <div> <p className="title">Tools & Technologies</p> <div className="search-container"> <input type="search" onChange={(e) => searchItem(e.target.value)} placeholder="Search Technologies" /> </div> <div className="item-container"> {searchData.map((item) => ( <Item {...item} key={item.name} /> ))} </div> </div> ); }; export default SettingsPage;
import React, { useState } from "react";
import data from "./data.json";
import Item from "./item";

const SettingsPage = () => {
  const [searchData, setSearchData] = useState(data);
  const searchItem = (query) => {
    if (!query) {
      setSearchData(data);
      return;
    }
    query = query.toLowerCase();

    const finalResult = [];
    data.forEach((item) => {
      if (
        item.name.toLowerCase().indexOf(query) !== -1 ||
        item.tags.includes(query)
      ) {
        finalResult.push(item);
      }
    });
    setSearchData(finalResult);
  };
  return (
    <div>
      <p className="title">Tools & Technologies</p>
      <div className="search-container">
        <input
          type="search"
          onChange={(e) => searchItem(e.target.value)}
          placeholder="Search Technologies"
        />
      </div>

      <div className="item-container">
        {searchData.map((item) => (
          <Item {...item} key={item.name} />
        ))}
      </div>
    </div>
  );
};

export default SettingsPage;

Output

Searched bot

Regularsearchop1

Searched booty

Regularsearchop2

I tried searching for “booty” and “bot,” but it only showed results for “booty” because the string comparison method works that way. We need a different string comparison algorithm to make the search fuzzy.

Many algorithms are available for this; you don’t need to study them all to use one.

To turn our search into a fuzzy search engine, we will use Fuse.js.

Fuse.js is a quick and lightweight fuzzy search library that doesn’t require any dependencies.

Integrating Fuse.js with Search

We can easily turn our search into a fuzzy one by making a small change.

setting.js

Implements the fuzzy search functionality using Fuse.js. It allows users to search through the technologies by their name and tags. The search results are refreshed instantly as the user types.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import React, { useState } from "react";
import data from "./data.json";
import Fuse from "fuse.js";
import Item from "./item";
const SettingsPage = () =>{
const [searchData, setSearchData] = useState(data);
const searchItem = (query) =>{
if(!query){
setSearchData(data);
return;
}
const fuse = newFuse(data, {
keys: ["name", "tags"]
});
const result = fuse.search(query);
const finalResult = [];
if(result.length){
result.forEach((item) =>{
finalResult.push(item.item);
});
setSearchData(finalResult);
}else{
setSearchData([]);
}
};
return(
<div>
<p className="title">Tools & Technologies</p>
<div className="search-container">
<input
type="search"
onChange={(e) =>searchItem(e.target.value)}
placeholder="Search Technologies"
/>
</div>
<div className="item-container">
{searchData.map((item) =>(
<Item {...item} key={item.name} />
))}
</div>
</div>
);
};
export default SettingsPage;
import React, { useState } from "react"; import data from "./data.json"; import Fuse from "fuse.js"; import Item from "./item"; const SettingsPage = () => { const [searchData, setSearchData] = useState(data); const searchItem = (query) => { if (!query) { setSearchData(data); return; } const fuse = new Fuse(data, { keys: ["name", "tags"] }); const result = fuse.search(query); const finalResult = []; if (result.length) { result.forEach((item) => { finalResult.push(item.item); }); setSearchData(finalResult); } else { setSearchData([]); } }; return ( <div> <p className="title">Tools & Technologies</p> <div className="search-container"> <input type="search" onChange={(e) => searchItem(e.target.value)} placeholder="Search Technologies" /> </div> <div className="item-container"> {searchData.map((item) => ( <Item {...item} key={item.name} /> ))} </div> </div> ); }; export default SettingsPage;
import React, { useState } from "react";
import data from "./data.json";
import Fuse from "fuse.js";
import Item from "./item";

const SettingsPage = () => {
  const [searchData, setSearchData] = useState(data);
  const searchItem = (query) => {
    if (!query) {
      setSearchData(data);
      return;
    }
    const fuse = new Fuse(data, {
      keys: ["name", "tags"]
    });
    const result = fuse.search(query);
    const finalResult = [];
    if (result.length) {
      result.forEach((item) => {
        finalResult.push(item.item);
      });
      setSearchData(finalResult);
    } else {
      setSearchData([]);
    }
  };
  return (
    <div>
      <p className="title">Tools & Technologies</p>
      <div className="search-container">
        <input
          type="search"
          onChange={(e) => searchItem(e.target.value)}
          placeholder="Search Technologies"
        />
      </div>

      <div className="item-container">
        {searchData.map((item) => (
          <Item {...item} key={item.name} />
        ))}
      </div>
    </div>
  );
};

export default SettingsPage;

Output

Searched bot

Fuseop1

Searched booty

Fuseop2

 

Conclusion

This blog explored how traditional string comparison methods fall short in handling misspellings and inaccuracies in search queries. Fuzzy Search, implemented using Fuse.js, provides an efficient solution by enabling approximate string matching, making the search experience more robust and user-friendly. By integrating Fuse.js into a simple React application, we created a real-time case-insensitive, flexible search functionality that can deliver accurate results even for imperfect queries. This lightweight library is a game-changer for enhancing search features in modern applications.

Thoughts on “Implementing a Fuzzy Search in React JS Using Fuse.JS”

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Sayyad Faizan Ali

Sayyad Faizan Ali is an associate technical consultant at Perficient with over three years of experience in Sitecore, including expertise in Sitecore SCORE and AEM CMS. He has worked on several Sitecore projects and specializes in front-end development with skills in HTML, SCSS, CSS, Bootstrap, JavaScript, jQuery, React, and Next.js.

More from this Author

Follow Us