Blogs

Using Search V1 in your templates

Last updated: · Published:

So recently we've added a new search engine; You can read here how to enable it on your site.

After that you can create a fairly easy search page.

In this blog I will show you how to use this using a simple form get request.

  • Using dedicated 'search' posttype with only index.
  • Using request object to read url query params.
  • Using search_results tag
  • Using a couple of includes
  • Using translate filter for UI translations.
    • Singular result
    • Plurar result
  • Using simple form tag to send simple get request at Plate
  • Using paginate tag

An bare example of html output.

Features

This implementation includes several advanced features:

  1. Post Type Filtering: Users can filter results by content type (articles, pages, job postings)
  2. Pagination: Results are paginated with configurable posts per page
  3. Rich Result Cards: Each result shows title, excerpt, and content type
  4. Translation Support: All UI elements use translation keys
  5. Clean URLs: Uses GET parameters for search queries and filters

Implementation

Let's look at how to implement a search page using the actual theme structure:

theme/
  ├── posts/
   └── search/
       └── index.plate
  └── includes/
      └── modules/
          └── search/
              ├── search_form.plate
              ├── search_index.plate
              └── search_card.plate

The Main Search Page

Create a search page template at theme/posts/search/index.plate:

{%- content_for search -%}
    <div class="search-index-page plate--container">
        <search>
            {% include 'includes/modules/search/search_form' %}
        </search>
    
        {% include 'includes/modules/search/search_index' %}
    </div>

    {% render_content %}
{%- endcontent_for -%}

Advanced Search Form with Filters

The search form (includes/modules/search/search_form.plate) includes post type filtering:

<form action="{{ site.search.index.url }}" method="get" class="search-form {{ classes }}">
  <div class="search-form-input-wrapper">
    <label for="search_form_input" class="search-form-input-label">
      {{ "search.search_what" | translate }}
    </label>
    <input 
      class="search-form-input" 
      id="search_form_input" 
      type="search" 
      name="q" 
      value="{{ request.query_object.q | escape }}" 
      placeholder="{{ "search.search_placeholder" | translate }}"
    >

    {% assign filters = 'article,page,job_posting' | split: ',' %}
    <fieldset>
      <legend>Filters</legend>
      {% for filter in filters %}
        <input 
          type="checkbox" 
          id="posttype_{{ filter }}" 
          name="searchPostTypes[]" 
          value="{{ filter }}" 
          {% if request.query_object.searchPostTypes contains filter %}checked{% endif %}
        >
        <label for="posttype_{{ filter }}">
          {{ filter | prepend: 'post_type.' | translate }}
        </label>
      {% endfor %}
    </fieldset>

    <button type="submit" class="search-form-button" title="{{ "search.search" | translate }}">
      <span>{{ "search.search" | translate }}</span>
    </button>
  </div>
</form>

Search Results with Pagination

The search index (includes/modules/search/search_index.plate) handles results display and pagination:

<div class="search-index-page-results">
  {%- assign search_string = request.query_object.q | escape -%}
  {%- assign searchPostTypes = false -%}
  {% if request.query_object.searchPostTypes != blank %}
    {%- assign searchPostTypes = request.query_object.searchPostTypes -%}
  {% endif %}

  {%- capture searchFilters -%}
    [
      {%- if searchPostTypes != false -%}
        {
          "field": "post.content_type_name",
          "operator": "in",
          "value": [{%- for filter in searchPostTypes -%}
          "{{filter}}"{% unless forloop.last %},{% endunless %}
          {%- endfor -%}]
        }
      {%- endif -%}
    ]
  {%- endcapture -%}

  {% search_results search_string, filters: searchFilters %}
    {%- if search_results != blank -%}
      {% if search_results.size == 1 %}
        <p class="filtered-results-item">
          <span class="count">{{ search_results.size }}</span> 
          {{ "search.results_found_singular" | translate }}
        </p>
      {% else %}
        <p class="filtered-results-item">
          <span class="count">{{ search_results.size }}</span> 
          {{ "search.results_found_plural" | translate }}
        </p>
      {% endif %}

      {%- assign postsPerPage = site.search.index.posts_per_page | default: site.posts_per_page | times: 1 -%}
      {% paginate object_array: search_results, per_page: postsPerPage, window_size: 5 %}
        {% for p in paginate.items %}
          {% include 'includes/modules/search/search_card', search_card: p %}
        {% endfor %}

        {% if search_results.size != 0 %}
          {%- include 'includes/modules/search/search_pagination_buttons' -%}
        {% endif %}
      {% endpaginate %}
    {% else %}
      {% if request.query_object.q != blank %}
        <div class="no-results">
          <h2>{{ "search.no_results" | translate }}</h2>
          <p>{{ "search.try_again" | translate }}</p>
        </div>
      {% endif %}
    {%- endif -%}
  {% endsearch_results %}
</div>

Search Result Card

Each search result is displayed using a card template (includes/modules/search/search_card.plate):

<a href="{{ search_card.url }}" class="search-card {% if item.image != blank %}has-image{% endif %}">
  <div class="search-card-content">
    <h2>{{ search_card.title | strip_html }}</h2>
    {% if search_card.index_text != blank %}
      <div class="index-text paragraph">
        {{ search_card.index_text }}
      </div>
    {% endif %}
    <p>{{ search_card.content_type.name | prepend: 'post_type.' | translate }}</p>
  </div>
</a>

Required Translations

Make sure to add these translations to your language files:

en.yml
search:
  search_what: "Search"
  search_placeholder: "Enter your search terms..."
  search: "Search"
  results_found_singular: "result found"
  results_found_plural: "results found"
  no_results: "No results found"
  try_again: "Please try different search terms"
post_type:
  article: "Article"
  page: "Page"
  job_posting: "Job Posting"

Bonus: SCSS Styling the Pagination

Add these styles for the pagination, which includes WCAG-compliant touch targets and proper visual styling:

.search-pagination {
  display: flex;
  color: black;

  .list-pagination {
    margin: 2rem auto;
    display: flex;
    flex-wrap: wrap;
    gap: 0;
    justify-content: center;
  }

  .pagination__item {
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;

    a,
    button {
      @include buttonReset;
      text-decoration: none;
      color: inherit;

      // WCAG-compliant touch target size
      width: 4.8rem;
      height: 4.8rem;

      display: flex;
      align-items: center;
      justify-content: center;
      padding: 0;
      z-index: 1;
      position: relative;

      span {
        font-weight: 600;
        font-size: 2rem;
        line-height: 1;
        font-family: sans-serif;
      }

      &::before {
        z-index: -1;
        content: '';
        display: flex;
        position: absolute;
        width: 90%;
        height: 90%;
        border-radius: 50%;
        background-color: lightgray;
        outline: none;
        transition: all .3s ease-in-out;
      }

      &[disabled] {
        opacity: 0.5;
        cursor: initial;
      }

      &:not([disabled]) {
        cursor: pointer;

        &:focus-visible,
        &:hover {
          color: white;
          &::before {
            background-color: black;
          }
        }
      }
    }

    &--active {
      a,
      button {
        font-size: 2.2rem;
        font-weight: 700;
        color: white;
        &::before {
          background-color: black;
        }
      }
    }
  }
}

With this setup, you have a fully functional, filterable, and paginated search page in Plate using Search V1, complete with translation support and accessible UI. Adjust the templates and styles as needed to fit your site's design and requirements.


Copyright © 2025