Skip to content

Templates

Perfect Autocomplete uses htm for flexible, type-safe templating. You can customize how items are rendered using either JavaScript or declarative approaches.

Default Template

By default, items are rendered with support for label, image, and description:

javascript
// Default rendering structure
{
  label: "John Doe",           // Required
  image: "/avatar.jpg",        // Optional
  description: "Engineer"      // Optional
}

JavaScript API

renderItem Function

Set a custom render function for full control:

javascript
const autocomplete = document.querySelector('perfect-autocomplete')

autocomplete.renderItem = (item, html, { highlighted, index }) => {
  return html`
    <div class="my-item ${highlighted ? 'active' : ''}">
      <img src=${item.avatar} alt="" />
      <div>
        <strong>${item.name}</strong>
        <small>${item.email}</small>
      </div>
    </div>
  `
}

Template Parameters

ParameterTypeDescription
itemAutocompleteItemThe item data to render
htmlHtmlTemplateFunctionThe htm template function
context.highlightedbooleanWhether the item is highlighted
context.indexnumberThe item's index in the list

htm Syntax

htm provides JSX-like syntax in template literals:

Basic Elements

javascript
html`<div class="container">Content</div>`

Dynamic Values

javascript
html`<span>${item.name}</span>`

Conditional Rendering

javascript
html`
  ${item.image && html`<img src=${item.image} />`}
`

Lists

javascript
html`
  <ul>
    ${items.map(item => html`<li>${item.name}</li>`)}
  </ul>
`

Event Handlers

javascript
html`<button onclick=${() => console.log('clicked')}>Click</button>`

Examples

User Card

javascript
autocomplete.renderItem = (item, html, { highlighted }) => html`
  <div class="user-card ${highlighted ? 'selected' : ''}">
    <img
      class="avatar"
      src=${item.avatar || '/default-avatar.png'}
      alt=${item.name}
    />
    <div class="user-info">
      <span class="name">${item.name}</span>
      <span class="email">${item.email}</span>
    </div>
    ${item.verified && html`
      <span class="badge">✓</span>
    `}
  </div>
`

Product Item

javascript
autocomplete.renderItem = (item, html, { highlighted }) => html`
  <div class="product ${highlighted ? 'highlighted' : ''}">
    <img src=${item.thumbnail} alt=${item.name} />
    <div class="details">
      <span class="name">${item.name}</span>
      <span class="price">$${item.price.toFixed(2)}</span>
    </div>
    ${item.inStock
      ? html`<span class="stock in">In Stock</span>`
      : html`<span class="stock out">Out of Stock</span>`
    }
  </div>
`

Search Result with Highlight

javascript
function highlightMatch(text, query) {
  const regex = new RegExp(`(${query})`, 'gi')
  return text.replace(regex, '<mark>$1</mark>')
}

autocomplete.renderItem = (item, html, context) => html`
  <div class="search-result">
    <span
      class="title"
      dangerouslySetInnerHTML=${{ __html: highlightMatch(item.title, query) }}
    />
    <span class="category">${item.category}</span>
  </div>
`

Styling Custom Templates

Add CSS for your custom templates:

css
/* User card styles */
.user-card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 12px;
}

.user-card.selected {
  background: var(--pac-highlight-bg);
}

.user-card .avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
}

.user-card .name {
  font-weight: 600;
}

.user-card .email {
  font-size: 12px;
  color: #666;
}

TypeScript Support

Templates are fully typed:

typescript
import type { RenderItemFunction, AutocompleteItem } from '@mikolaj.jeziorny/perfect-autocomplete'

interface UserItem extends AutocompleteItem {
  avatar: string
  email: string
  verified: boolean
}

const renderUser: RenderItemFunction = (item, html, context) => {
  const user = item as UserItem
  return html`
    <div class="user">
      <img src=${user.avatar} />
      <span>${user.label}</span>
    </div>
  `
}

autocomplete.renderItem = renderUser