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

wmk-search

v1.1.4

Published

WMK Menu Classes and Components

Readme

wmk-search

Helps create instant feedback search given a search index from a general GraphQL query of plural content models. Search prompt and results appear in an overlay drawer.

Components

There are two main components and a file with a couple other default utility components (which can be replaced by components of your choice).

SeachSliderOpen

This component renders the button / icon that initiates the search process.

It uses the following props:

interface SearchSliderOpenProps {
  isSearchOpen: boolean;
  setIsSearchOpen: React.Dispatch<React.SetStateAction<boolean>>;
  className?: string;
  children: React.ReactNode;
  style?: CSS.StandardProperties;
}

It is important that a parent component declares and manages the isSearchOpen and setIsSearchOpen state variables. These variables track and modify the open state of the search drawer component.

SearchSliderDrawer

This is where most of the magic happens, and the component was designed to be as flexible as possible, thus there are a lot of props / configuration options:

interface SearchSliderDrawerProps {
  isSearchOpen: boolean;
  setIsSearchOpen: React.Dispatch<React.SetStateAction<boolean>>;
  query: SearchIndexQuery;
  Result?: ({ result }: { result: SearchLink }) => JSX.Element;
  CloseIcon?: () => JSX.Element;
  className?: string;
  style?: CSS.StandardProperties;
  useAlgorithm?: (
    event: React.ChangeEvent<HTMLInputElement>,
    setSearchKey: React.Dispatch<React.SetStateAction<string>>,
    index: SearchIndex,
    setSearchResults: React.Dispatch<React.SetStateAction<IndexedSearch[]>>
  ) => void;
  resultConversion: (results: IndexedSearch) => SearchLink;
}

searchKey, setSearchKey, searchResults and setSearchResults are all managed internally, and do not need to be declared.

[searchKey, setSearchKey];

These variable track and modify the search query the user types into the search input.

[searchResults, setSearchResults];

These variables store and manage the array of objects that match the search key.

query: SearchIndexQuery;

This is the result of a query on several "all" nodes. Because we are potentially querying a lot of data, rather than using full fragments, queries should contain just enough data for search comparison and to generate the result link. You may want to query things such as thumbnail images or descriptions, but the more data you query, the more data and processor intensive using search will be. Example query that matches the SearchIndexQuery type definition:

{
  blog: allContentfulBlog {
    edges {
      node {
        title
        slug
      }
    }
  }
  files: allContentfulAsset {
    edges {
      node {
        title
        url
      }
    }
  }
  news: allContentfulNews {
    edges {
      node {
        title
        slug
      }
    }
  }
}

Using the graphql aliases is helpful, as the resultant IndexedSearch will have a type property that corresponds to the topmost name of every graph

You would then instantiate the SearchIndex class with your query data:

new SearchIndex(query);

The resultConversion method is important, it is what you use to process raw query data and return something that matches the SearchLink interface:

export interface SearchLink {
  to: string;
  text: string;
  target?: "blank" | "self";
}

The idea is to map over each item within the IndexedSearch array. There should be enough information to tell which __typename each result initiated from so you can properly construct the to and text strings. T

The and components render each individual result (given SearchLink data), and the button / icon that will close the SearchDrawer.

useAlgorithim

If you want to modify the default search behavior beyond just checking for entry titles, there is the useAlgorithim method. With this hook, you recieve the input event, a setter to modify value the input is hooked up to, the search index, and a setter to modify the search results.

Example: Modifying the search algorithm to compare titles and a stringified array of keywords

<SearchDrawer
  useAlgorithm={(e, setKey, index, setResults) => {
    const search = e.currentTarget.value;
    const results = index.index.filter((item) => {
      // if keywords do not exist, return an empty array
      const keywords = item.node?.keywords ? item.node.keywords : [];
      // join the arry to the title string
      const searchString = item.node.title + " " + keywords.join(", ");
      // set up a regular expression to match the search query, case insensitive
      const regex = new RegExp(search, "i");
      // if there is a match, return the data from the search index
      return Array.isArray(searchString.match(regex));
    });
    setResults(results);
    setKey(search);
  }}
/>