How to build a Single Facet Refinement Toggle Component in Algolia Instant Search with VueJS
One the biggest struggle I've had while building Find A Maker was the implementation of the roles toggle button that filters the jobs thanks to Algolia Instant Search with VueJS.
The defaut Facet Refinement component allow you to build a list of checkbox based on a "facet" that you define in your index settings in Algolia.
In my case, I have a list of Job with 4 different booleans attributes like this
class Job
{
// ...
public bool $builder;
public bool $organizer;
public bool $seller;
public bool $designer;
// ...
}
So when I asked Algolia to register Facets based on those attributes, things were messy and impossible to control.
After searching and asking the Algolia team on Github, I finally re-created a VueJS component to extend their own.
The goal was to have one big button "Builder?" on the page, and when you click on it, it becomes "active" and the list of jobs is refined to show only the jobs that have the $builder === true
attribute.
And of course I wanted to customize the button so it has an image and specific html syntax.
Here's the final code
<template>
<div :class="bem()" class="is-inline-block">
<img
src="/img/icons/builder.svg"
title="I'm a builder"
alt="Builder Icon"
:class="{'is-active': isRefined}"
@click="toggleRefinement"
>
</div>
</template>
<script>
import { FACET_OR, FACET_AND, Component } from 'vue-instantsearch';
export default {
mixins: [Component],
data() {
return {
isRefined: false,
blockClassName: 'ais-refinement-list',
facetName: 'builder',
}
},
created() {
this.searchStore.addFacet(this.facetName, this.operator);
},
destroyed() {
this.searchStore.stop();
this.searchStore.removeFacet(this.facetName);
this.searchStore.start();
},
methods: {
toggleRefinement() {
this.isRefined = !this.isRefined;
return this.searchStore.toggleFacetRefinement(
this.facetName,
true
);
},
},
watch: {
operator() {
this.searchStore.addFacet(this.facetName, this.operator);
},
},
};
</script>
As you can see, this component is "hardcoded" for the $builder
attribute. You can easily extract this data and make the component more abstract thanks to VueJS props
system.
<template>
<div :class="{'is-active': isRefined}"
class="box is-inline-block is-capitalized has-text-centered"
v-tooltip="'I\'m a' + name"
@click="toggleRefinement"
>
<div class="icon">
<img
:src="icon"
:title="'I am a' + name"
:alt="name + 'Icon'"
>
</div>
<div class="">{{name}}</div>
</div>
</template>
<script>
import { FACET_OR, FACET_AND, Component } from 'vue-instantsearch';
export default {
props: ['name', 'icon'],
mixins: [Component],
data() {
return {
isRefined: false,
blockClassName: 'ais-refinement-list',
}
},
created() {
this.searchStore.addFacet(this.name, this.operator);
},
destroyed() {
this.searchStore.stop();
this.searchStore.removeFacet(this.name);
this.searchStore.start();
},
methods: {
toggleRefinement() {
this.isRefined = !this.isRefined;
return this.searchStore.toggleFacetRefinement(
this.name,
true
);
},
},
watch: {
operator() {
this.searchStore.addFacet(this.name, this.operator);
},
},
};
</script>
I consider myself as an IT Business Artisan. Or Consultant CTO. I'm a self-taught Web Developper, coach and teacher. My main work is helping and guiding digital startups.
more about meBTC
bc1qgw9a8hyqqwvcls9ln7vhql4mad0qkveutr2td7
ETH
0x3A720717Da03dB6f3c4a4Ff08BC64100205A79d0
2025 © My Dynamic Production SRL All rights Reserved.