diff --git a/src/components/Map/LayerSelector.vue b/src/components/Map/LayerSelector.vue
new file mode 100644
index 0000000000000000000000000000000000000000..967275a5e1d32604e57526b630f50c02fbffdca9
--- /dev/null
+++ b/src/components/Map/LayerSelector.vue
@@ -0,0 +1,176 @@
+<template>
+  <div class="basemaps-items ui accordion styled">
+    <div
+      :class="['basemap-item title', { active }]"
+      @click="$emit('activateGroup', basemap.id)"
+    >
+      <i
+        class="map outline fitted icon"
+        aria-hidden="true"
+      />
+      <span>{{ basemap.title }}</span>
+    </div>
+    <div
+      v-if="queryableLayersOptions.length > 0 && active"
+      :id="`queryable-layers-selector-${basemap.id}`"
+    >
+      <strong>Couche requêtable</strong>
+      <Dropdown
+        :options="queryableLayersOptions"
+        :selected="selectedQueryLayer"
+        :search="true"
+        @update:selection="onQueryLayerChange($event)"
+      />
+    </div>
+    <div
+      :id="`list-${basemap.id}`"
+      :class="['content', { active }]"
+      :data-basemap-index="basemap.id"
+    >
+      <div
+        v-for="(layer, index) in basemap.layers"
+        :key="basemap.id + '-' + layer.id + '-' + index"
+        class="layer-item transition visible item list-group-item"
+        :data-id="layer.id"
+      >
+        <!-- layer id is used for retrieving layer when changing order -->
+        <p class="layer-handle-sort">
+          <i
+            class="th icon"
+            aria-hidden="true"
+          />
+          {{ layer.title }}
+        </p>
+        <label>Opacité &nbsp;<span>(%)</span></label>
+        <div class="range-container">
+          <input
+            type="range"
+            min="0"
+            max="1"
+            :value="layer.opacity"
+            step="0.01"
+            @change="updateOpacity($event, layer)"
+          ><output class="range-output-bubble">{{
+            getOpacity(layer.opacity)
+          }}</output>
+        </div>
+        <div class="ui divider" />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import Sortable from 'sortablejs';
+
+import Dropdown from '@/components/Dropdown.vue';
+import mapService from '@/services/map-service';
+
+
+export default {
+  name: 'LayerSelector',
+
+  components: {
+    Dropdown,
+  },
+
+  props: {
+    basemap: {
+      type: Object,
+      default: null,
+    },
+    baseMaps: {
+      type: Array,
+      default: () => [],
+    },
+    active: {
+      type: Boolean,
+      default: false,
+    },
+  },
+
+  data() {
+    return {
+      baseMap: {},
+      selectedQueryLayer: null,
+      sortable: null,
+    };
+  },
+
+  computed: {
+    queryableLayersOptions() {
+      const queryableLayers = this.basemap.layers.filter((l) => l.queryable === true);
+      return queryableLayers.map((x) => {
+        return {
+          name: x.title,
+          value: x,
+        };
+      });
+    }
+  },
+
+  mounted() {
+    if (this.queryableLayersOptions.length > 0) {
+      this.selectedQueryLayer = this.queryableLayersOptions[0].name;
+    }
+    setTimeout(this.initSortable.bind(this), 1000);
+  },
+
+  methods: {
+    isQueryable(baseMap) {
+      const queryableLayer = baseMap.layers.filter((l) => l.queryable === true);
+      return queryableLayer.length > 0;
+    },
+
+    initSortable() {
+      const element = document.getElementById(`list-${this.basemap.id}`);
+      if (element) {
+        this.sortable = new Sortable(element, {
+          animation: 150,
+          handle: '.layer-handle-sort', // The element that is active to drag
+          ghostClass: 'blue-background-class',
+          dragClass: 'white-opacity-background-class',
+          onEnd: () => this.$emit('onlayerMove'),
+        });
+      } else {
+        console.error(`list-${this.basemap.id} not found in dom`);
+      }
+    },
+
+    onQueryLayerChange(layer) {
+      this.selectedQueryLayer = layer.name;
+      const baseMap = this.baseMaps[0].layers.find((l) => l.title === layer.name);
+      if (baseMap) {
+        baseMap.query = true;
+      } else {
+        console.error('No such param \'query\' found among basemap[0].layers');
+      }
+      layer.query = true;
+    },
+
+    getOpacity(opacity) {
+      return Math.round(parseFloat(opacity) * 100);
+    },
+
+    updateOpacity(event, layer) {
+      mapService.updateOpacity(layer.id, event.target.value);
+      layer.opacity = event.target.value;
+    },
+  }
+};
+</script>
+
+<style>
+.basemap-item.title > i {
+  margin-left: -1em !important;
+}
+.basemap-item.title > span {
+  margin-left: .5em;
+}
+.queryable-layers-dropdown {
+  margin-bottom: 1em;
+}
+.queryable-layers-dropdown > label {
+  font-weight: bold;
+}
+</style>
diff --git a/src/components/Map/SidebarLayers.vue b/src/components/Map/SidebarLayers.vue
index c2faef528133167542f4fceb9e8b418b4c04f3bc..c70b60fe9b6c5435f7b5c7d42e1f2709fee34604 100644
--- a/src/components/Map/SidebarLayers.vue
+++ b/src/components/Map/SidebarLayers.vue
@@ -46,89 +46,37 @@
         Fonds cartographiques
       </h4>
     </div>
-
-    <div
+    <LayerSelector
       v-for="basemap in baseMaps"
       :key="`list-${basemap.id}`"
-      class="basemaps-items ui accordion styled"
-    >
-      <div
-        :class="['basemap-item title', { active: isActive(basemap) }]"
-        @click="activateGroup(basemap)"
-      >
-        {{ basemap.title }}
-      </div>
-      <div
-        v-if="isQueryable(basemap)"
-        :id="`queryable-layers-selector-${basemap.id}`"
-      >
-        <strong>Couche requêtable</strong>
-        <Dropdown
-          :options="getQueryableLayers(basemap)"
-          :selected="selectedQueryLayer"
-          :search="true"
-          @update:selection="onQueryLayerChange($event)"
-        />
-      </div>
-      <div
-        :id="`list-${basemap.id}`"
-        :class="['content', { active: isActive(basemap) }]"
-        :data-basemap-index="basemap.id"
-      >
-        <div
-          v-for="(layer, index) in basemap.layers"
-          :key="basemap.id + '-' + layer.id + '-' + index"
-          class="layer-item transition visible item list-group-item"
-          :data-id="layer.id"
-        >
-          <!-- layer id is used for retrieving layer when changing order -->
-          <p class="layer-handle-sort">
-            <i
-              class="th icon"
-              aria-hidden="true"
-            />
-            {{ layer.title }}
-          </p>
-          <label>Opacité &nbsp;<span>(%)</span></label>
-          <div class="range-container">
-            <input
-              type="range"
-              min="0"
-              max="1"
-              :value="layer.opacity"
-              step="0.01"
-              @change="updateOpacity($event, layer)"
-            ><output class="range-output-bubble">{{
-              getOpacity(layer.opacity)
-            }}</output>
-          </div>
-          <div class="ui divider" />
-        </div>
-      </div>
-    </div>
+      :basemap="basemap"
+      :base-maps="baseMaps"
+      :active="activeBasemap.id === basemap.id"
+      @addLayers="addLayers"
+      @activateGroup="activateGroup"
+      @onlayerMove="onlayerMove"
+    />
   </div>
 </template>
 
 <script>
 import { mapState } from 'vuex';
-import Sortable from 'sortablejs';
-import Dropdown from '@/components/Dropdown.vue';
+
+import LayerSelector from '@/components/Map/LayerSelector.vue';
 import mapService from '@/services/map-service';
 
 export default {
   name: 'SidebarLayers',
 
   components: {
-    Dropdown,
+    LayerSelector
   },
 
   data() {
     return {
-      selectedQueryLayer: null,
-      activeBasemap: null,
       baseMaps: [],
       expanded: false,
-      sortable: null
+      projectSlug: this.$route.params.slug
     };
   },
 
@@ -137,27 +85,29 @@ export default {
       'isOnline',
     ]),
     ...mapState('map', [
-      'availableLayers'
+      'availableLayers',
     ]),
+    activeBasemap() {
+      return this.baseMaps.find((baseMap) => baseMap.active);
+    },
   },
 
   mounted() {
     this.baseMaps = this.$store.state.map.basemaps;
-    const project = this.$route.params.slug;
     const mapOptions =
       JSON.parse(localStorage.getItem('geocontrib-map-options')) || {};
-    if (mapOptions && mapOptions[project]) {
+    if (mapOptions && mapOptions[this.projectSlug]) {
       // If already in the storage, we need to check if the admin did some
       // modification in the basemaps on the server side. The rule is: if one layer has been added
       // or deleted in the server, then we reset the localstorage.
-      const baseMapsFromLocalstorage = mapOptions[project]['basemaps'];
+      const baseMapsFromLocalstorage = mapOptions[this.projectSlug]['basemaps'];
       const areChanges = this.areChangesInBasemaps(
         this.baseMaps,
         baseMapsFromLocalstorage
       );
 
       if (areChanges) {
-        mapOptions[project] = {
+        mapOptions[this.projectSlug] = {
           'map-options': this.baseMaps,
           'current-basemap-index': 0,
         };
@@ -172,7 +122,6 @@ export default {
 
     if (this.baseMaps.length > 0) {
       this.baseMaps[0].active = true;
-      this.activeBasemap = this.baseMaps[0];
       this.addLayers(this.baseMaps[0]);
     } else {
       mapService.addLayers(
@@ -182,120 +131,13 @@ export default {
         this.$store.state.configuration.DEFAULT_BASE_MAP_SCHEMA_TYPE
       );
     }
-    setTimeout(this.initSortable.bind(this), 1000);
   },
 
   methods: {
-    isActive(basemap) {
-      return basemap.active !== undefined && basemap.active;
-    },
-
     toggleSidebar(value) {
       this.expanded = value !== undefined ? value : !this.expanded;
     },
 
-    activateGroup(basemap) {
-      this.baseMaps.forEach((basemap) => (basemap.active = false));
-      basemap.active = true;
-      this.activeBasemap = basemap;
-      basemap.title += ' '; //weird!! Force refresh
-      this.addLayers(basemap);
-
-      let mapOptions = localStorage.getItem('geocontrib-map-options') || {};
-      mapOptions = mapOptions.length ? JSON.parse(mapOptions) : {};
-      const project = this.$route.params.slug;
-      mapOptions[project] = {
-        ...mapOptions[project],
-        'current-basemap-index': basemap.id,
-      };
-      localStorage.setItem(
-        'geocontrib-map-options',
-        JSON.stringify(mapOptions)
-      );
-    },
-
-    updateOpacity(event, layer) {
-      mapService.updateOpacity(layer.id, event.target.value);
-      layer.opacity = event.target.value;
-    },
-
-    getOpacity(opacity) {
-      return Math.round(parseFloat(opacity) * 100);
-    },
-
-    onQueryLayerChange(layer) {
-      this.selectedQueryLayer = layer.name;
-      if (this.baseMaps[0].layers.find((l) => l.title === layer.name)) {
-        this.baseMaps[0].layers.find((l) => l.title === layer.name).query = true;
-      } else {
-        console.error('No such param \'query\' found among basemap[0].layers');
-      }
-      layer.query = true;
-    },
-
-    isQueryable(baseMap) {
-      const queryableLayer = baseMap.layers.filter((l) => l.queryable === true);
-      return queryableLayer.length > 0;
-    },
-
-    onlayerMove() {
-      // Get the names of the current layers in order.
-      const currentLayersNamesInOrder = Array.from(
-        document.getElementsByClassName('layer-item transition visible')
-      ).map((el) => parseInt(el.attributes['data-id'].value));
-      // Create an array to put the layers in order.
-      let movedLayers = [];
-
-      for (const layerName of currentLayersNamesInOrder) {
-        movedLayers.push(
-          this.activeBasemap.layers.filter((el) => el.id === layerName)[0]
-        );
-      }
-      // Remove existing layers undefined
-      movedLayers = movedLayers.filter(function (x) {
-        return x !== undefined;
-      });
-      const eventOrder = new CustomEvent('change-layers-order', {
-        detail: {
-          layers: movedLayers,
-        },
-      });
-      document.dispatchEvent(eventOrder);
-      // Save the basemaps options into the localstorage
-      this.setLocalstorageMapOptions(this.baseMaps);
-    },
-
-    setLocalstorageMapOptions(basemaps) {
-      let mapOptions = localStorage.getItem('geocontrib-map-options') || {};
-      mapOptions = mapOptions.length ? JSON.parse(mapOptions) : {};
-      const project = this.$route.params.slug;
-      mapOptions[project] = {
-        ...mapOptions[project],
-        basemaps: basemaps,
-      };
-      localStorage.setItem(
-        'geocontrib-map-options',
-        JSON.stringify(mapOptions)
-      );
-    },
-
-    initSortable() {
-      this.baseMaps.forEach((basemap) => {
-        const element = document.getElementById(`list-${basemap.id}`);
-        if (element) {
-          this. sortable = new Sortable(element, {
-            animation: 150,
-            handle: '.layer-handle-sort', // The element that is active to drag
-            ghostClass: 'blue-background-class',
-            dragClass: 'white-opacity-background-class',
-            onEnd: this.onlayerMove.bind(this),
-          });
-        } else {
-          console.error(`list-${basemap.id} not found in dom`);
-        }
-
-      });
-    },
     // Check if there are changes in the basemaps settings. Changes are detected if:
     // - one basemap has been added or deleted
     // - one layer has been added or deleted to a basemap
@@ -354,16 +196,6 @@ export default {
       return !(isSameBasemaps && isSameLayers && isSameTitles);
     },
 
-    getQueryableLayers(baseMap) {
-      const queryableLayer = baseMap.layers.filter((l) => l.queryable === true);
-      return queryableLayer.map((x) => {
-        return {
-          name: x.title,
-          value: x,
-        };
-      });
-    },
-
     addLayers(baseMap) {
       baseMap.layers.forEach((layer) => {
         const layerOptions = this.availableLayers.find((l) => l.id === layer.id);
@@ -375,15 +207,57 @@ export default {
       // Slice is done because reverse() changes the original array, so we make a copy first
       mapService.addLayers(baseMap.layers.slice().reverse(), null, null, null,);
     },
+
+    activateGroup(basemapId) {
+      //* to activate a basemap, we need to set the active property to true et set the others to false
+      this.baseMaps = this.baseMaps.map((bm) => {
+        return { ...bm, active: bm.id === basemapId ? true : false };
+      });
+      //* add the basemap that was clicked to the map
+      this.addLayers(this.baseMaps.find((bm) => bm.id === basemapId));
+      //* to persist the settings, we set the localStorage mapOptions per project
+      this.setLocalstorageMapOptions({ 'current-basemap-index': basemapId });
+    },
+
+    onlayerMove() {
+      // Get the names of the current layers in order.
+      const currentLayersNamesInOrder = Array.from(
+        document.getElementsByClassName('layer-item transition visible')
+      ).map((el) => parseInt(el.attributes['data-id'].value));
+      // Create an array to put the layers in order.
+      let movedLayers = [];
+
+      for (const layerName of currentLayersNamesInOrder) {
+        movedLayers.push(
+          this.activeBasemap.layers.find((el) => el.id === layerName)
+        );
+      }
+      // Remove existing layers undefined
+      movedLayers = movedLayers.filter(function (x) {
+        return x !== undefined;
+      });
+      const eventOrder = new CustomEvent('change-layers-order', {
+        detail: {
+          layers: movedLayers,
+        },
+      });
+      document.dispatchEvent(eventOrder);
+      // Save the basemaps options into the localstorage
+      this.setLocalstorageMapOptions({ basemaps: this.baseMaps });
+    },
+
+    setLocalstorageMapOptions(newOptionObj) {
+      let mapOptions = localStorage.getItem('geocontrib-map-options') || {};
+      mapOptions = mapOptions.length ? JSON.parse(mapOptions) : {};
+      mapOptions[this.projectSlug] = {
+        ...mapOptions[this.projectSlug],
+        ...newOptionObj,
+      };
+      localStorage.setItem(
+        'geocontrib-map-options',
+        JSON.stringify(mapOptions)
+      );
+    },
   },
 };
 </script>
-
-<style>
-.queryable-layers-dropdown {
-  margin-bottom: 1em;
-}
-.queryable-layers-dropdown > label {
-  font-weight: bold;
-}
-</style>