diff --git a/samples/stores-list-sync-map/index.njk b/samples/stores-list-sync-map/index.njk
new file mode 100644
index 00000000..6731bfd9
--- /dev/null
+++ b/samples/stores-list-sync-map/index.njk
@@ -0,0 +1,29 @@
+{% extends '../../src/_includes/layout.njk' %}
+{% block html %}
+
+
+
+{% endblock %}
diff --git a/samples/stores-list-sync-map/index.ts b/samples/stores-list-sync-map/index.ts
new file mode 100644
index 00000000..3e64049f
--- /dev/null
+++ b/samples/stores-list-sync-map/index.ts
@@ -0,0 +1,259 @@
+// [START woosmap_stores_list_sync_map]
+let map: woosmap.map.Map;
+let activeLocation: woosmap.map.Marker | null = null;
+let storesOverlay: woosmap.map.StoresOverlay;
+let storesService: woosmap.map.StoresService;
+let localitiesService: woosmap.map.LocalitiesService;
+let visibleStores: woosmap.map.stores.StoreResponse[] = [];
+let allStores: woosmap.map.stores.StoreResponse[] = [];
+
+const storesStyle: woosmap.map.Style = {
+ breakPoint: 14,
+ rules: [],
+ default: {
+ color: "#55baa6",
+ size: 10,
+ minSize: 3,
+ icon: {
+ url: "https://images.woosmap.com/marker-green.svg",
+ },
+ selectedIcon: {
+ url: "https://images.woosmap.com/marker-red.svg",
+ },
+ },
+};
+
+const mapOptions: woosmap.map.MapOptions = {
+ zoom: 5,
+ center: {
+ lat: 46.5,
+ lng: 1.4,
+ },
+ styles: [{ featureType: "all", stylers: [{ saturation: -100 }] }],
+};
+
+const inputElement = document.getElementById(
+ "autocomplete-input",
+) as HTMLInputElement;
+const submitButton = document.getElementById(
+ "submit-button",
+) as HTMLButtonElement;
+
+if (inputElement && submitButton) {
+ inputElement.addEventListener("keydown", handleKeyPress);
+ submitButton.addEventListener("click", handleGeocodeFromSubmit);
+}
+
+function handleKeyPress(event: KeyboardEvent) {
+ if (event.key === "Enter") {
+ handleGeocode(null);
+ }
+}
+
+function handleGeocodeFromSubmit() {
+ handleGeocode(null);
+}
+
+// Function to filter stores based on map bounds
+function filterStoresByBounds(
+ stores: woosmap.map.stores.StoreResponse[],
+ bounds: woosmap.map.LatLngBounds | null,
+): woosmap.map.stores.StoreResponse[] {
+ return stores.filter((store: woosmap.map.stores.StoreResponse) =>
+ bounds
+ ? bounds.contains({
+ lat: store.geometry.coordinates[1],
+ lng: store.geometry.coordinates[0],
+ })
+ : false,
+ );
+}
+
+// Function to get all Stores recursively using query.page parameter
+function getAllStores() {
+ const allStores: woosmap.map.stores.StoreResponse[] = [];
+ const query: woosmap.map.stores.StoresSearchRequest = { storesByPage: 300 };
+ function getStores(storePage?: number) {
+ if (storePage) {
+ query.page = storePage;
+ }
+ return storesService
+ .search(query)
+ .then((response) => {
+ allStores.push(...response.features);
+ if (query.page === response.pagination.pageCount) {
+ return allStores;
+ }
+ return getStores(response.pagination.page + 1);
+ })
+ .catch((err) => {
+ console.error(err);
+ throw new Error("Error getting all stores");
+ });
+ }
+
+ return getStores();
+}
+
+function displayListStores(stores: woosmap.map.stores.StoreResponse[]) {
+ const storeTemplates = stores.map(createStoreTemplate);
+ const storeList = document.querySelector(".stores-list");
+ if (storeList) {
+ storeList.innerHTML = `Results: ${stores.length} stores in bounds
`;
+ storeList.innerHTML += storeTemplates.join("");
+ }
+ addClickListenerToStoreCards(stores);
+}
+
+function createStoreTemplate(
+ store: woosmap.map.stores.StoreResponse,
+ indexStore: number,
+) {
+ const { name, address, contact } = store.properties;
+ const phoneHtml = contact.phone
+ ? ``
+ : "";
+ const websiteHtml = contact.website
+ ? ``
+ : "";
+
+ return `
+
+
+
${name}
+
+
${
+ (address && address.lines && address.lines.join(", ")) ||
+ "Address not available"
+ }
+ ${phoneHtml}
+ ${websiteHtml}
+
+
+
`;
+}
+
+function addClickListenerToStoreCards(
+ stores: woosmap.map.stores.StoreResponse[],
+) {
+ document.querySelectorAll(".summary-store-card").forEach((storeCard) => {
+ storeCard.addEventListener("click", () => {
+ storesOverlay.setSelection(stores[storeCard["dataset"].index]);
+ const storeList = document.querySelector(".stores-list");
+ if (storeList) {
+ Array.from(storeList.children).forEach((child) =>
+ child.classList.remove("active"),
+ );
+ storeCard.classList.add("active");
+ }
+ });
+ });
+}
+
+function createLocalitiesRequest(
+ latlng: woosmap.map.LatLng | null,
+): woosmap.map.localities.LocalitiesGeocodeRequest | null {
+ return inputElement
+ ? latlng
+ ? { latLng: latlng }
+ : { address: inputElement.value }
+ : null;
+}
+
+function handleGeocode(latlng: woosmap.map.LatLng | null) {
+ const localitiesRequest = createLocalitiesRequest(latlng);
+
+ if (localitiesRequest) {
+ localitiesService
+ .geocode(localitiesRequest)
+ .then((localities) => handleGeocodeResults(localities))
+ .catch(handleError);
+ }
+}
+
+function handleGeocodeResults(
+ localities: woosmap.map.localities.LocalitiesGeocodeResponse,
+) {
+ const location = localities.results[0]?.geometry?.location;
+ location && handleSearchResults(location);
+}
+
+function handleSearchResults(originalLatLng: woosmap.map.LatLngLiteral) {
+ if (activeLocation) {
+ activeLocation.setMap(null);
+ }
+
+ if (originalLatLng) {
+ activeLocation = new woosmap.map.Marker({
+ position: originalLatLng,
+ icon: {
+ url: "https://images.woosmap.com/marker.png",
+ scaledSize: {
+ height: 50,
+ width: 32,
+ },
+ },
+ });
+ activeLocation.setMap(map);
+ map.setCenter(originalLatLng);
+ map.setZoom(8);
+ }
+}
+
+function selectStoreOnList(storeId: string) {
+ const storeList = document.querySelector(".stores-list");
+ if (storeList) {
+ const storeElement = Array.from(storeList.children).find(
+ (child) => child.getAttribute("data-store-id") == storeId,
+ );
+ if (storeElement) {
+ Array.from(storeList.children).forEach((child) =>
+ child.classList.remove("active"),
+ );
+ storeElement.scrollIntoView();
+ storeElement.classList.add("active");
+ }
+ }
+}
+function initMap() {
+ map = new window.woosmap.map.Map(
+ document.getElementById("map") as HTMLElement,
+ mapOptions,
+ );
+ storesOverlay = new woosmap.map.StoresOverlay(storesStyle);
+ storesOverlay.setMap(map);
+ map.addListener("bounds_changed", () => {
+ const bounds = map.getBounds();
+ visibleStores = filterStoresByBounds(allStores, bounds);
+ displayListStores(visibleStores);
+ });
+ window.woosmap.map.event.addListener(
+ map,
+ "store_selected",
+ (store: woosmap.map.stores.StoreResponse) => {
+ selectStoreOnList(store.properties.store_id);
+ },
+ );
+
+ storesService = new woosmap.map.StoresService();
+ localitiesService = new woosmap.map.LocalitiesService();
+
+ getAllStores().then((stores: woosmap.map.stores.StoreResponse[]) => {
+ allStores = stores;
+ displayListStores(stores);
+ });
+}
+
+function handleError(error: woosmap.map.errors.APIError) {
+ console.error("Error:", error);
+}
+
+declare global {
+ interface Window {
+ initMap: () => void;
+ }
+}
+window.initMap = initMap;
+// [END woosmap_stores_list_sync_map]
+
+export {};
diff --git a/samples/stores-list-sync-map/stores-list-sync-map.json b/samples/stores-list-sync-map/stores-list-sync-map.json
new file mode 100644
index 00000000..ef2a0b15
--- /dev/null
+++ b/samples/stores-list-sync-map/stores-list-sync-map.json
@@ -0,0 +1,15 @@
+{
+ "title": "Stores List Sync Map",
+ "description": "Get all stores recursively. Synchronise store list with map display using map bounds and contains method",
+ "category": "Services",
+ "callback": "initMap",
+ "WOOSMAP_PUBLIC_API_KEY": "woos-5b16337d-8364-303a-854d-86f87b480aa5",
+ "tag": "stores_list_sync_map",
+ "name": "stores-list-sync-map",
+ "pagination": {
+ "data": "mode",
+ "size": 1,
+ "alias": "mode"
+ },
+ "permalink": "samples/{{ page.fileSlug }}/{{mode}}/{% if mode == 'jsfiddle' %}demo{% else %}index{% endif %}.{{ page.outputFileExtension }}"
+}
diff --git a/samples/stores-list-sync-map/style.scss b/samples/stores-list-sync-map/style.scss
new file mode 100644
index 00000000..089cd27d
--- /dev/null
+++ b/samples/stores-list-sync-map/style.scss
@@ -0,0 +1,129 @@
+@use 'sass:meta'; // To enable @use via meta.load-css and keep comments in order
+
+/* [START woosmap_stores_list_sync_map] */
+@include meta.load-css("../../shared/scss/_sidebar.scss");
+
+@import "../../shared/scss/mixins.scss";
+
+.btn {
+ @include button($background: #3d5afe, $display: inline, $position: relative, $height: 40px);
+}
+
+.btnText {
+ @include actionText($color: #fff);
+}
+
+#sidebar {
+ flex-basis: 25rem;
+ box-shadow: 0 -2px 4px 0 rgba(0, 0, 0, 0.12);
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ z-index: 1;
+}
+
+#stores-panel {
+ overflow: hidden;
+ background: #fff;
+ overflow-y: auto;
+ font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;;
+ scroll-behavior: smooth;
+
+ h3 {
+ padding: 2rem 1.3rem 1.3rem;
+ margin-bottom: 0;
+ outline: 0;
+ }
+
+ ul {
+ margin: 0 !important;
+ padding: 0;
+ list-style: none;
+ }
+
+ button {
+ border: none;
+ }
+}
+
+#search-container {
+ padding: 1rem 0.5rem;
+ background: rgba(250, 251, 252, 1);
+ border-bottom: 1px solid rgba(0, 0, 0, 0.2);
+
+ #search-input-container {
+ display: flex;
+ justify-content: space-between;
+ }
+
+ h3 {
+ margin-top: 0;
+ }
+}
+
+input {
+ padding: 0.6em;
+ border: 1px solid #ccc;
+ box-shadow: inset 0 1px 3px #ddd;
+ border-radius: 4px;
+ vertical-align: middle;
+ font-weight: normal;
+ letter-spacing: 0.01em;
+ font-size: 1em;
+ display: inline-block;
+ box-sizing: border-box;
+ width: 100%;
+ margin-right: 5px;
+
+ &[type="text"]:focus {
+ outline: 0;
+ border-color: #03a9f4;
+ }
+}
+
+.summary-store-card {
+ display: flex;
+ padding: 0.8rem 0.8rem;
+ vertical-align: middle;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.08);
+ justify-content: space-between;
+ cursor: pointer;
+
+ &:hover {
+ background-color: rgba(0, 0, 0, 0.04);
+ }
+
+ &.active {
+ background-color: rgba(0, 0, 0, 0.08);
+ }
+}
+
+.store-address {
+ line-height: 1.6rem;
+}
+
+.store-contact a,
+.store-website a {
+ padding: 0.2em 0;
+ margin-left: 20px;
+ color: #1d1d1d;
+
+ &::before {
+ content: "";
+ background-size: 16px 16px;
+ background-repeat: no-repeat;
+ padding-right: 20px;
+ vertical-align: middle;
+ margin-left: -20px;
+ }
+}
+
+.store-contact a::before {
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24' viewBox='0 0 24 24' width='24'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M6.54 5c.06.89.21 1.76.45 2.59l-1.2 1.2c-.41-1.2-.67-2.47-.76-3.79h1.51m9.86 12.02c.85.24 1.72.39 2.6.45v1.49c-1.32-.09-2.59-.35-3.8-.75l1.2-1.19M7.5 3H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.49c0-.55-.45-1-1-1-1.24 0-2.45-.2-3.57-.57-.1-.04-.21-.05-.31-.05-.26 0-.51.1-.71.29l-2.2 2.2c-2.83-1.45-5.15-3.76-6.59-6.59l2.2-2.2c.28-.28.36-.67.25-1.02C8.7 6.45 8.5 5.25 8.5 4c0-.55-.45-1-1-1z'/%3E%3C/svg%3E");
+}
+
+.store-website a::before {
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24' viewBox='0 0 24 24' width='24'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M17 7h-4v2h4c1.65 0 3 1.35 3 3s-1.35 3-3 3h-4v2h4c2.76 0 5-2.24 5-5s-2.24-5-5-5zm-6 8H7c-1.65 0-3-1.35-3-3s1.35-3 3-3h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-2zm-3-4h8v2H8z'/%3E%3C/svg%3E");
+}
+
+/* [END woosmap_stores_list_sync_map] */