Skip to content
ResearchSearch resultsMenu

Search results

Web results from a search tool in three views: a full list, a compact list and an image grid, with the query and result count on top.

Site, title, snippet with the query in a heavier weight, and date. Switch to the compact list.

Installation

$ pnpm dlx shadcn@latest add https://chatcn.dev/r/search-results.json

Usage

tsx
import { SearchResults } from "@/components/ui/chatcn/search-results"

<SearchResults
  query="optimal line length for reading on screens"
  results={results}
  total={2140}
  durationMs={420}
  variant={view}
  onVariantChange={setView}
/>

Each result takes a url, a title and optionally a snippet, site, date and image. Query words are set in a heavier weight inside snippets, matched as whole words so line marks *lines* but never the line in *deadline*.

Examples

Image grid

Each frame holds a 4:3 placeholder from the first paint and fades the picture in over 200 ms once it decodes. A broken image keeps its frame.

Image results hold their frame while they load.

Loading, empty and error

Skeletons are static blocks in the exact shape of the current view: no shimmer, because shimmer belongs to status labels. Use loading only while you know which view will render.

Skeletons in the shape of the view, an empty search and a failed one.

Props

PropTypeNotes
resultsSearchResult[]url, title, snippet?, site?, domain?, favicon?, date?, image?
querystringShown in the header and bolded in snippets
totalnumberTotal matches from the API. Defaults to the number of results
durationMsnumberSearch time next to the count
variant"list" | "compact" | "images"Default list
onVariantChange(variant) => voidShows the view switcher
variantsSearchResultsVariant[]Views offered by the switcher
loadingbooleanSkeleton rows in the shape of the view
error, onRetryReactNode, () => voidError state with Retry search
onResultClick(result, index, event) => voidCall event.preventDefault() to open results yourself
highlightbooleanBold query words in snippets. Default true
emptyReactNodeMessage for zero results
localestringDates and counts. Default en-US so server and client agree

Keyboard

KeyAction
TabMove through result titles, then the view switcher
Arrow keysChange the view inside the switcher, like a radio group
EnterOpen the focused result

Accessibility

  • The list is a labelled section with real links. The whole row is the hit area, while the title stays the accessible name.
  • The view switcher is a radiogroup with roving focus. Each icon button has a label.
  • The count is a polite live region: it says Searching, then the number of results once.
  • Errors use a red icon next to the words Search failed, never color alone.

Motion

  • Hover changes background and underline color over 150 ms. Nothing moves or scales.
  • Images fade in over 200 ms once loaded. Switching views is instant, since it is a frequent action.