Step 2: Connecting Search State with the URL

Changing the Default Search Functionality

When the user enters a query using the SearchBar, you can store the query in the URL so that it can be persisted when sharing a link to your application. The first step is to change what happens when a new search is triggered from the SearchBar. In StandardLayout, add an onSearch function that uses the React Router useSearchParams hook to add the query search parameter, delete it if the query is empty, and update the URL.

// src/pages/StandardLayout.tsx

import { SearchBar } from "@yext/search-ui-react";
import { useSearchParams } from "react-router-dom";
import { NavBar } from "../components/NavBar";

export interface StandardLayoutProps {
  page: JSX.Element;
}

export const StandardLayout = ({ page }: StandardLayoutProps): JSX.Element => {
  const [searchParams, setSearchParams] = useSearchParams();

  const onSearch = (searchEventData: {
    verticalKey?: string,
    query?: string,
  }) => {
    const { query } = searchEventData;
    if (query) {
      searchParams.set("query", query);
    } else {
      searchParams.delete("query");
    }
    setSearchParams(searchParams);
  };

  return (
    <>
      <SearchBar onSearch={onSearch} />
      <NavBar />
      {page}
    </>
  );
};

Now, when a user searches, the query param of the URL will update, but it won’t actually run a search. Instead, we want the app to rerun the search whenever the URL changes. For this, we’ll add a new usePageSetupEffect hook that will re-trigger the search whenever the URL changes.

// src/hooks/usePageSetupEffect.tsx

import { useSearchActions } from "@yext/search-headless-react";
import { useEffect } from "react";
import { useLocation, useSearchParams } from "react-router-dom";

export const usePageSetupEffect = (verticalKey?: string) => {
  const location = useLocation();
  const [searchParams] = useSearchParams();
  const searchActions = useSearchActions();

  useEffect(() => {
    searchActions.setQuery(searchParams.get("query") || "");

    if (verticalKey) {
      searchActions.setVertical(verticalKey);
      searchActions.executeVerticalQuery();
    } else {
      searchActions.setUniversal();
      searchActions.executeUniversalQuery();
    }
  }, [location, searchActions]);
};

Remove the search logic from the VerticalResultsPage and UniversalResultsPage and replace it with your custom hook.

// src/pages/VerticalResultsPage.tsx

import { StandardCard, VerticalResults } from "@yext/search-ui-react";
import { usePageSetupEffect } from "../hooks/usePageSetupEffect";

interface VerticalResultsPageProps {
  verticalKey: string;
}

export const VerticalResultsPage = ({
  verticalKey,
}: VerticalResultsPageProps): JSX.Element => {
  usePageSetupEffect(verticalKey);

  return <VerticalResults CardComponent={StandardCard} />;
};
// src/pages/UniversalResultsPage.tsx

import { UniversalResults } from "@yext/search-ui-react";
import { usePageSetupEffect } from "../hooks/usePageSetupEffect";

const universalResultsConfig = {
  events: {
    label: "Events",
  },
  locations: {
    label: "Locations",
  },
  artists: {
    label: "Artists",
  },
};

export const UniversalResultsPage = (): JSX.Element => {
  usePageSetupEffect();

  return <UniversalResults verticalConfigMap={universalResultsConfig} />;
};

Now when you use the SearchBar, the URL will update with the query and the usePageSetupEffect hook will handle all the search logic.

Preserving Query Parameters

You may notice that when you click on the links in your NavBar, the query parameter disappears from the URL. You want for the query string to persist as you click on each vertical so that you can apply that query on a new search vertical. To fix this problem, add a LinkWithQuery component that will preserve the existing search parameters in the URL when clicking on a link.

// src/components/LinkWithQuery.tsx

import { useEffect, useState } from "react";
import { Link, useLocation, useSearchParams } from "react-router-dom";

interface LinkWithQueryProps {
  children: React.ReactNode;
  to: string;
}

export const LinkWithQuery = ({
  children,
  to,
}: LinkWithQueryProps): JSX.Element => {
  const [search, setSearch] = useState("");
  const location = useLocation();
  const [searchParams, setSearchParams] = useSearchParams();

  useEffect(() => {
    setSearch(
      searchParams.has("query") ? `?query=${searchParams.get("query")}` : ""
    );
  }, [location]);

  return <Link to={{ pathname: to, search }}>{children}</Link>;
};

Replace the Link components in the renderLink function in NavBar with your LinkWithQuery component.

// src/components/NavBar.tsx

const renderLink = (label: string, path: string): JSX.Element => {
  return (
    <LinkWithQuery key={`${path}_link`} to={path}>
      <div
        className={classNames(
          "whitespace-nowrap py-3 mt-1 font-medium text-md border-b-2 hover:border-gray-300 border-transparent",
          {
            "text-blue-600  border-blue-600": isActiveLink(path),
          }
        )}
      >
        <div className="py-3 px-2">{label}</div>
      </div>
    </LinkWithQuery>
  );
};

Now you can navigate between Verticals while preserving the query in the URL and you can copy and paste the URL into a new tab and get the same results as the original tab.

query_in_url