Outline: [Article Title]

Keyword: [Enter Targeted Keyword]

Keyword MSV: [Enter Targeted Keyword’s Monthly Search Volume]

Author: [Enter Author Name]

Due Date: [Enter Due Date]

Publish Date: [Enter Desired Publish Date]

User Persona: [Enter Targeted Reader and/or User Persona]


Search engines that are typo-tolerant, effective, and efficient are extremely difficult to develop. Even if the requested item is in the database, a typographical error could cause the search to fail. Typesense might save a lot of time and effort by eliminating the need to develop a search engine from the ground up. Users will also be able to successfully use the app’s search tool, resulting in a great user experience. Typesense is a free, open-source typo-tolerant search engine for programmers that attempts to cut down on the time it takes to conduct effective and efficient searches. Follow this link to find out more about typesense. What is Typesense, and why is it such a great tool?.This article will explain you exactly on how to install Typesense, how to build up the Typesense application from absolute scratch, how to preconfigure the Typesense client, and more. In addition, this article will show you how to create a Typesense Collection. Finally, we’ll launch and initialize our app, add an item to our collection, and search our indexed data/collection.

We’ve made a similar application in Nextjs and Typescript called (Ecommerce Storefront Nextjs typesense project), so in this article we’ll make a simple application that uses the vannila instantsearchjs approach for searching through a collection of ecommerce store products, using typesense as the search engine once again. Everything will be written in javascript, and we’ll style our application from the ground up with simple CSS.

Configuring Typesense: a step-by-step guide (Docker approach)

Typesense may be used by installing its prebuilt docker image or by using the Typesense cloud hosting solution, which is the most straightforward method to get started. To get started, go to the Typesense cloud website and sign up with your Github account, or use the docker way directly. We’ll use the Docker approach for the purposes of this tutorial.To do so, simply go to the Typesense Dockerhub and download the prebuilt image, then follow the instructions stated below.

docker pull typesense/typesense

and

mkdir /tmp/typesense-data
docker run -p 8108:8108 -v/tmp/data:/data typesense/typesense:0.22.2 --data-dir /data --api-key=ecommerceapp --enable-cors

Docker running

Docker

Installing Typesense

npm install typesense

Typesense

Now that Typesense is installed and running, we can construct a Typesense collection, index some documents in it, and try searching for them. Finally, let’s create a search interface for our application.

Crafting Search UIs from scratch.

By making use of the open source InstantSearch.js library library or its React, Vue, and Angularjs, as well as the Typesense-InstantSearch-Adapter, you can create a plug-and-play fully-featured search interface with just a few lines of code.

Installing instantsearch-app: a step-by-step guide

$ npx create-instantsearch-app ecommerce-product-search

? The name of the application or widget ecommerce-product-search
? InstantSearch template InstantSearch.js

Creating a new InstantSearch app in ecommerce-product-search.

? InstantSearch.js version 4.38.1
? Application ID latency
? Search API key spellcheck
? Index name words
? Attributes to display
  Used to generate the default result template
? Attributes to display
  Used to filter the search interface Dynamic widgets

📦 Installing dependencies.........
....................................
....................................
....................................
....................................
....................................
....................................
....................................
....................................
....................................
yarn install v1.22.0
info No lockfile found.
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
success Saved lockfile.

Done in 173.57s.

🎉  Created ecommerce-product-search at ecommerce-product-search.

Begin by typing:

  cd ecommerce-product-search
  yarn start

⚡️  Start building something awesome !

First step: Give a name to your application.

Application name

Second step: Choose the Instantsearch application version you want to install.

Instantsearch template

Third step: Select the default latency as an application id.

Instantsearch version

Fourth step: AddSearch API Key to the project.

search api key

Fifth step: Specify the Index name for this application.

Index name

Sixth step: Select None as an attributes and Hit Enter.

Atrrib to display

Final step: Select Dynamic Widgets as an attributes to display and Hit Enter.

Atrrib to display

Instantsearch app

After you’ve completed installing your UI, your folder structure should look like this.

Folder structure

Here are some setup suggestions for the npx create-instantsearch-app command:

  • You can use any of the web libraries typesense supports, including InstantSearch.js, React, Vue, and Angular.
  • You can use the default version of InstantSearch.js.
  • Any string can be used as the application ID; we’ll be changing this later in the article.
  • Search API key: any string - we’ll replace this with your Typesense Search-only API Key later in the guide.
  • The name of your collection in Typesense is the index name.
  • Displaying attributes: leave it as is.

To use InstantSearch with a Typesense backend, we’ll need to install the Typesense InstantSearch adapter. To do so, simply open a terminal window and type the following command into it.

npm install --save typesense-instantsearch-adapter

Typesense Instantsearch adapter

After you’ve installed all of the necessary dependencies, your package.json file should look like this.

// package.json
{
  "name": "ecommerce-product-search-typesense",
  "version": "1.0.0",
  "private": true,
  "main": "src/app.js",
  "scripts": {
    "start": "parcel index.html --port 3000",
    "build": "parcel build index.html",
    "lint": "eslint .",
    "lint:fix": "npm run lint -- --fix"
  },
  "devDependencies": {
    "babel-eslint": "10.0.3",
    "eslint": "5.7.0",
    "eslint-config-algolia": "13.2.3",
    "eslint-config-prettier": "3.6.0",
    "eslint-plugin-import": "2.19.1",
    "eslint-plugin-prettier": "3.1.2",
    "parcel-bundler": "1.12.5",
    "prettier": "1.19.1"
  },
  "dependencies": {
    "algoliasearch": "4",
    "instantsearch.js": "4.38.1",
    "typesense": "^1.1.3",
    "typesense-instantsearch-adapter": "^2.3.0"
  }
}

As you can see, it’s a pretty basic app with only two crucial files: app.js and index.html, which contains the structure of our entire program. In the index.html file, you can see an id. We can use those ids to attach UI components like as a searchbox, results, facets and pagination if you want to include them inside your application.

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, shrink-to-fit=no"
    />
    <meta name="theme-color" content="#000000" />

    <link rel="manifest" href="./manifest.webmanifest" />
    <link rel="shortcut icon" href="./favicon.png" />

    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/instantsearch.css@7/themes/algolia-min.css"
    />
    <link rel="stylesheet" href="./src/index.css" />
    <link rel="stylesheet" href="./src/app.css" />

    <title>ecommerce-product-search-typesense</title>
  </head>

  <body>
    <header class="header">
      <h1 class="header-title">
        <a href="/">ecommerce-product-search-typesense</a>
      </h1>
      <p class="header-subtitle">
        using
        <a href="https://github.com/algolia/instantsearch.js">
          InstantSearch.js
        </a>
      </p>
    </header>

    <div class="container">
      <div class="search-panel">
        <div class="search-panel__filters">
          <div id="dynamic-widgets"></div>
        </div>

        <div class="search-panel__results">
          <div id="searchbox"></div>
          <div id="hits"></div>
        </div>
      </div>

      <div id="pagination"></div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/algoliasearch@4.10.5/dist/algoliasearch-lite.umd.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/instantsearch.js@4.38.1"></script>
    <script src="./src/app.js"></script>
  </body>
</html>

As you can see below, the app.js file is configured for algolia, and we’ll need to change that for typesense.

const { algoliasearch, instantsearch } = window;

const searchClient = algoliasearch("latency", "ecommerceapp");

const search = instantsearch({
  indexName: "products",
  searchClient,
});

search.addWidgets([
  instantsearch.widgets.searchBox({
    container: "#searchbox",
  }),
  instantsearch.widgets.hits({
    container: "#hits",
  }),
  instantsearch.widgets.configure({
    facets: ["*"],
    maxValuesPerFacet: 20,
  }),
  instantsearch.widgets.dynamicWidgets({
    container: "#dynamic-widgets",
    fallbackWidget({ container, attribute }) {
      return instantsearch.widgets.refinementList({
        container,
        attribute,
      });
    },
    widgets: [],
  }),
  instantsearch.widgets.pagination({
    container: "#pagination",
  }),
]);

search.start();

We can now finally adjust the project to use Typesense.We’ve got our typesense instance up and running in the background.To get InstantSearch.js to use the Typesense adapter, open src/app.js and change the way InstantSearch is initialized, from this:

const { algoliasearch, instantsearch } = window;

const searchClient = algoliasearch("latency", "ecommerceapp");

const search = instantsearch({
  indexName: "instant_search",
  searchClient,
});

to this

import TypesenseInstantSearchAdapter from "typesense-instantsearch-adapter";

const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
  server: {
    apiKey: "ecommerceapp",
    nodes: [
      {
        host: "localhost",
        port: "8108",
        protocol: "http",
      },
    ],
  },
  additionalSearchParameters: {
    queryBy: "name,categories,description", //quering by
  },
});
const searchClient = typesenseInstantsearchAdapter.searchClient;

const search = instantsearch({
  searchClient,
  indexName: "products",
});

Now we need to import our products data, so create a dataset folder and inside it, create your own ecommerce products json file, filling it with all of the necessary product information, or download the ecommerce dataset from here. Finally, your folder structure should look something like this.

Folder structure

Dataset

Let’s get started writing the data-importing scripts. We’ll start by creating a file called loadData.js in which we’ll initialize the typesense client.

//loadData.js
const Typesense = require("typesense");

const runClient = () => {
  let client = new Typesense.Client({
    nodes: [
      {
        host: "localhost", // For Typesense Cloud use xxx.a1.typesense.net
        port: "8108", // For Typesense Cloud use 443
        protocol: "http", // For Typesense Cloud use https
      },
    ],
    apiKey: "ecommerceapp",
    connectionTimeoutSeconds: 2,
  });
};

runClient();

Creating a collection named products

In Typesense, a Collection is a group of related Documents that works similarly to a table in a relational database. When we establish a collection, we give it a name and explain the fields that will be indexed when a document is added to it. Now, go to the loadData.js and make the following changes to the code to manage the bulk import capabilities/functionality.

Let’s get started with the data-importing scripts. We’ll begin by creating a file named loadData.js in which we’ll export and initialize the typesense client, but first we’ll need to import a few modules such as fs/promises and Typesense.

// loadData.js
const Typesense = require("typesense");
const fs = require("fs/promises");

Creating a collection : a step-by-step guide

In Typesense, a Collection is a group of related Documents that works similarly to a table in a relational database. When we establish a collection, we give it a name and explain the fields that will be indexed when a document is added to it .Now head over to loadData.js file and add the following changes to the code.

First step: We need to create a typesense client

// loadData.js
const runClient = async () => {
  let client = new Typesense.Client({
    nodes: [
      {
        host: "localhost",
        port: "8108",
        protocol: "http",
      },
    ],
    apiKey: "ecommerceapp",
    connectionTimeoutSeconds: 2,
  });

Second step: If an error occurs while loading the data(data duplication error), simply add the following snippet of code to the loadData.js file before creating schema becasue it will simply remove the existing data and populates it with the new ones.

await typesense.collections("products").delete();

Third step: Create a schema

const productsSchema = {
  name: "products",
  num_documents: 0,
  fields: [
    {
      name: "name",
      type: "string",
      facet: false,
    },
    {
      name: "description",
      type: "string",
      facet: false,
    },
    {
      name: "brand",
      type: "string",
      facet: true,
    },
    {
      name: "categories",
      type: "string[]",
      facet: true,
    },
    {
      name: "categories.lvl0",
      type: "string[]",
      facet: true,
    },
    {
      name: "categories.lvl1",
      type: "string[]",
      facet: true,
      optional: true,
    },
    {
      name: "categories.lvl2",
      type: "string[]",
      facet: true,
      optional: true,
    },
    {
      name: "categories.lvl3",
      type: "string[]",
      facet: true,
      optional: true,
    },
    {
      name: "price",
      type: "float",
      facet: true,
    },
    {
      name: "image",
      type: "string",
      facet: false,
    },
    {
      name: "popularity",
      type: "int32",
      facet: false,
    },
    {
      name: "free_shipping",
      type: "bool",
      facet: true,
    },
    {
      name: "rating",
      type: "int32",
      facet: true,
    },
  ],
  default_sorting_field: "popularity",
};

Fourth step: Now that we have the schema, we must add the documents. We have our documents saved as ecommerce.json in our dataset folder. All you have to do now is import and load the document into our loadData.js file.Also, we can specify the batch file size, and for now, let’s insert some elements to it.

const products = require("./dataset/ecommerce.json");

let reindexNeeded = false;
try {
  const collection = await client.collections("products").retrieve();
  console.log("Found existing schema");
  if (
    collection.num_documents !== products.length ||
    process.env.FORCE_REINDEX === "true"
  ) {
    console.log("Deleting existing schema");
    reindexNeeded = true;
    await client.collections("products").delete();
  }
} catch (e) {
  reindexNeeded = true;
}

if (!reindexNeeded) {
  return true;
}

console.log("Creating schema: ");
console.log(JSON.stringify(productsSchema, null, 2));
await client.collections().create(productsSchema);

console.log("Adding records: ");

Fifth step: Now, create a Bulk import functionality.

// Bulk Import
products.forEach((product) => {
  product.free_shipping = product.name.length % 2 === 1;
  product.rating = (product.description.length % 5) + 1;
  product.categories.forEach((category, index) => {
    product[`categories.lvl${index}`] = [
      product.categories.slice(0, index + 1).join(" > "),
    ];
  });
});

try {
  const returnData = await client
    .collections("products")
    .documents()
    .import(products);
  console.log(returnData);
  console.log("Done indexing.");

  const failedItems = returnData.filter((item) => item.success === false);
  if (failedItems.length > 0) {
    throw new Error(
      `Error indexing items ${JSON.stringify(failedItems, null, 2)}`
    );
  }

  return returnData;
} catch (error) {
  console.log(error);
}

Your final code inside loadData.js file should look like this.

// loadData.js
const Typesense = require("typesense");
const fs = require("fs/promises");

const runClient = async () => {
  let client = new Typesense.Client({
    nodes: [
      {
        host: "localhost",
        port: "8108",
        protocol: "http",
      },
    ],
    apiKey: "ecommerceapp",
    connectionTimeoutSeconds: 2,
  });

  const productsSchema = {
    name: "products",
    num_documents: 0,
    fields: [
      {
        name: "name",
        type: "string",
        facet: false,
      },
      {
        name: "description",
        type: "string",
        facet: false,
      },
      {
        name: "brand",
        type: "string",
        facet: true,
      },
      {
        name: "categories",
        type: "string[]",
        facet: true,
      },
      {
        name: "categories.lvl0",
        type: "string[]",
        facet: true,
      },
      {
        name: "categories.lvl1",
        type: "string[]",
        facet: true,
        optional: true,
      },
      {
        name: "categories.lvl2",
        type: "string[]",
        facet: true,
        optional: true,
      },
      {
        name: "categories.lvl3",
        type: "string[]",
        facet: true,
        optional: true,
      },
      {
        name: "price",
        type: "float",
        facet: true,
      },
      {
        name: "image",
        type: "string",
        facet: false,
      },
      {
        name: "popularity",
        type: "int32",
        facet: false,
      },
      {
        name: "free_shipping",
        type: "bool",
        facet: true,
      },
      {
        name: "rating",
        type: "int32",
        facet: true,
      },
    ],
    default_sorting_field: "popularity",
  };

  console.log("Populating index in Typesense");

  const products = require("./dataset/ecommerce.json");

  let reindexNeeded = false;
  try {
    const collection = await client.collections("products").retrieve();
    console.log("Found existing schema");
    if (
      collection.num_documents !== products.length ||
      process.env.FORCE_REINDEX === "true"
    ) {
      console.log("Deleting existing schema");
      reindexNeeded = true;
      await client.collections("products").delete();
    }
  } catch (e) {
    reindexNeeded = true;
  }

  if (!reindexNeeded) {
    return true;
  }

  console.log("Creating schema: ");
  console.log(JSON.stringify(productsSchema, null, 2));
  await client.collections().create(productsSchema);

  console.log("Adding records: ");

  // Bulk Import
  products.forEach((product) => {
    product.free_shipping = product.name.length % 2 === 1;
    product.rating = (product.description.length % 5) + 1;
    product.categories.forEach((category, index) => {
      product[`categories.lvl${index}`] = [
        product.categories.slice(0, index + 1).join(" > "),
      ];
    });
  });

  try {
    const returnData = await client
      .collections("products")
      .documents()
      .import(products);
    console.log(returnData);
    console.log("Done indexing.");

    const failedItems = returnData.filter((item) => item.success === false);
    if (failedItems.length > 0) {
      throw new Error(
        `Error indexing items ${JSON.stringify(failedItems, null, 2)}`
      );
    }

    return returnData;
  } catch (error) {
    console.log(error);
  }
};

runClient();

Finally, type node loadData.js into the terminal of that same project directory, and you should see something similar to the image below.

indexing

indexing

Now,you can start your application by typing npm start in the terminal of that project directory.

npm start

As you can see, application works but nothing is displayed.

Demo

So, to fix it, go to our app.js file and remove a few of the widgets that we don’t need, so the app.js file should look something like this.

import TypesenseInstantSearchAdapter from "typesense-instantsearch-adapter";

const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
  server: {
    apiKey: "ecommerceapp",
    nodes: [
      {
        host: "localhost",
        port: "8108",
        protocol: "http",
      },
    ],
  },
  additionalSearchParameters: {
    queryBy: "name,categories,description", //quering by
  },
});
const searchClient = typesenseInstantsearchAdapter.searchClient;

const search = instantsearch({
  searchClient,
  indexName: "products",
});

search.addWidgets([
  instantsearch.widgets.searchBox({
    container: "#searchbox",
  }),
  instantsearch.widgets.hits({
    container: "#hits",
  }),
  instantsearch.widgets.pagination({
    container: "#pagination",
  }),
]);

search.start();

Simply re-run the application after you’ve fixed it, and your application should now look like this.

Demo

You can now build a search interface using any of the InstantSearch widgets, and we’ll be adding a template to present the data in a pleasing manner.Simplycopy the template code below and add it inside the #hits widget present inside app.js file.

templates: {
      item: `
        <div>
          <img src="" align="left" alt="" />
          <div class="hit-name">
            { "attribute": "name" }
          </div>
          <div class="hit-description">
            { "attribute": "description" }
          </div>
          <div class="hit-price"></div>
          <div class="hit-rating">Brand: </div>
        </div>
      `,
    },

app.js file should resemble something like this.

// app.js
import TypesenseInstantSearchAdapter from "typesense-instantsearch-adapter";

const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
  server: {
    apiKey: "ecommerceapp",
    nodes: [
      {
        host: "localhost",
        port: "8108",
        protocol: "http",
      },
    ],
  },
  additionalSearchParameters: {
    queryBy: "name,categories,description", //quering by
  },
});
const searchClient = typesenseInstantsearchAdapter.searchClient;

const search = instantsearch({
  searchClient,
  indexName: "products",
});

search.addWidgets([
  instantsearch.widgets.searchBox({
    container: "#searchbox",
  }),
  instantsearch.widgets.hits({
    container: "#hits",
    templates: {
      item: `
        <div>
          <img src="" align="left" alt="" />
          <div class="hit-name">
            { "attribute": "name" }
          </div>
          <div class="hit-description">
            { "attribute": "description" }
          </div>
          <div class="hit-price"></div>
          <div class="hit-rating">Brand: </div>
        </div>
      `,
    },
  }),
  instantsearch.widgets.pagination({
    container: "#pagination",
  }),
]);

search.start();

Again, re-run the application after you’ve fixed it, and your application should now look like this.

Demo

To configure the 4 hits per page or you can configure to your own requirements, simply add the following code above the #hits widgets inside the app.js file.

instantsearch.widgets.configure({
    hitsPerPage: 4,
  }),

So Now let’s try to add the facets ,to do that so we’ll add a instant search widgets and in instanctsearch library this is called refinementlist and then we have to specify the container so let’s call it a #filterList and then the attribute we want to take so in our case let’s call it authors we reference that if we do this and then we add the id name as a refinements on our index.html and run our application it should probaly work. As a result, this is how our final code should look.

// app.js
import TypesenseInstantSearchAdapter from "typesense-instantsearch-adapter";

const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
  server: {
    apiKey: "ecommerceapp",
    nodes: [
      {
        host: "localhost",
        port: "8108",
        protocol: "http",
      },
    ],
  },
  additionalSearchParameters: {
    queryBy: "name,categories,description", //quering by
  },
});
const searchClient = typesenseInstantsearchAdapter.searchClient;

const search = instantsearch({
  searchClient,
  indexName: "products",
});

search.addWidgets([
  instantsearch.widgets.searchBox({
    container: "#searchbox",
  }),
  instantsearch.widgets.configure({
    hitsPerPage: 4,
  }),
  instantsearch.widgets.hits({
    container: "#hits",
    templates: {
      item: `
        <div>
          <img src="" align="left" alt="" />
          <div class="hit-name">
            { "attribute": "name" }
          </div>
          <div class="hit-description">
            { "attribute": "description" }
          </div>
          <div class="hit-price"></div>
          <div class="hit-rating">Brand: </div>
        </div>
      `,
    },
  }),
  instantsearch.widgets.pagination({
    container: "#pagination",
  }),
  instantsearch.widgets.refinementList({
    container: "#filterList",
    attribute: "categories",
  }),
]);

search.start();

and dont forget to change the id name on our index.html file

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, shrink-to-fit=no"
    />
    <meta name="theme-color" content="#000000" />

    <link rel="manifest" href="./manifest.webmanifest" />
    <link rel="shortcut icon" href="./favicon.png" />

    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/instantsearch.css@7/themes/algolia-min.css"
    />
    <link rel="stylesheet" href="./src/index.css" />
    <link rel="stylesheet" href="./src/app.css" />

    <title>ecommerce-product-search-typesense</title>
  </head>

  <body>
    <header class="header">
      <h1 class="header-title">
        <a href="/">ecommerce-product-search-typesense</a>
      </h1>
      <p class="header-subtitle">
        using
        <a href="https://github.com/algolia/instantsearch.js">
          InstantSearch.js
        </a>
      </p>
    </header>

    <div class="container">
      <div class="search-panel">
        <div class="search-panel__filters">
          <div id="filterList"></div>
        </div>

        <div class="search-panel__results">
          <div id="searchbox"></div>
          <div id="hits"></div>
        </div>
      </div>

      <div id="pagination"></div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/algoliasearch@4.10.5/dist/algoliasearch-lite.umd.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/instantsearch.js@4.38.1"></script>
    <script src="./src/app.js"></script>
  </body>
</html>

Here’s how your application should appear.

Demo

Finally, we can add sorting functionality to the application by following the same steps as before: add the widget/component called SortBy from react-instantsearch-dom, and specify the items with the label default with the value products, and then create another label called Price (asc) with the value products/sort/price:asc, and again another label called Price (desc) with the value products/sort/price:desc.

 instantsearch.widgets.sortBy({
    container: '#sort',
    items: [
     { label: "Default", value: "products" },
    { label: "Price (asc)", value: "products/sort/price:asc" },
    { label: "Price (desc)", value: "products/sort/price:desc" },
    ],
  }),

Finally, update and add sort-books id before the searchbox inside index.html file.

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, shrink-to-fit=no"
    />
    <meta name="theme-color" content="#000000" />

    <link rel="manifest" href="./manifest.webmanifest" />
    <link rel="shortcut icon" href="./favicon.png" />

    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/instantsearch.css@7/themes/algolia-min.css"
    />
    <link rel="stylesheet" href="./src/index.css" />
    <link rel="stylesheet" href="./src/app.css" />

    <title>ecommerce-product-search-typesense</title>
  </head>

  <body>
    <header class="header">
      <p class="header-subtitle"></p>
    </header>

    <div class="container">
      <div class="search-panel">
        <div class="search-panel__filters">
          <div id="filterList"></div>
        </div>

        <div class="search-panel__results">
          <div id="searchbox"></div>
          <div id="sort"></div>
          <div id="hits"></div>
        </div>
      </div>

      <div id="pagination"></div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/algoliasearch@4.10.5/dist/algoliasearch-lite.umd.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/instantsearch.js@4.38.1"></script>
    <script src="./src/app.js"></script>
  </body>
</html>

default

ascending

descending

Ultimately, as a final step, let’s update the styles and template for the information that we want to display in our app, so here’s how your code should look.

/* app.css */
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600&display=swap");

body {
  font-family: "Inter", sans-serif;
  background-color: #e8e8e8;
}

.header {
  display: flex;
  align-items: center;
  min-height: 70px;
  padding: 0.5rem 1rem;
  background-color: #8661d1;
  color: #fff;
  margin-bottom: 2rem;
}

.header a {
  color: #fff;
  text-decoration: none;
}

.header-title {
  font-size: 1.2rem;
  font-weight: normal;
}

.header-title::after {
  content: " ▸ ";
  padding: 0 0.5rem;
}

.header-subtitle {
  font-size: 1.2rem;
}

.container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 1rem;
}

.search-panel {
  display: flex;
}

.search-panel__filters {
  flex: 1;
}

.search-panel__results {
  flex: 3;
}

.ais-Highlight-highlighted {
  color: inherit;
  font-size: inherit;
}

#searchbox {
  margin-bottom: 2rem;
}

#pagination {
  margin: 2rem auto;
  text-align: center;
}

Let’s take a look at the final version of our typesense-integrated ecommerce product search application.

Final Demo

Closing

Typesense was built with several distinctive features primarily aimed at making the developer’s job easier while also giving customer as well as user the ability to provide a better search experience as possible.This article may have been entertaining as well as instructive in terms of how to install typesense from the ground up on a variety of platforms. Join Aviyel’s community to learn more about the open source project, get tips on how to contribute, and join active dev groups.

Call-to-Action

Aviyel is a collaborative platform that assists open source project communities in monetizing and long-term sustainability. To know more visit Aviyel.com and find great blogs and events, just like this one! Sign up now for early access, and don’t forget to follow us on our socials!