Custom Search Result Cards | Yext Hitchhikers Platform
What You’ll Learn
In this section, you will:
- Generate the Typescript types that represent entity search results
- Create custom search result cards for the “jobs” vertical
Overview
In this module, you’re going to be building a job searcher for Turtlehead Tacos. Up to this point, you have used the StandardCard
component for displaying search results on the page. This component is useful for getting up and running, but it doesn’t display all the data you need.
In this unit, you’re going to define a custom JobCard
component to display more information for each job search result.
1. Switch Verticals
To start, you need to switch to search on the “jobs” vertical in src/templates/search.tsx
. Change the vertical key in your headlessConfig
object from “faqs” to “jobs”.
// src/templates/search.tsx
// other imports and code...
const headlessConfig: HeadlessConfig = {
apiKey: "Your API Key Here",
experienceKey: "turtlehead",
locale: "en",
verticalKey: "jobs",
environment: Environment.SANDBOX,
};
2. Add a Heading and Search Bar Placeholder Text
Run npm run dev
in the terminal if your application isn’t already running. Now searching for “waiter” or “chef” will return jobs with their name
and description
fields displayed on each card.
Add some heading text and pass the placeholder
prop to SearchBar
:
const Search: Template<TemplateRenderProps> = () => {
return (
<SearchHeadlessProvider searcher={searcher}>
<div className="px-4 py-8">
<div className="mx-auto flex max-w-5xl flex-col">
{/* new code starts here... */}
<h1 className="pb-4 text-center text-3xl font-bold text-red-700">
Turtlehead Tacos Careers
</h1>
<SearchBar placeholder="Search job title, department, or employment type" />
{/* ...and ends here */}
<SpellCheck />
<ResultsCount />
<VerticalResults
CardComponent={StandardCard}
displayAllOnNoResults={false}
/>
</div>
<Pagination />
</div>
</SearchHeadlessProvider>
);
};
3. Search Result Types
Search results represent Content entities and therefore can take on any shape or size. Rather than define your own Typescript interfaces for each vertical, you can use the Yext CLI to generate types for you.
In your terminal, run:
yext types generate search src/types --experienceKey turtlehead
This will generate a new file for each vertical in your search experience inside the src/types
folder.
4. Custom Job Card
Create a new folder src/components
with a new file called JobCard.tsx
. Add the following code.
// src/components/JobCard.tsx
import { CardComponent, CardProps } from "@yext/search-ui-react";
import Job from "../types/jobs";
const JobCard: CardComponent<Job> = ({
result,
}: CardProps<Job>): JSX.Element => {
const job = result.rawData;
// function that takes a date in the format YYYY-MM-DD and returns date in the format Month Day, Year
const formatDate = (date: string): string => {
if (!date) return "";
const dateObj = new Date(date);
const month = dateObj.toLocaleString("default", { month: "long" });
const day = dateObj.getDate();
const year = dateObj.getFullYear();
return `${month} ${day}, ${year}`;
};
return (
<div className="mb-4 justify-between rounded-lg border bg-zinc-100 p-4 text-stone-900 shadow-sm">
<div className="flex flex-col">
<div className="text-lg font-semibold text-red-700">{job.name}</div>
<div>{job.c_jobDepartment}</div>
<div className="flex gap-1">
{job.employmentType && (
<div className="flex rounded bg-gray-600 px-1 text-sm text-gray-100">
{`${job.employmentType}`}
</div>
)}
{job.c_salary && (
<div className="flex rounded bg-gray-600 px-1 text-sm text-gray-100">
{`$${job.c_salary}/hour`}
</div>
)}
</div>
<div className="py-2 ">{job.description}</div>
{job.datePosted && (
<div className="text-sm">{`Date Posted: ${formatDate(
job.datePosted
)}`}</div>
)}
</div>
</div>
);
};
export default JobCard;
Let’s review what you just added:
JobCard
is aCardComponent
which is a generic type. This means when theCardProps
are providedJob
as a type parameter,result.rawData
will be typed as aJob
.- In addition to the Job
name
anddescription
, you’re also displaying thec_jobDepartment
,employmentType
,c_salary
, and formatteddatePosted
field. - You’ve also added some custom styling to your card. In future units, you’ll add some custom styles to other components to match.
5. Add a Custom Card to VerticalResults
Now that we have created the JobCard
, we need to add it to our search experience.
In search.tsx
, pass the JobCard
as the CardComponent
prop to VerticalResults
.
// src/templates/search.tsx
// other imports...
import JobCard from "../components/JobCard";
import Job from "../types/jobs";
// other code...
const Search: Template<TemplateRenderProps> = () => {
return (
<SearchHeadlessProvider searcher={searcher}>
<div className="px-4 py-8">
<div className="mx-auto flex max-w-5xl flex-col">
<h1 className="pb-4 text-center text-3xl font-bold text-red-700">
Turtlehead Tacos Careers
</h1>
<SearchBar placeholder="Search job title, department, or employment type" />
<SpellCheck />
<ResultsCount />
{/* new code starts here... */}
<VerticalResults<Job>
CardComponent={JobCard}
displayAllOnNoResults={false}
/>
{/* ...and ends here */}
</div>
<Pagination />
</div>
</SearchHeadlessProvider>
);
};
Refresh the page and run a new search to see your new Job search results!