Svelte-spotlight

Headless spotlight component for Svelte

Build your site global search box in minutes

npm i svelte-spotlight
pnpm add svelte-spotlight
yarn add svelte-spotlight
  • Bring your own style, completely headless. Svelte-spotlight only handle the layout.
  • Search method agnostic, local or asynchronous.
  • Data agnostic, render flat lists or grouped results. Integrate perfectly with Algolia multi-index search.
  • Great DX with full TS support
  • 9 slots to customize every part you need
  • Animate it as you wish
  • Keyboard shortcut and navigation
  • Accessible (but can be better)
  • No dependencies

Anatomy

Strucutre
Classes
Slots
Dynamic slots

Overall structure of svelte-spotlight

Overlay
Modal
Header
Header left
Input
Header right
Content
Content top
Results
Group
Group header
Result
Side panel
Content bottom
Footer

Props

Name descriptiontypedefaultValue
isOpen Whether to spotlight is openbooleanfalse
results A flat array of result or a one level deep array of grouped resultsR[][]
groupResultsKey If the results are grouped, you want to define the key of the nested resultsGK extends ConditionalKeys<R, any[]>
groupIdKey The key to find the ID of a group in order to keyed the each loopConditionalKeys<R, string | number>
resultIdKey The key to find the ID of a result in order to keyed the each loopGroupedResult extends string ? ConditionalKeys<R, string | number> : ConditionalKeys<GroupedResult, string | number>
cleanQueryOnClose Whether to clean query on close. This option also clean the preselected result and the selected resultbooleanfalse
distanceFromTop The top distance where the spotlight will be fixednumber100
searchPlaceholder The input placeholderstringSearch
query The current value of the search inputstring""
preSelectedResult The current result that is preselected by the keyboard navigationResult
selectedResult The current selected result either after hitting "Enter" or by clicking on itResult
isFocused Whether the search input is focusedbooleanfalse
listElement HTML element type for the the result liststringul
resultElement HTML element type for the result elementstringli

Animations

Name descriptiondefaultValue
overlayTransition The svelte-transition to animate the overlayfade
overlayTransitionConfig Config for the overlay aimation{ duration : 200 }
modalTransitionIn The svelte-transition to animate the modal on enterscale
modalTransitionInConfig Config for the modal enter aimation{ start: 0.95 }
modalTransitionOut The svelte-transition to animate the modal on exitscale
modalTransitionOutConfig Config for the modal exit aimation{ start: 0.95 }

Classes

Name descriptiontype
overlayClass The overlay classstring
modalClass The modal classstring
headerClass The modal header classstring
inputClass The input classstring
contentClass The modal content container classstring
resultsClass The modal results container classstring
footerClass The modal footer classstring

Dynamic slots

Name descriptiontype
headerCenterComponent Optional header center component to replace the default input. We don't use a slot here because it's impossible to define a conditional slot, and you don't want to display this component all the time. It's useful if you want to simulate some sort of navigation inside the spotlight componentSvelte component
contentComponent Optional content component to replace the default result list. We don't use a slot here because it's impossible to define a conditional slot, and you don't want to display this component all the time. It's useful if you want to simulate some sort of navigation inside the spotlight componentSvelte component

Slots

Default available slot props

Name descriptiontypedefaultValue
selectedResult The current selected result either after hitting "Enter" or by clicking on itResultundefined
preSelectedResult The current result that is preselected by the keyboard navigationResultundefined
noResults If search results list is emptybooleanfalse
query The current value of the search inputstring''

headerLeft

Use it to render a search icon or spinner to the left of the input.

Name type
group R
groupIndex number

headerRight

Use it to render a close icon or a clear query button.

contentTop

Render inside the result container but before the results list, you can use it to display a filtering menu.

contentBottom

Render inside the result container but after the results list, you can use it to display a load more button.

emptySearch

Render when query is empty, useful to display some hints. If not provided the results list is shown (or if no result, the noResults slot)

noResults

Render when results list is empty.

groupHeader

If results are grouped you can use this slot to provide segmentation infos.

result

Slot to display the result.

Name type
result Result
selected boolean
index number

sidePanel

Side panel to render to the right of the results container. You can use it to render information about the current preselected result like Raycast does.

Name type
maxHeight number

footer

Render to the bottom of the modal. You can display some hints here too or the famous 'Search by Algolia' button

trigger

Slot to render a button that trigger the modal to open.

Name typedescrition
toggle () => voidundefined

Basic example

Tailwind
CSS
<script lang="ts"> import SvelteSpotlight from 'svelte-spotlight/SvelteSpotlight.svelte'; import { matchSorter } from 'match-sorter'; let query = ""; let items = [ { title: 'Hit 1', description: 'Lorem ipsum dolor sit, amet consectetur adipisicing elit. ', }, { title: 'Hit 2', description: 'Lorem ipsum dolor sit, amet consectetur adipisicing elit. ', }, { title: 'Hit 3', description: 'Lorem ipsum dolor sit, amet consectetur adipisicing elit. ', }, { title: 'Hit 4', description: 'Lorem ipsum dolor sit, amet consectetur adipisicing elit. ', } ]; $: results = matchSorter(items, query, { keys: ['title'] }); </script> <SvelteSpotlight {results} bind:query modalClass={'w-[600px] max-w-[95%] bg-white shadow-lg rounded-sm'} headerClass={'py-3 px-10 border-b-2 border-slate-100 border-b-solid'} inputClass="focus:outline-none bg-transparent" resultIdKey="title" on:select={(event) => { // DO stuff }} > <div slot="result" let:selected let:result class={"hover:bg-slate-100 cursor-pointer text-sm px-10 py-3 w-full" + selected ? "bg-slate-100" : ''}> {result.title} <p class="text-slate-500 text-sm">{result.description}</p> </div> <div slot="noResults" class="px-10 py-3 text-slate-500 text-sm"> No results... </div> </SvelteSpotlight>