diff --git a/client/src/app/instance/instance-routing.module.ts b/client/src/app/instance/instance-routing.module.ts
index 03c0e21f5b879295d917d7152162fc4e94e73988..53ae1296d4a2648f295956697e42626368d31be5 100644
--- a/client/src/app/instance/instance-routing.module.ts
+++ b/client/src/app/instance/instance-routing.module.ts
@@ -18,6 +18,7 @@ const routes: Routes = [
             { path: '', redirectTo: 'home', pathMatch: 'full' },
             { path: 'home', loadChildren: () => import('./home/home.module').then(m => m.HomeModule) },
             { path: 'search', loadChildren: () => import('./search/search.module').then(m => m.SearchModule) },
+            { path: 'search-multiple', loadChildren: () => import('./search-multiple/search-multiple.module').then(m => m.SearchMultipleModule) },
             { path: 'documentation', loadChildren: () => import('./documentation/documentation.module').then(m => m.DocumentationModule) }
         ]
     }
diff --git a/client/src/app/instance/instance.reducer.ts b/client/src/app/instance/instance.reducer.ts
index 76e651beb8b286a870569e434a9c274b4ba1369a..d14c08ae52efefef8b043088ae0b74cddbb1a44d 100644
--- a/client/src/app/instance/instance.reducer.ts
+++ b/client/src/app/instance/instance.reducer.ts
@@ -11,12 +11,14 @@ import { combineReducers, createFeatureSelector } from '@ngrx/store';
 
 import { RouterReducerState } from 'src/app/custom-route-serializer';
 import * as search from './store/reducers/search.reducer';
+import * as searchMultiple from './store/reducers/search-multiple.reducer';
 import * as samp from './store/reducers/samp.reducer';
 import * as coneSearch from './store/reducers/cone-search.reducer';
 import * as detail from './store/reducers/detail.reducer';
 
 export interface State {
     search: search.State,
+    searchMultiple: searchMultiple.State,
     samp: samp.State,
     coneSearch: coneSearch.State
     detail: detail.State
@@ -24,6 +26,7 @@ export interface State {
 
 const reducers = {
     search: search.searchReducer,
+    searchMultiple: searchMultiple.searchMultipleReducer,
     samp: samp.sampReducer,
     coneSearch: coneSearch.coneSearchReducer,
     detail: detail.detailReducer
diff --git a/client/src/app/instance/search-multiple/components/datasets/dataset-list.component.html b/client/src/app/instance/search-multiple/components/datasets/dataset-list.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..4395a45c9d7adad1d8ebe8fa6598b3c3f90f6fee
--- /dev/null
+++ b/client/src/app/instance/search-multiple/components/datasets/dataset-list.component.html
@@ -0,0 +1,13 @@
+<div class="row">
+    <div *ngFor="let datasetFamily of getDatasetFamilyList()" class="col-12 col-lg-6 col-xl-4 my-3 text-center">
+        <app-datasets-by-family
+            [datasetFamily]="datasetFamily"
+            [datasetList]="getDatasetsByFamily(datasetFamily.id)"
+            [surveyList]="surveyList"
+            [selectedDatasets]="selectedDatasets"
+            [isAllSelected]="getIsAllSelected(datasetFamily.id)"
+            [isAllUnselected]="getIsAllUnselected(datasetFamily.id)"
+            (updateSelectedDatasets)="updateSelectedDatasets.emit($event)">
+        </app-datasets-by-family>
+    </div>
+</div>
diff --git a/client/src/app/instance/search-multiple/components/datasets/dataset-list.component.ts b/client/src/app/instance/search-multiple/components/datasets/dataset-list.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f01569abfd679097f564b04a1563c52806e6957e
--- /dev/null
+++ b/client/src/app/instance/search-multiple/components/datasets/dataset-list.component.ts
@@ -0,0 +1,82 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
+
+import { Dataset, DatasetFamily, Survey } from 'src/app/metamodel/models';
+
+@Component({
+    selector: 'app-dataset-list',
+    templateUrl: 'dataset-list.component.html',
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+/**
+ * @class
+ * @classdesc Search multiple dataset list component.
+ */
+export class DatasetListComponent {
+    @Input() datasetFamilyList: DatasetFamily[];
+    @Input() datasetList: Dataset[];
+    @Input() surveyList: Survey[];
+    @Input() selectedDatasets: string[];
+    @Output() updateSelectedDatasets: EventEmitter<string[]> = new EventEmitter();
+
+    /**
+     * Returns dataset family list sorted by display, that contains datasets with cone search enabled.
+     *
+     * @return Family[]
+     */
+    getDatasetFamilyList(): DatasetFamily[] {
+        const familyId: number[] = [];
+        this.datasetList.forEach(d => {
+            if (!familyId.includes(d.id_dataset_family)) {
+                familyId.push(d.id_dataset_family);
+            }
+        });
+        return this.datasetFamilyList
+            .filter(f => familyId.includes(f.id));
+    }
+
+    /**
+     * Returns dataset list that belongs to the given ID family.
+     *
+     * @param  {number} familyId - The dataset family ID.
+     *
+     * @return Dataset[]
+     */
+    getDatasetsByFamily(familyId: number): Dataset[] {
+        return this.datasetList.filter(d => d.id_dataset_family === familyId);
+    }
+
+    /**
+     * Checks if all datasets that belongs to the given dataset family ID are selected.
+     *
+     * @param  {number} familyId - The dataset family ID.
+     *
+     * @return boolean
+     */
+    getIsAllSelected(familyId: number): boolean {
+        const datasetListName = this.getDatasetsByFamily(familyId).map(d => d.name);
+        const filteredSelectedDatasets = this.selectedDatasets.filter(name => datasetListName.indexOf(name) > -1);
+        return datasetListName.length === filteredSelectedDatasets.length;
+    }
+
+    /**
+     * Checks if none of datasets that belongs to the given dataset family ID are selected.
+     *
+     * @param  {number} familyId - The dataset family ID.
+     *
+     * @return boolean
+     */
+    getIsAllUnselected(familyId: number): boolean {
+        const datasetListName = this.getDatasetsByFamily(familyId).map(d => d.name);
+        const filteredSelectedDatasets = this.selectedDatasets.filter(name => datasetListName.indexOf(name) > -1);
+        return filteredSelectedDatasets.length === 0;
+    }
+}
diff --git a/client/src/app/instance/search-multiple/components/datasets/datasets-by-family.component.html b/client/src/app/instance/search-multiple/components/datasets/datasets-by-family.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..c0ab965e6bdb4d3cabde7446155f1f95ae62490d
--- /dev/null
+++ b/client/src/app/instance/search-multiple/components/datasets/datasets-by-family.component.html
@@ -0,0 +1,43 @@
+<p class="mb-3"><em>{{ datasetFamily.label }}</em></p>
+<div class="row mb-1">
+    <div class="col pr-1">
+        <button (click)="selectAll()" [disabled]="isAllSelected"
+            class="btn btn-sm btn-block btn-outline-secondary letter-spacing">
+            Select All
+        </button>
+    </div>
+    <div class="col pl-1">
+        <button (click)="unselectAll()" [disabled]="isAllUnselected"
+            class="btn btn-sm btn-block btn-outline-secondary letter-spacing">
+            Unselect All
+        </button>
+    </div>
+</div>
+<div class="selectbox p-0">
+    <div *ngFor="let dataset of datasetList">
+        <div *ngIf="isSelected(dataset.name)">
+            <button class="btn btn-block text-left py-1 m-0 rounded-0" (click)="toggleSelection(dataset.name)">
+                <span class="fas fa-fw fa-check-square theme-color"></span>
+                {{ dataset.label }}
+                <span [tooltip]="datasetInfo" placement="right" containerClass="custom-tooltip right-tooltip">
+                    <span class="far fa-question-circle fa-xs"></span>
+                </span>
+            </button>
+        </div>
+        <div *ngIf="!isSelected(dataset.name)">
+            <button class="btn btn-block text-left py-1 m-0 rounded-0" (click)="toggleSelection(dataset.name)">
+                <span class="far fa-fw fa-square text-secondary"></span>
+                {{ dataset.label }}
+                <span [tooltip]="datasetInfo" placement="right" containerClass="custom-tooltip right-tooltip">
+                    <span class="far fa-question-circle fa-xs"></span>
+                </span>
+            </button>
+        </div>
+
+        <ng-template #datasetInfo class="text-left">
+            {{ dataset.description }}
+            <br><br>
+            {{ getSurveyDescription(dataset.survey_name) }}
+        </ng-template>
+    </div>
+</div>
diff --git a/client/src/app/instance/search-multiple/components/datasets/datasets-by-family.component.scss b/client/src/app/instance/search-multiple/components/datasets/datasets-by-family.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..14afb86a712e88c6595f1578648cb2b5c8040dbb
--- /dev/null
+++ b/client/src/app/instance/search-multiple/components/datasets/datasets-by-family.component.scss
@@ -0,0 +1,27 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+.selectbox {
+    height: 200px;
+    overflow-y: auto;
+    border: 1px solid #ced4da;
+    border-radius: .25rem;
+}
+
+.letter-spacing {
+    letter-spacing: 2px;
+}
+
+.selectbox button:hover {
+    background-color: #ECECEC;
+}
+
+.selectbox button:focus {
+    box-shadow: none;
+}
diff --git a/client/src/app/instance/search-multiple/components/datasets/datasets-by-family.component.ts b/client/src/app/instance/search-multiple/components/datasets/datasets-by-family.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8a1a260c5e72f0f86b46ee6910c9e76f1bfe5976
--- /dev/null
+++ b/client/src/app/instance/search-multiple/components/datasets/datasets-by-family.component.ts
@@ -0,0 +1,96 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, ViewEncapsulation } from '@angular/core';
+
+import { Dataset, DatasetFamily, Survey } from 'src/app/metamodel/models';
+
+@Component({
+    selector: 'app-datasets-by-family',
+    templateUrl: 'datasets-by-family.component.html',
+    styleUrls: ['datasets-by-family.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+    encapsulation: ViewEncapsulation.None
+})
+/**
+ * @class
+ * @classdesc Search multiple datasets by family component.
+ */
+export class DatasetsByFamilyComponent {
+    @Input() datasetFamily: DatasetFamily;
+    @Input() datasetList: Dataset[];
+    @Input() surveyList: Survey[];
+    @Input() selectedDatasets: string[];
+    @Input() isAllSelected: boolean;
+    @Input() isAllUnselected: boolean;
+    @Output() updateSelectedDatasets: EventEmitter<string[]> = new EventEmitter();
+
+    /**
+     * Checks if dataset is selected fot the given dataset name.
+     *
+     * @param  {string} dname - The dataset name.
+     *
+     * @return boolean
+     */
+    isSelected(dname: string): boolean {
+        return this.selectedDatasets.filter(i => i === dname).length > 0;
+    }
+
+    /**
+     * Emits event to update dataset list selection with the given updated selected dataset name list.
+     *
+     * @param  {string} dname - The dataset name.
+     */
+    toggleSelection(dname: string): void {
+        const clonedSelectedDatasets = [...this.selectedDatasets];
+        const index = clonedSelectedDatasets.indexOf(dname);
+        if (index > -1) {
+            clonedSelectedDatasets.splice(index, 1);
+        } else {
+            clonedSelectedDatasets.push(dname);
+        }
+        this.updateSelectedDatasets.emit(clonedSelectedDatasets);
+    }
+
+    /**
+     * Emits event to update dataset list selection with all datasets names.
+     */
+    selectAll(): void {
+        const clonedSelectedDatasets = [...this.selectedDatasets];
+        const datasetListName = this.datasetList.map(d => d.name);
+        datasetListName.filter(name => clonedSelectedDatasets.indexOf(name) === -1).forEach(name => {
+            clonedSelectedDatasets.push(name);
+        });
+        this.updateSelectedDatasets.emit(clonedSelectedDatasets);
+    }
+
+    /**
+     * Emits event to update dataset list selection with no datasets names.
+     */
+    unselectAll(): void {
+        const clonedSelectedDatasets = [...this.selectedDatasets];
+        const datasetListName = this.datasetList.map(d => d.name);
+        datasetListName.filter(name => clonedSelectedDatasets.indexOf(name) > -1).forEach(name => {
+            const index = clonedSelectedDatasets.indexOf(name);
+            clonedSelectedDatasets.splice(index, 1);
+        });
+        this.updateSelectedDatasets.emit(clonedSelectedDatasets);
+    }
+
+    /**
+     * Returns survey description of the given survey name.
+     *
+     * @param  {string} surveyName - The survey name.
+     *
+     * @return string
+     */
+    getSurveyDescription(surveyName: string): string {
+        return this.surveyList.find(p => p.name === surveyName).description;
+    }
+}
diff --git a/client/src/app/instance/search-multiple/components/datasets/index.ts b/client/src/app/instance/search-multiple/components/datasets/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..730e87c6c191b0313c5f71920538f3374fe5245f
--- /dev/null
+++ b/client/src/app/instance/search-multiple/components/datasets/index.ts
@@ -0,0 +1,7 @@
+import { DatasetListComponent } from './dataset-list.component';
+import { DatasetsByFamilyComponent } from './datasets-by-family.component';
+
+export const datasetsComponents = [
+    DatasetListComponent,
+    DatasetsByFamilyComponent
+];
diff --git a/client/src/app/instance/search-multiple/components/index.ts b/client/src/app/instance/search-multiple/components/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..53355e402864869e82505c5ac93da6e6c7da61ec
--- /dev/null
+++ b/client/src/app/instance/search-multiple/components/index.ts
@@ -0,0 +1,11 @@
+import { ProgressBarMultipleComponent } from './progress-bar-multiple.component';
+import { SummaryMultipleComponent } from './summary-multiple.component';
+import { datasetsComponents } from './datasets';
+import { resultComponents } from './result';
+
+export const dummiesComponents = [
+    ProgressBarMultipleComponent,
+    SummaryMultipleComponent,
+    datasetsComponents,
+    resultComponents
+];
diff --git a/client/src/app/instance/search-multiple/components/progress-bar-multiple.component.html b/client/src/app/instance/search-multiple/components/progress-bar-multiple.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..4e9fec69aa928951984caba5d04fe6a3dc02d8b1
--- /dev/null
+++ b/client/src/app/instance/search-multiple/components/progress-bar-multiple.component.html
@@ -0,0 +1,51 @@
+<div class="row text-center">
+    <div class="col">
+        <h1>Search around a position in multiple datasets</h1>
+        <p class="text-muted">Fill RA & DEC position, select datasets and display the result.</p>
+    </div>
+</div>
+<div class="progress-navigation">
+    <div class="progress progress-with-circle">
+        <div class="progress-bar" role="progressbar" aria-valuenow="1" aria-valuemin="1" aria-valuemax="4" [ngClass]="getStepClass()"></div>
+    </div>
+    <ul class="nav nav-pills">
+        <li class="nav-item checked" [ngClass]="{'active': currentStep === 'position'}">
+            <a class="nav-link" routerLink="/instance/{{ instance.name }}/search-multiple/position" [queryParams]="queryParams" data-toggle="tab">
+                <div class="icon-circle">
+                    <span class="fas fa-drafting-compass"></span>
+                </div>
+                Position
+            </a>
+        </li>
+
+        <li class="nav-item" [ngClass]="{'active': currentStep === 'datasets', 'checked': datasetsStepChecked}">
+            <a *ngIf="coneSearch" class="nav-link" routerLink="/instance/{{ instance.name }}/search-multiple/datasets" [queryParams]="queryParams" data-toggle="tab">
+                <div class="icon-circle">
+                    <span class="fas fa-book"></span>
+                </div>
+                Datasets
+            </a>
+            <a *ngIf="!coneSearch" class="nav-link disabled" data-toggle="tab">
+                <div class="icon-circle">
+                    <span class="fas fa-book"></span>
+                </div>
+                Datasets
+            </a>
+        </li>
+
+        <li class="nav-item" [ngClass]="{'active': currentStep === 'result', 'checked': resultStepChecked}">
+            <a *ngIf="coneSearch && selectedDatasets.length > 0" class="nav-link" routerLink="/instance/{{ instance.name }}/search-multiple/result" [queryParams]="queryParams" data-toggle="tab">
+                <div class="icon-circle">
+                    <span class="fas fa-table"></span>
+                </div>
+                Result
+            </a>
+            <a *ngIf="!coneSearch || selectedDatasets.length < 1" class="nav-link disabled" data-toggle="tab">
+                <div class="icon-circle">
+                    <span class="fas fa-table"></span>
+                </div>
+                Result
+            </a>
+        </li>
+    </ul>
+</div>
diff --git a/client/src/app/instance/search-multiple/components/progress-bar-multiple.component.scss b/client/src/app/instance/search-multiple/components/progress-bar-multiple.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..0a42a748bbc5a7bef5571be4cbd2b9011a183dd3
--- /dev/null
+++ b/client/src/app/instance/search-multiple/components/progress-bar-multiple.component.scss
@@ -0,0 +1,120 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+.progress-navigation {
+    position: relative;
+    height: 125px;
+    margin-top: 15px;
+}
+
+.progress-with-circle {
+    position: relative;
+    top: 40px;
+    z-index: 50;
+    height: 4px;
+}
+
+.progress-bar {
+    box-shadow: none;
+    -webkit-transition: width .3s ease;
+    -o-transition: width .3s ease;
+    transition: width .3s ease;
+    background-color: #7AC29A;
+}
+
+.nav-pills {
+    background-color: #F3F2EE;
+    position: absolute;
+    width: 100%;
+    height: 4px;
+    top: 40px;
+    text-align: center;
+}
+
+.nav-pills li a {
+    padding: 0;
+    max-width: 78px;
+    margin: 0 auto;
+    color: rgba(0, 0, 0, 0.2);
+    border-radius: 50%;
+    position: relative;
+    top: -32px;
+    z-index: 100;
+}
+
+.icon-circle {
+    font-size: 20px;
+    border: 3px solid #E9ECEF;
+    text-align: center;
+    border-radius: 50%;
+    color: rgba(0, 0, 0, 0.2);
+    font-weight: 600;
+    width: 70px;
+    height: 70px;
+    background-color: #FFFFFF;
+    margin: 0 auto;
+    position: relative;
+    top: -2px;
+}
+
+.nav-item {
+    width: 33%;
+}
+
+.nav-item.checked .icon-circle {
+    border-color: #7AC29A;
+    color: #7AC29A;
+}
+
+.nav-item.checked a {
+    color: #7AC29A !important;
+}
+
+.nav-item.active a {
+    color: #7AC29A !important;
+    background-color: transparent;
+}
+
+.nav-item.active .icon-circle {
+    color: white !important;
+    border-color: #7AC29A;
+    background-color: #7AC29A;
+}
+
+.nav-link.disabled {
+    cursor: not-allowed;
+}
+
+.icon-circle svg {
+    position: absolute;
+    z-index: 1;
+    left: 22px;
+    right: 0;
+    top: 23px;
+}
+
+.positionStep {
+    width: 15%;
+}
+
+.datasetsStep {
+    width: 48%;
+}
+
+.resultStep {
+    width: 100%;
+}
+
+.btn-clear-form span {
+    display: none;
+}
+
+.btn-clear-form:hover span {
+    display: inline;
+}
diff --git a/client/src/app/instance/search-multiple/components/progress-bar-multiple.component.ts b/client/src/app/instance/search-multiple/components/progress-bar-multiple.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d3c3d5105ad47a2aa59d2a81a710813478ee128a
--- /dev/null
+++ b/client/src/app/instance/search-multiple/components/progress-bar-multiple.component.ts
@@ -0,0 +1,52 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
+
+import { Instance } from 'src/app/metamodel/models';
+import { ConeSearch, SearchMultipleQueryParams } from 'src/app/instance/store/models';
+
+@Component({
+    selector: 'app-progress-bar-multiple',
+    templateUrl: 'progress-bar-multiple.component.html',
+    styleUrls: ['progress-bar-multiple.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+/**
+ * @class
+ * @classdesc Search multiple progress bar component.
+ */
+export class ProgressBarMultipleComponent {
+    @Input() instance: Instance;
+    @Input() currentStep: string;
+    @Input() positionStepChecked: boolean;
+    @Input() datasetsStepChecked: boolean;
+    @Input() resultStepChecked: boolean;
+    @Input() coneSearch: ConeSearch;
+    @Input() selectedDatasets: string[];
+    @Input() queryParams: SearchMultipleQueryParams;
+
+    /**
+     * Returns step class that match to the current step.
+     *
+     * @return string
+     */
+    getStepClass(): string {
+        switch (this.currentStep) {
+            case 'position':
+                return 'positionStep';
+            case 'datasets':
+                return 'datasetsStep';
+            case 'result':
+                return 'resultStep';
+            default:
+                return 'positionStep';
+        }
+    }
+}
diff --git a/client/src/app/instance/search-multiple/components/result/index.ts b/client/src/app/instance/search-multiple/components/result/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..73ec101031c08b3bdd8c58d04fed9afcb762ddac
--- /dev/null
+++ b/client/src/app/instance/search-multiple/components/result/index.ts
@@ -0,0 +1,5 @@
+import { OverviewComponent } from './overview.component';
+
+export const resultComponents = [
+    OverviewComponent
+];
diff --git a/client/src/app/instance/search-multiple/components/result/overview.component.html b/client/src/app/instance/search-multiple/components/result/overview.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..91b90040c64e0fa910f1e502ff2d7c33d34a5be1
--- /dev/null
+++ b/client/src/app/instance/search-multiple/components/result/overview.component.html
@@ -0,0 +1,75 @@
+<div class="jumbotron mb-5 py-4">
+    <div *ngIf="getTotalObject() === 0">
+        <div class="">
+            <h2 class="font-weight-bold">No results.</h2>
+        </div>
+        <hr class="my-4">
+        <div class="row justify-content-around">
+            <div class="col-auto border-right">
+                <span class="title">Cone search</span>
+                <ul class="list-unstyled pl-3">
+                    <li>RA = {{ coneSearch.ra }}°</li>
+                    <li>DEC = {{ coneSearch.dec }}°</li>
+                    <li>radius = {{ coneSearch.radius }} arcsecond</li>
+                </ul>
+            </div>
+            <div *ngFor="let datasetFamily of getSortedDatasetFamilyList()" class="col-auto">
+                <span class="title">{{ datasetFamily.label }}</span>
+                <ul class="list-unstyled pl-3">
+                    <li *ngFor="let dataset of getSelectedDatasetsByFamily(datasetFamily.id)" >
+                        {{ dataset.label }} <span class="badge badge-pill badge-light text-danger">0</span>
+                    </li>
+                </ul>
+            </div>
+        </div>
+        <hr class="my-4">
+        <div class="text-center">
+            <a routerLink="/instance/{{ instanceSelected }}/search-multiple/position" class="btn btn-lg btn-outline-primary">
+                <span class="fas fa-undo"></span> Try something else
+            </a>
+        </div>
+    </div>
+
+    <div *ngIf="getTotalObject() > 0">
+        <div class="lead">
+            Found
+            <span class="font-weight-bold">{{ getTotalObject() }}</span>
+            <span *ngIf="getTotalObject() > 1; else object"> objects</span>
+            <ng-template #object> object</ng-template>
+            in
+            <span class="font-weight-bold">{{ getTotalDatasets() }}</span>
+            <span *ngIf="getTotalDatasets() > 1; else dataset"> datasets</span>
+            <ng-template #dataset> dataset</ng-template>.
+        </div>
+        <hr class="my-4">
+        <div class="row justify-content-around">
+            <div class="col-auto border-right">
+                <span class="title">Cone search</span>
+                <ul class="list-unstyled pl-3">
+                    <li>RA = {{ coneSearch.ra }}°</li>
+                    <li>DEC = {{ coneSearch.dec }}°</li>
+                    <li>radius = {{ coneSearch.radius }} arcsecond</li>
+                </ul>
+            </div>
+            <div *ngFor="let datasetFamily of getSortedDatasetFamilyList()" class="col-auto">
+                <span class="title">{{ datasetFamily.label }}</span>
+                <ul class="list-unstyled pl-3">
+                    <li *ngFor="let dataset of getSelectedDatasetsByFamily(datasetFamily.id)" >
+                        {{ dataset.label }}
+                        <span class="badge badge-pill badge-light" [ngClass]="{'text-primary': getCountByDataset(dataset.name) !== 0}">
+                            {{ getCountByDataset(dataset.name) }}
+                        </span>
+                        <span *ngIf="getCountByDataset(dataset.name) < 2"> object found</span>
+                        <span *ngIf="getCountByDataset(dataset.name) > 1"> objects found</span>
+                        <hr *ngIf="getCountByDataset(dataset.name) > 0" class="my-4">
+                        <div *ngIf="getCountByDataset(dataset.name) > 0" class="text-center">
+                            <a routerLink="/instance/{{ instanceSelected }}/search/result/{{ dataset.name }}" [queryParams]="getCsQueryParams()"  class="btn btn-outline-primary">
+                                <span class="fas fa-forward"></span> Go to result
+                            </a>
+                        </div>
+                    </li>
+                </ul>
+            </div>
+        </div>
+    </div>
+</div>
diff --git a/client/src/app/instance/search-multiple/components/result/overview.component.ts b/client/src/app/instance/search-multiple/components/result/overview.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e85af4d2feed7f641e352c6f5a998a7a596899c8
--- /dev/null
+++ b/client/src/app/instance/search-multiple/components/result/overview.component.ts
@@ -0,0 +1,94 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { Component, Input } from '@angular/core';
+
+import { DatasetFamily, Dataset } from 'src/app/metamodel/models';
+import { ConeSearch, SearchMultipleDatasetLength, SearchMultipleQueryParams } from 'src/app/instance/store/models';
+
+@Component({
+    selector: 'app-overview',
+    templateUrl: 'overview.component.html'
+})
+export class OverviewComponent {
+    @Input() instanceSelected: string;
+    @Input() datasetFamilyList: DatasetFamily[];
+    @Input() datasetList: Dataset[];
+    @Input() coneSearch: ConeSearch;
+    @Input() selectedDatasets: string[];
+    @Input() dataLength: SearchMultipleDatasetLength[];
+    @Input() queryParams: SearchMultipleQueryParams;
+
+    /**
+     * Returns total amount of results for all datasets.
+     *
+     * @return number
+     */
+    getTotalObject(): number {
+        return this.dataLength
+            .filter(datasetLength => datasetLength.length > 0)
+            .reduce((sum, datasetLength) => sum + datasetLength.length, 0);
+    }
+
+    /**
+     * Returns total number of datasets with results.
+     *
+     * @return number
+     */
+    getTotalDatasets(): number {
+        return this.dataLength.filter(datasetLength => datasetLength.length > 0).length;
+    }
+
+    /**
+     * Returns dataset families sorted by display, that contains selected datasets.
+     *
+     * @return Family[]
+     */
+    getSortedDatasetFamilyList(): DatasetFamily[] {
+        let datasetFamiliesWithSelectedDataset: DatasetFamily[] = [];
+        this.selectedDatasets.forEach(dname => {
+            const dataset: Dataset = this.datasetList.find(d => d.name === dname);
+            const datasetFamily: DatasetFamily = this.datasetFamilyList.find(f => f.id === dataset.id_dataset_family);
+            if (!datasetFamiliesWithSelectedDataset.includes(datasetFamily)) {
+                datasetFamiliesWithSelectedDataset.push(datasetFamily);
+            }
+        });
+        return datasetFamiliesWithSelectedDataset;
+    }
+
+    /**
+     * Returns selected dataset list for the given dataset family ID.
+     *
+     * @param  {number} familyId - The family ID.
+     *
+     * @return Dataset[]
+     */
+    getSelectedDatasetsByFamily(familyId: number): Dataset[] {
+        return this.datasetList
+            .filter(d => d.id_dataset_family === familyId)
+            .filter(d => this.selectedDatasets.includes(d.name));
+    }
+
+    /**
+     * Returns the result number for the given dataset name.
+     *
+     * @param  {string} dname - The dataset name.
+     *
+     * @return number
+     */
+    getCountByDataset(dname: string): number {
+        return this.dataLength.find(datasetLength => datasetLength.datasetName === dname).length;
+    }
+
+    getCsQueryParams() {
+        return {
+            cs: `${this.coneSearch.ra}:${this.coneSearch.dec}:${this.coneSearch.radius}`
+        }
+    }
+}
diff --git a/client/src/app/instance/search-multiple/components/summary-multiple.component.html b/client/src/app/instance/search-multiple/components/summary-multiple.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..3d60496597bb4fde521dab330da270201431178a
--- /dev/null
+++ b/client/src/app/instance/search-multiple/components/summary-multiple.component.html
@@ -0,0 +1,59 @@
+<div class="border rounded">
+    <p class="lead text-center border-bottom bg-light py-3">Search summary</p>
+
+    <!-- Position -->
+    <p class="text-center font-italic">
+        Position
+    </p>
+    <span *ngIf="coneSearch" class="pl-5">
+        Cone search:
+        <ul class="ml-3 pl-5 list-unstyled">
+            <li>RA = {{ coneSearch.ra }}°</li>
+            <li>DEC = {{ coneSearch.dec }}°</li>
+            <li>radius = {{ coneSearch.radius }} arcsecond</li>
+        </ul>
+    </span>
+    <p *ngIf="!coneSearch" class="pl-5 text-danger font-weight-bold">
+        Not valid position!
+    </p>
+    <hr>
+
+    <!-- Dataset List -->
+    <p class="text-center font-italic">
+        Datasets
+    </p>
+    <div>
+        <p *ngIf="selectedDatasets.length < 1" class="pl-5 text-danger font-weight-bold">
+            At least 1 dataset required!
+        </p>
+        <div *ngIf="selectedDatasets.length > 0">
+            <!-- Accordion Dataset families -->
+            <accordion [isAnimated]="true">
+                <accordion-group *ngFor="let datasetFamily of getDatasetFamilyList()" #ag panelClass="abstract-accordion" [isOpen]="accordionFamilyIsOpen" class="pl-5">
+                    <button class="btn btn-link btn-block clearfix pb-1 text-primary" accordion-heading>
+                        <div class="pull-left float-left">
+                            {{ datasetFamily.label }}
+                            &nbsp;
+                            <span *ngIf="ag.isOpen">
+                                <span class="fas fa-chevron-up"></span>
+                            </span>
+                            <span *ngIf="!ag.isOpen">
+                                <span class="fas fa-chevron-down"></span>
+                            </span>
+                        </div>
+                    </button>
+
+                    <!-- Selected Datasets -->
+                    <ul *ngIf="getSelectedDatasetsByFamily(datasetFamily.id).length > 0; else noDataset" class="mb-0 pl-4 list-unstyled">
+                        <li *ngFor="let dataset of getSelectedDatasetsByFamily(datasetFamily.id)" class="pb-1">
+                            {{ dataset.label }}
+                        </li>
+                    </ul>
+                    <ng-template #noDataset>
+                        <p class="mb-1 pl-4">No selected dataset</p>
+                    </ng-template>
+                </accordion-group>
+            </accordion>
+        </div>
+    </div>
+</div>
diff --git a/client/src/app/instance/search-multiple/components/summary-multiple.component.scss b/client/src/app/instance/search-multiple/components/summary-multiple.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..c837d14e1ce994aeb0857803139aaad3fd7f8d6d
--- /dev/null
+++ b/client/src/app/instance/search-multiple/components/summary-multiple.component.scss
@@ -0,0 +1,12 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+li>span {
+    cursor: pointer;
+}
diff --git a/client/src/app/instance/search-multiple/components/summary-multiple.component.ts b/client/src/app/instance/search-multiple/components/summary-multiple.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..790fcd5c75c8862f0dab3bef3ff867edc4c7a361
--- /dev/null
+++ b/client/src/app/instance/search-multiple/components/summary-multiple.component.ts
@@ -0,0 +1,64 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
+
+import { SearchMultipleQueryParams, ConeSearch } from 'src/app/instance/store/models';
+import { Dataset, DatasetFamily } from 'src/app/metamodel/models';
+
+@Component({
+    selector: 'app-summary-multiple',
+    templateUrl: 'summary-multiple.component.html',
+    styleUrls: ['summary-multiple.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+})
+/**
+ * @class
+ * @classdesc Search multiple summary component.
+ */
+export class SummaryMultipleComponent {
+    @Input() currentStep: string;
+    @Input() coneSearch: ConeSearch;
+    @Input() selectedDatasets: string[];
+    @Input() queryParams: SearchMultipleQueryParams;
+    @Input() datasetFamilyList: DatasetFamily[];
+    @Input() datasetList: Dataset[];
+
+    accordionFamilyIsOpen = true;
+
+    /**
+     * Returns dataset families sorted by display, that contains datasets with cone search enabled.
+     *
+     * @return Family[]
+     */
+    getDatasetFamilyList(): DatasetFamily[] {
+        const familiesId: number[] = [];
+        this.datasetList.forEach(d => {
+            if (!familiesId.includes(d.id_dataset_family)) {
+                familiesId.push(d.id_dataset_family);
+            }
+        });
+        return this.datasetFamilyList
+            .filter(f => familiesId.includes(f.id))
+            //.sort(sortByDisplay);
+    }
+
+    /**
+     * Returns dataset list for the given dataset family ID.
+     *
+     * @param  {number} familyId - The family ID.
+     *
+     * @return Dataset[]
+     */
+    getSelectedDatasetsByFamily(familyId: number): Dataset[] {
+        return this.datasetList
+            .filter(d => d.id_dataset_family === familyId)
+            .filter(d => this.selectedDatasets.includes(d.name));
+    }
+}
diff --git a/client/src/app/instance/search-multiple/containers/abstract-search-multiple.component.ts b/client/src/app/instance/search-multiple/containers/abstract-search-multiple.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d8bbadd476b404c8b20a43d728284d0423c3a67f
--- /dev/null
+++ b/client/src/app/instance/search-multiple/containers/abstract-search-multiple.component.ts
@@ -0,0 +1,58 @@
+import { Directive, OnDestroy, OnInit } from '@angular/core';
+
+import { Store } from '@ngrx/store';
+import { Observable, Subscription } from 'rxjs';
+
+import { Dataset, DatasetFamily } from 'src/app/metamodel/models';
+import { ConeSearch, SearchMultipleQueryParams } from '../../store/models';
+import * as instanceSelector from 'src/app/metamodel/selectors/instance.selector';
+import * as datasetFamilySelector from 'src/app/metamodel/selectors/dataset-family.selector';
+import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector';
+import * as searchMultipleActions from '../../store/actions/search-multiple.actions';
+import * as searchMultipleSelector from 'src/app/instance/store/selectors/search-multiple.selector';
+import * as coneSearchSelector from 'src/app/instance/store/selectors/cone-search.selector';
+
+@Directive()
+export abstract class AbstractSearchMultipleComponent implements OnInit, OnDestroy {
+    public pristine: Observable<boolean>;
+    public instanceSelected: Observable<string>;
+    public datasetFamilyListIsLoading: Observable<boolean>;
+    public datasetFamilyListIsLoaded: Observable<boolean>;
+    public datasetFamilyList: Observable<DatasetFamily[]>;
+    public datasetListIsLoading: Observable<boolean>;
+    public datasetListIsLoaded: Observable<boolean>;
+    public datasetList: Observable<Dataset[]>;
+    public currentStep: Observable<string>;
+    public selectedDatasets: Observable<string[]>;
+    public coneSearch: Observable<ConeSearch>;
+    public queryParams: Observable<SearchMultipleQueryParams>;
+
+    private datasetListSubscription: Subscription;
+
+    constructor(protected store: Store<{ }>) {
+        this.pristine = this.store.select(searchMultipleSelector.selectPristine);
+        this.instanceSelected = this.store.select(instanceSelector.selectInstanceNameByRoute);
+        this.datasetFamilyListIsLoading = store.select(datasetFamilySelector.selectDatasetFamilyListIsLoading);
+        this.datasetFamilyListIsLoaded = store.select(datasetFamilySelector.selectDatasetFamilyListIsLoaded);
+        this.datasetFamilyList = store.select(datasetFamilySelector.selectAllDatasetFamilies);
+        this.datasetListIsLoading = this.store.select(datasetSelector.selectDatasetListIsLoading);
+        this.datasetListIsLoaded = this.store.select(datasetSelector.selectDatasetListIsLoaded);
+        this.datasetList = this.store.select(datasetSelector.selectAllConeSearchDatasets);
+        this.currentStep = this.store.select(searchMultipleSelector.selectCurrentStep)
+        this.selectedDatasets = this.store.select(searchMultipleSelector.selectSelectedDatasets);
+        this.coneSearch = this.store.select(coneSearchSelector.selectConeSearch);
+        this.queryParams = this.store.select(searchMultipleSelector.selectQueryParams);
+    }
+    
+    ngOnInit() {
+        this.datasetListSubscription = this.datasetListIsLoaded.subscribe(datasetListIsLoaded =>  {
+            if (datasetListIsLoaded) {
+                Promise.resolve(null).then(() => this.store.dispatch(searchMultipleActions.initSearch()));
+            }
+        })
+    }
+
+    ngOnDestroy() {
+        this.datasetListSubscription.unsubscribe();
+    }
+}
diff --git a/client/src/app/instance/search-multiple/containers/datasets.component.html b/client/src/app/instance/search-multiple/containers/datasets.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..6c030e4a4f8ca0b8ec8d5ce94b654778d7baf1f0
--- /dev/null
+++ b/client/src/app/instance/search-multiple/containers/datasets.component.html
@@ -0,0 +1,44 @@
+<app-spinner *ngIf="(datasetFamilyListIsLoading | async) || (datasetListIsLoading | async) || (surveyListIsLoading | async)">
+</app-spinner>
+
+<div *ngIf="(datasetFamilyListIsLoaded | async) && (datasetListIsLoaded | async) && (surveyListIsLoaded | async)" class="row mt-4">
+    <div class="col-12 col-md-8 col-lg-9">
+        <div class="border rounded my-2">
+            <p class="border-bottom bg-light text-primary mb-0 py-4 pl-4">Datasets</p>
+            <div class="px-3">
+                <app-dataset-list
+                    [surveyList]="surveyList | async"
+                    [datasetFamilyList]="datasetFamilyList | async"
+                    [datasetList]="datasetList | async"
+                    [selectedDatasets]="selectedDatasets | async"
+                    (updateSelectedDatasets)="updateSelectedDatasets($event)">
+                </app-dataset-list>
+            </div>
+        </div>
+    </div>
+    <div class="col-12 col-md-4 col-lg-3 pt-2">
+        <app-summary-multiple
+            [currentStep]="currentStep | async"
+            [coneSearch]="coneSearch | async"
+            [selectedDatasets]="selectedDatasets | async"
+            [queryParams]="queryParams | async"
+            [datasetFamilyList]="datasetFamilyList | async"
+            [datasetList]="datasetList | async">
+        </app-summary-multiple>
+    </div>
+</div>
+
+<div class="row mt-5 justify-content-between">
+    <div class="col">
+        <a routerLink="/instance/{{ instanceSelected | async }}/search-multiple/position" [queryParams]="queryParams | async" 
+            class="btn btn-outline-secondary">
+            <span class="fas fa-arrow-left"></span> Position
+        </a>
+    </div>
+    <div *ngIf="(selectedDatasets | async).length > 0" class="col col-auto">
+        <a routerLink="/instance/{{ instanceSelected | async }}/search-multiple/result" [queryParams]="queryParams | async"
+            class="btn btn-outline-primary">
+            Result <span class="fas fa-arrow-right"></span>
+        </a>
+    </div>
+</div>
diff --git a/client/src/app/instance/search-multiple/containers/datasets.component.ts b/client/src/app/instance/search-multiple/containers/datasets.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5b990465122fc275552cdd57367d0299df39bccc
--- /dev/null
+++ b/client/src/app/instance/search-multiple/containers/datasets.component.ts
@@ -0,0 +1,45 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { Component } from '@angular/core';
+
+import { Store } from '@ngrx/store';
+import { Observable } from 'rxjs';
+
+import { Survey } from 'src/app/metamodel/models';
+import { AbstractSearchMultipleComponent } from './abstract-search-multiple.component';
+import * as searchMultipleActions from '../../store/actions/search-multiple.actions';
+import * as surveySelector from 'src/app/metamodel/selectors/survey.selector';
+
+@Component({
+    selector: 'app-datasets',
+    templateUrl: 'datasets.component.html'
+})
+export class DatasetsComponent extends AbstractSearchMultipleComponent {
+    public surveyListIsLoading: Observable<boolean>;
+    public surveyListIsLoaded: Observable<boolean>;
+    public surveyList: Observable<Survey[]>;
+
+    constructor(protected store: Store<{ }>) {
+        super(store);
+        this.surveyListIsLoading = store.select(surveySelector.selectSurveyListIsLoading);
+        this.surveyListIsLoaded = store.select(surveySelector.selectSurveyListIsLoaded);
+        this.surveyList = store.select(surveySelector.selectAllSurveys);
+    }
+
+    ngOnInit() {
+        Promise.resolve(null).then(() => this.store.dispatch(searchMultipleActions.changeStep({ step: 'datasets' })));
+        Promise.resolve(null).then(() => this.store.dispatch(searchMultipleActions.checkDatasets()));
+        super.ngOnInit();
+    }
+
+    updateSelectedDatasets(selectedDatasets: string[]) {
+        this.store.dispatch(searchMultipleActions.updateSelectedDatasets({ selectedDatasets }));
+    }
+}
diff --git a/client/src/app/instance/search-multiple/containers/position.component.html b/client/src/app/instance/search-multiple/containers/position.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..c12f7eee58be2fcc472afa3b853db897c874d433
--- /dev/null
+++ b/client/src/app/instance/search-multiple/containers/position.component.html
@@ -0,0 +1,47 @@
+<app-spinner *ngIf="(pristine | async) || (datasetFamilyListIsLoading | async) || (datasetListIsLoading | async)">
+</app-spinner>
+
+<div *ngIf="!(pristine | async) && (datasetFamilyListIsLoaded | async) && (datasetListIsLoaded | async)" class="row mt-4">
+    <div class="col-12 col-md-8 col-lg-9">
+        <div class="border rounded my-2">
+            <p class="border-bottom bg-light text-primary mb-0 py-4 pl-4">Cone Search</p>
+            <div class="row p-4">
+                <div class="col">
+                    <app-cone-search
+                        [coneSearch]="coneSearch | async"
+                        [resolver]="resolver | async"
+                        [resolverIsLoading]="resolverIsLoading | async"
+                        [resolverIsLoaded]="resolverIsLoaded | async"
+                        (retrieveCoordinates)="retrieveCoordinates($event)" #cs>
+                    </app-cone-search>
+                </div>
+                <div class="col-2 text-center align-self-end">
+                    <button class="btn btn-outline-success" *ngIf="!(coneSearch | async)" [hidden]="cs.form.invalid" (click)="addConeSearch(cs.getConeSearch())">
+                        <span class="fas fa-plus fa-fw"></span>
+                    </button>
+                    <button class="btn btn-outline-danger" *ngIf="coneSearch | async" (click)="deleteConeSearch()">
+                        <span class="fa fa-times fa-fw"></span>
+                    </button>
+                </div>
+            </div>
+        </div>
+    </div>
+    <div class="col-12 col-md-4 col-lg-3 pt-2">
+        <app-summary-multiple
+            [currentStep]="currentStep | async"
+            [coneSearch]="coneSearch | async"
+            [selectedDatasets]="selectedDatasets | async"
+            [queryParams]="queryParams | async"
+            [datasetFamilyList]="datasetFamilyList | async"
+            [datasetList]="datasetList | async">
+        </app-summary-multiple>
+    </div>
+</div>
+
+<div *ngIf="coneSearch | async" class="row mt-5 justify-content-end">
+    <div class="col col-auto">
+        <a routerLink="/instance/{{ instanceSelected | async }}/search-multiple/datasets" [queryParams]="queryParams | async"
+            class="btn btn-outline-primary">Datasets <span class="fas fa-arrow-right"></span>
+        </a>
+    </div>
+</div>
diff --git a/client/src/app/instance/search-multiple/containers/position.component.ts b/client/src/app/instance/search-multiple/containers/position.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dea597950d3f0c527909d7059537152eda0c00f8
--- /dev/null
+++ b/client/src/app/instance/search-multiple/containers/position.component.ts
@@ -0,0 +1,53 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { Component } from '@angular/core';
+
+import { Store } from '@ngrx/store';
+import { Observable } from 'rxjs';
+
+import { AbstractSearchMultipleComponent } from './abstract-search-multiple.component';
+import { Resolver, ConeSearch } from '../../store/models';
+import * as searchMultipleActions from '../../store/actions/search-multiple.actions';
+import * as coneSearchActions from '../../store/actions/cone-search.actions';
+import * as coneSearchSelector from '../../store/selectors/cone-search.selector';
+
+@Component({
+    selector: 'app-position',
+    templateUrl: 'position.component.html'
+})
+export class PositionComponent extends AbstractSearchMultipleComponent {
+    public resolver: Observable<Resolver>;
+    public resolverIsLoading: Observable<boolean>;
+    public resolverIsLoaded: Observable<boolean>;
+
+    constructor(protected store: Store<{ }>) {
+        super(store);
+        this.resolver = this.store.select(coneSearchSelector.selectResolver);
+        this.resolverIsLoading = this.store.select(coneSearchSelector.selectResolverIsLoading);
+        this.resolverIsLoaded = this.store.select(coneSearchSelector.selectResolverIsLoaded);
+    }
+
+    ngOnInit() {
+        Promise.resolve(null).then(() => this.store.dispatch(searchMultipleActions.changeStep({ step: 'position' })));
+        super.ngOnInit();
+    }
+
+    addConeSearch(coneSearch: ConeSearch): void {
+        this.store.dispatch(coneSearchActions.addConeSearch({ coneSearch }));
+    }
+
+    deleteConeSearch(): void {
+        this.store.dispatch(coneSearchActions.deleteConeSearch());
+    }
+
+    retrieveCoordinates(name: string): void {
+        this.store.dispatch(coneSearchActions.retrieveCoordinates({ name }));
+    }
+}
diff --git a/client/src/app/instance/search-multiple/containers/result-multiple.component.html b/client/src/app/instance/search-multiple/containers/result-multiple.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..258f1567d2d3f55ae3fba8ef57b014824078f4a8
--- /dev/null
+++ b/client/src/app/instance/search-multiple/containers/result-multiple.component.html
@@ -0,0 +1,41 @@
+<app-spinner *ngIf="(datasetFamilyListIsLoading | async) || (datasetListIsLoading | async) || (dataLengthIsLoading | async)">
+</app-spinner>
+
+<div *ngIf="(datasetFamilyListIsLoaded | async) && (datasetListIsLoaded | async) || (dataLengthIsLoaded | async)" class="row mt-4">
+    <div class="col-12">
+        <app-overview
+            [instanceSelected]="instanceSelected | async"
+            [datasetFamilyList]="datasetFamilyList | async"
+            [datasetList]="datasetList | async"
+            [coneSearch]="coneSearch | async"
+            [selectedDatasets]="selectedDatasets | async"
+            [dataLength]="dataLength | async"
+            [queryParams]="queryParams | async">
+        </app-overview>
+        <!--
+        <app-datasets-result
+            [datasetsCountIsLoaded]="datasetsCountIsLoaded | async"
+            [datasetFamilyList]="datasetFamilyList | async"
+            [datasetList]="datasetList | async"
+            [coneSearch]="coneSearch | async"
+            [selectedDatasets]="selectedDatasets | async"
+            [datasetsCount]="datasetsCount | async"
+            [datasetsWithAttributeList]="datasetsWithAttributeList | async"
+            [allAttributeList]="allAttributeList | async"
+            [datasetsWithData]="datasetsWithData | async"
+            [allData]="allData | async"
+            [selectedData]="selectedData | async"
+            (retrieveMeta)="retrieveMeta($event)"
+            (retrieveData)="retrieveData($event)"
+            (updateSelectedData)="updateSelectedData($event)">
+        </app-datasets-result> -->
+    </div>
+</div>
+<div class="row mt-5 justify-content-between">
+    <div class="col">
+        <a routerLink="/instance/{{ instanceSelected | async }}/search-multiple/datasets" [queryParams]="queryParams | async" 
+            class="btn btn-outline-secondary">
+            <span class="fas fa-arrow-left"></span> Datasets
+        </a>
+    </div>
+</div>
diff --git a/client/src/app/instance/search-multiple/containers/result-multiple.component.ts b/client/src/app/instance/search-multiple/containers/result-multiple.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3e4d66b40fbfcad6aabcededb30c6f628554f20d
--- /dev/null
+++ b/client/src/app/instance/search-multiple/containers/result-multiple.component.ts
@@ -0,0 +1,60 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { Component } from '@angular/core';
+
+import { Observable, Subscription } from 'rxjs';
+import { Store } from '@ngrx/store';
+
+import { SearchMultipleDatasetLength, SearchMultipleDatasetData } from '../../store/models';
+import { AbstractSearchMultipleComponent } from './abstract-search-multiple.component';
+import * as searchMultipleActions from '../../store/actions/search-multiple.actions';
+import * as searchMultipleSelector from 'src/app/instance/store/selectors/search-multiple.selector';
+
+@Component({
+    selector: 'app-result-multiple',
+    templateUrl: 'result-multiple.component.html'
+})
+export class ResultMultipleComponent extends AbstractSearchMultipleComponent {
+    public dataLength: Observable<SearchMultipleDatasetLength[]>;
+    public dataLengthIsLoading: Observable<boolean>;
+    public dataLengthIsLoaded: Observable<boolean>;
+    public data: Observable<SearchMultipleDatasetData[]>;
+    public dataIsLoading: Observable<boolean>;
+    public dataIsLoaded: Observable<boolean>;
+
+    private pristineSubscription: Subscription;
+
+    constructor(protected store: Store<{ }>) {
+        super(store);
+        this.dataLength = this.store.select(searchMultipleSelector.selectDataLength);
+        this.dataLengthIsLoading = this.store.select(searchMultipleSelector.selectDataLengthIsLoading);
+        this.dataLengthIsLoaded = this.store.select(searchMultipleSelector.selectDataLengthIsLoaded);
+        this.data = this.store.select(searchMultipleSelector.selectData);
+        this.dataIsLoading = this.store.select(searchMultipleSelector.selectDataIsLoading);
+        this.dataIsLoaded = this.store.select(searchMultipleSelector.selectDataIsLoaded);
+    }
+
+    ngOnInit() {
+        Promise.resolve(null).then(() => this.store.dispatch(searchMultipleActions.changeStep({ step: 'result' })));
+        Promise.resolve(null).then(() => this.store.dispatch(searchMultipleActions.checkDatasets()));
+        Promise.resolve(null).then(() => this.store.dispatch(searchMultipleActions.checkResult()));
+        super.ngOnInit();
+        this.pristineSubscription = this.pristine.subscribe(pristine => {
+            if (!pristine) {
+                Promise.resolve(null).then(() => this.store.dispatch(searchMultipleActions.retrieveDataLength()));
+            }
+        });
+    }
+
+    ngOnDestroy() {
+        this.pristineSubscription.unsubscribe();
+        super.ngOnDestroy();
+    }
+}
diff --git a/client/src/app/instance/search-multiple/search-multiple-routing.module.ts b/client/src/app/instance/search-multiple/search-multiple-routing.module.ts
index 4438a68664b0e8e931aabab371af2a8841fc845f..a3590eb28c7d96bbd48addb36fa15a9d00c3ff0b 100644
--- a/client/src/app/instance/search-multiple/search-multiple-routing.module.ts
+++ b/client/src/app/instance/search-multiple/search-multiple-routing.module.ts
@@ -10,10 +10,20 @@
 import { NgModule } from '@angular/core';
 import { Routes, RouterModule } from '@angular/router';
 
-//import { SearchMultipleComponent } from './search-multiple.component';
+import { SearchMultipleComponent } from './search-multiple.component';
+import { PositionComponent } from './containers/position.component';
+import { DatasetsComponent } from './containers/datasets.component';
+import { ResultMultipleComponent } from './containers/result-multiple.component';
 
 const routes: Routes = [
-    //{ path: '', component: SearchMultipleComponent }
+    {
+        path: '', component: SearchMultipleComponent, children: [
+            { path: '', redirectTo: 'position', pathMatch: 'full' },
+            { path: 'position', component: PositionComponent },
+            { path: 'datasets', component: DatasetsComponent },
+            { path: 'result', component: ResultMultipleComponent }
+        ]
+    }
 ];
 
 @NgModule({
@@ -23,5 +33,8 @@ const routes: Routes = [
 export class SearchMultipleRoutingModule { }
 
 export const routedComponents = [
-    //SearchMultipleComponent
+    SearchMultipleComponent,
+    PositionComponent,
+    DatasetsComponent,
+    ResultMultipleComponent
 ];
diff --git a/client/src/app/instance/search-multiple/search-multiple.component.html b/client/src/app/instance/search-multiple/search-multiple.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..7f7342be55b65395e810bc751799628776dea30e
--- /dev/null
+++ b/client/src/app/instance/search-multiple/search-multiple.component.html
@@ -0,0 +1,13 @@
+<div class="mx-1 mx-sm-5 px-1 px-sm-5">
+    <app-progress-bar-multiple
+        [instance]="instance | async"
+        [currentStep]="currentStep | async"
+        [positionStepChecked]="positionStepChecked | async"
+        [datasetsStepChecked]="datasetsStepChecked | async"
+        [resultStepChecked]="resultStepChecked | async"
+        [coneSearch]="coneSearch | async"
+        [selectedDatasets]="selectedDatasets | async"
+        [queryParams]="queryParams | async">
+    </app-progress-bar-multiple>
+    <router-outlet></router-outlet>
+</div>
diff --git a/client/src/app/instance/search-multiple/search-multiple.component.ts b/client/src/app/instance/search-multiple/search-multiple.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b0d2b7b88a44d5099b04b2455761c63191ecc8da
--- /dev/null
+++ b/client/src/app/instance/search-multiple/search-multiple.component.ts
@@ -0,0 +1,49 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { Component, } from '@angular/core';
+
+import { Store } from '@ngrx/store';
+import { Observable } from 'rxjs';
+
+import { ConeSearch, SearchMultipleQueryParams } from '../store/models';
+import { Instance } from 'src/app/metamodel/models';
+import * as instanceSelector from 'src/app/metamodel/selectors/instance.selector';
+import * as searchMultipleSelector from '../store/selectors/search-multiple.selector';
+import * as coneSearchSelector from '../store/selectors/cone-search.selector';
+
+@Component({
+    selector: 'app-search-multiple',
+    templateUrl: 'search-multiple.component.html'
+})
+/**
+ * @class
+ * @classdesc Search multiple container.
+ */
+export class SearchMultipleComponent {
+    public instance: Observable<Instance>;
+    public currentStep: Observable<string>;
+    public positionStepChecked: Observable<boolean>;
+    public datasetsStepChecked: Observable<boolean>;
+    public resultStepChecked: Observable<boolean>;
+    public coneSearch: Observable<ConeSearch>;
+    public selectedDatasets: Observable<string[]>;
+    public queryParams: Observable<SearchMultipleQueryParams>;
+
+    constructor(private store: Store<{ }>) {
+        this.instance = this.store.select(instanceSelector.selectInstanceByRouteName);
+        this.currentStep = this.store.select(searchMultipleSelector.selectCurrentStep);
+        this.positionStepChecked = this.store.select(searchMultipleSelector.selectPositionStepChecked);
+        this.datasetsStepChecked = this.store.select(searchMultipleSelector.selectDatasetsStepChecked);
+        this.resultStepChecked = this.store.select(searchMultipleSelector.selectResultStepChecked);
+        this.coneSearch = this.store.select(coneSearchSelector.selectConeSearch);
+        this.selectedDatasets = this.store.select(searchMultipleSelector.selectSelectedDatasets);
+        this.queryParams = this.store.select(searchMultipleSelector.selectQueryParams);
+    }
+}
diff --git a/client/src/app/instance/search-multiple/search-multiple.module.ts b/client/src/app/instance/search-multiple/search-multiple.module.ts
index 6368aaf5ac0eed7e0f4be27e5839c470efd21e25..f309487d7a04e1e064c428c6a1303379f093d57d 100644
--- a/client/src/app/instance/search-multiple/search-multiple.module.ts
+++ b/client/src/app/instance/search-multiple/search-multiple.module.ts
@@ -10,13 +10,19 @@
 import { NgModule } from '@angular/core';
 
 import { SharedModule } from 'src/app/shared/shared.module';
+import { SharedSearchModule } from '../shared-search/shared-search.module';
 import { SearchMultipleRoutingModule, routedComponents } from './search-multiple-routing.module';
+import { dummiesComponents } from './components';
 
 @NgModule({
     imports: [
         SharedModule,
+        SharedSearchModule,
         SearchMultipleRoutingModule
     ],
-    declarations: [routedComponents]
+    declarations: [
+        routedComponents,
+        dummiesComponents
+    ]
 })
 export class SearchMultipleModule { }
diff --git a/client/src/app/instance/search/components/criteria/cone-search-tab.component.html b/client/src/app/instance/search/components/criteria/cone-search-tab.component.html
index ea951c32bd3106f1c745ad5e18876c73d592bad5..f3ec7c1649ba70a4fb2d5a3c4ae111451c49fd15 100644
--- a/client/src/app/instance/search/components/criteria/cone-search-tab.component.html
+++ b/client/src/app/instance/search/components/criteria/cone-search-tab.component.html
@@ -19,8 +19,6 @@
                     [resolver]="resolver"
                     [resolverIsLoading]="resolverIsLoading"
                     [resolverIsLoaded]="resolverIsLoaded"
-                    (addConeSearch)="addConeSearch.emit($event)"
-                    (deleteConeSearch)="deleteConeSearch.emit()"
                     (retrieveCoordinates)="retrieveCoordinates.emit($event)" #cs>
                 </app-cone-search>
             </div>
diff --git a/client/src/app/instance/search/containers/result.component.ts b/client/src/app/instance/search/containers/result.component.ts
index 82f65348ed3b1cd56878cb116aa9eaae9053fec9..f62ca538ed8b10b2a94be5d2b5e3778e92907a22 100644
--- a/client/src/app/instance/search/containers/result.component.ts
+++ b/client/src/app/instance/search/containers/result.component.ts
@@ -68,7 +68,7 @@ export class ResultComponent extends AbstractSearchComponent {
             if (!pristine) {
                 Promise.resolve(null).then(() => this.store.dispatch(searchActions.retrieveDataLength()));
             }
-        })
+        });
     }
 
     sampRegister() {
diff --git a/client/src/app/instance/shared-search/components/cone-search/cone-search.component.ts b/client/src/app/instance/shared-search/components/cone-search/cone-search.component.ts
index 59de49ada64fd52e4fcd09720480a1bb8d2daf7d..0bd90e12c4ef706ddd60ccb4bf6a151e8ea2eb6e 100644
--- a/client/src/app/instance/shared-search/components/cone-search/cone-search.component.ts
+++ b/client/src/app/instance/shared-search/components/cone-search/cone-search.component.ts
@@ -26,8 +26,6 @@ export class ConeSearchComponent implements OnChanges {
     @Input() resolver: Resolver;
     @Input() resolverIsLoading: boolean;
     @Input() resolverIsLoaded: boolean;
-    @Output() addConeSearch: EventEmitter<ConeSearch> = new EventEmitter();
-    @Output() deleteConeSearch: EventEmitter<{ }> = new EventEmitter();
     @Output() retrieveCoordinates: EventEmitter<string> = new EventEmitter();
 
     public form = new FormGroup({
diff --git a/client/src/app/instance/store/actions/search-multiple.actions.ts b/client/src/app/instance/store/actions/search-multiple.actions.ts
new file mode 100644
index 0000000000000000000000000000000000000000..87d20074517eeb51f87b1895a8c81f6d9a5161a8
--- /dev/null
+++ b/client/src/app/instance/store/actions/search-multiple.actions.ts
@@ -0,0 +1,26 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { createAction, props } from '@ngrx/store';
+
+import { SearchMultipleDatasetLength, SearchMultipleDatasetData } from '../models';
+
+export const initSearch = createAction('[Search Multiple] Init Search');
+export const restartSearch = createAction('[Search Multiple] Restart Search');
+export const markAsDirty = createAction('[Search Multiple] Mark As Dirty');
+export const changeStep = createAction('[Search Multiple] Change Step', props<{ step: string }>());
+export const checkDatasets = createAction('[Search Multiple] Check Datasets');
+export const checkResult = createAction('[Search Multiple] Check Result');
+export const updateSelectedDatasets = createAction('[Search Multiple] Update Selected Datasets', props<{ selectedDatasets: string[] }>());
+export const retrieveDataLength = createAction('[Search Multiple] Retrieve Data Length');
+export const retrieveDataLengthSuccess = createAction('[Search Multiple] Retrieve Data Length Success', props<{ dataLength: SearchMultipleDatasetLength[] }>());
+export const retrieveDataLengthFail = createAction('[Search Multiple] Retrieve Data Length Fail');
+export const retrieveData = createAction('[Search Multiple] Retrieve Data');
+export const retrieveDataSuccess = createAction('[Search Multiple] Retrieve Data Success', props<{ data: SearchMultipleDatasetData[] }>());
+export const retrieveDataFail = createAction('[Search Multiple] Retrieve Data Fail');
diff --git a/client/src/app/instance/store/effects/index.ts b/client/src/app/instance/store/effects/index.ts
index 132bb177b9768dd206195f68ed2d2eef1ca6a8fd..582dd51102f0f5b76cdf2db4ff4fd0e3e36307b6 100644
--- a/client/src/app/instance/store/effects/index.ts
+++ b/client/src/app/instance/store/effects/index.ts
@@ -1,11 +1,13 @@
 import { SampEffects } from './samp.effects';
 import { SearchEffects } from './search.effects';
+import { SearchMultipleEffects } from './search-multiple.effects';
 import { ConeSearchEffects } from './cone-search.effects';
 import { DetailEffects } from './detail.effects';
 
 export const instanceEffects = [
     SampEffects,
     SearchEffects,
+    SearchMultipleEffects,
     ConeSearchEffects,
     DetailEffects
 ];
diff --git a/client/src/app/instance/store/effects/search-multiple.effects.ts b/client/src/app/instance/store/effects/search-multiple.effects.ts
new file mode 100644
index 0000000000000000000000000000000000000000..84382d44732bfb1c719f7d0cbaa96e4326c693a3
--- /dev/null
+++ b/client/src/app/instance/store/effects/search-multiple.effects.ts
@@ -0,0 +1,132 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { Injectable } from '@angular/core';
+
+import { Actions, createEffect, ofType, concatLatestFrom } from '@ngrx/effects';
+import { Store, Action } from '@ngrx/store';
+import { forkJoin, of } from 'rxjs';
+import { map, tap, mergeMap, catchError } from 'rxjs/operators';
+import { ToastrService } from 'ngx-toastr';
+
+import { SearchMultipleDatasetLength } from '../models';
+import { SearchService } from '../services/search.service';
+import * as instanceSelector from 'src/app/metamodel/selectors/instance.selector';
+import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector';
+import * as searchMultipleActions from '../actions/search-multiple.actions';
+import * as searchMultipleSelector from '../selectors/search-multiple.selector';
+import * as coneSearchActions from '../actions/cone-search.actions';
+import * as coneSearchSelector from '../selectors/cone-search.selector';
+
+@Injectable()
+export class SearchMultipleEffects {
+    initSearch$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(searchMultipleActions.initSearch),
+            concatLatestFrom(() => [
+                this.store.select(searchMultipleSelector.selectPristine),
+                this.store.select(coneSearchSelector.selectConeSearchByRoute),
+                this.store.select(searchMultipleSelector.selectSelectedDatasetsByRoute),
+                this.store.select(instanceSelector.selectInstanceByRouteName),
+                this.store.select(datasetSelector.selectAllConeSearchDatasets)
+            ]),
+            mergeMap(([action, pristine, coneSearchByRoute, selectedDatasetsByRoute, instance, datasetList]) => {
+                if (!pristine && !coneSearchByRoute) {
+                    // Restart search
+                    return [
+                        coneSearchActions.deleteConeSearch(),
+                        searchMultipleActions.restartSearch()
+                    ];
+                }
+
+                if (!pristine) {
+                    // Default form parameters already loaded or no dataset selected
+                    return of({ type: '[No Action] Load Default Form Parameters' });
+                }
+
+                let actions: Action[] = [
+                    searchMultipleActions.markAsDirty()
+                ];
+
+                // Update cone search
+                if (coneSearchByRoute) {
+                    const params = coneSearchByRoute.split(':');
+                    const coneSearch = {
+                        ra: +params[0],
+                        dec: +params[1],
+                        radius: +params[2]
+                    };
+                    actions.push(coneSearchActions.addConeSearch({ coneSearch }));
+                }
+
+                // Update selected datasets
+                if (selectedDatasetsByRoute) {
+                    // Build output list with the URL query parameters (a)
+                    const selectedDatasets = selectedDatasetsByRoute.split(';');
+                    actions.push(
+                        searchMultipleActions.updateSelectedDatasets({ selectedDatasets }),
+                        searchMultipleActions.checkDatasets()
+                    );
+                } else if (instance.config.search.search_multiple_all_datasets_selected) {
+                    const selectedDatasets = datasetList.map(dataset => dataset.name);
+                    actions.push(
+                        searchMultipleActions.updateSelectedDatasets({ selectedDatasets })
+                    );
+                }
+
+                // Returns actions and mark the form as dirty
+                return actions;
+            })
+        )
+    );
+
+    restartSearch$ = createEffect(() => 
+        this.actions$.pipe(
+            ofType(searchMultipleActions.restartSearch),
+            map(() => searchMultipleActions.initSearch())
+        )
+    );
+
+    retrieveDataLength$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(searchMultipleActions.retrieveDataLength),
+            concatLatestFrom(() => [
+                this.store.select(searchMultipleSelector.selectSelectedDatasets),
+                this.store.select(coneSearchSelector.selectConeSearch)
+            ]),
+            mergeMap(([action, selectedDatasets, coneSearch]) => {
+                const queries = selectedDatasets.map(datasetName => this.searchService.retrieveDataLength(
+                    `${datasetName}?a=count&cs=${coneSearch.ra}:${coneSearch.dec}:${coneSearch.radius}`
+                ).pipe(
+                    map((response: { nb: number }[]) => ({ datasetName, length: response[0].nb }))
+                ));
+
+                return forkJoin(queries)
+                    .pipe(
+                        map((response: SearchMultipleDatasetLength[]) => searchMultipleActions.retrieveDataLengthSuccess({ dataLength: response })),
+                        catchError(() => of(searchMultipleActions.retrieveDataLengthFail()))
+                    )
+            })
+        )
+    );
+
+    retrieveDataLengthFail$ = createEffect(() => 
+        this.actions$.pipe(
+            ofType(searchMultipleActions.retrieveDataLengthFail),
+            tap(() => this.toastr.error('Loading Failed', 'The search multiple data length loading failed'))
+        ), { dispatch: false}
+    );
+
+    constructor(
+        private actions$: Actions,
+        private searchService: SearchService,
+        private store: Store<{ }>,
+        private toastr: ToastrService
+    ) {}
+}
diff --git a/client/src/app/instance/store/models/index.ts b/client/src/app/instance/store/models/index.ts
index c471d6929ad3c505f8a5a9386d3d6f716d49e750..962eede1ad19a550541f007ee3155525a696c59c 100644
--- a/client/src/app/instance/store/models/index.ts
+++ b/client/src/app/instance/store/models/index.ts
@@ -1,6 +1,9 @@
 export * from './criterion.model';
 export * from './search-query-params.model';
+export * from './search-multiple-query-params.model';
 export * from './criterion';
 export * from './pagination.model';
 export * from './cone-search.model';
 export * from './resolver.model';
+export * from './search-multiple-dataset-length';
+export * from './search-multiple-dataset-data';
diff --git a/client/src/app/instance/store/models/search-multiple-dataset-data.ts b/client/src/app/instance/store/models/search-multiple-dataset-data.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fa25ed88176093d7840cdd984c476aed52b25ed3
--- /dev/null
+++ b/client/src/app/instance/store/models/search-multiple-dataset-data.ts
@@ -0,0 +1,13 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+export interface SearchMultipleDatasetData {
+    datasetName: string;
+    data: any[];
+}
diff --git a/client/src/app/instance/store/models/search-multiple-dataset-length.ts b/client/src/app/instance/store/models/search-multiple-dataset-length.ts
new file mode 100644
index 0000000000000000000000000000000000000000..75961a0d5c68acd8878099d32715c9a4c9143242
--- /dev/null
+++ b/client/src/app/instance/store/models/search-multiple-dataset-length.ts
@@ -0,0 +1,13 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+export interface SearchMultipleDatasetLength {
+    datasetName: string;
+    length: number;
+}
diff --git a/client/src/app/instance/store/models/search-multiple-query-params.model.ts b/client/src/app/instance/store/models/search-multiple-query-params.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9e4cd67b1047b0288bf8fcfabe5c895f41a1713a
--- /dev/null
+++ b/client/src/app/instance/store/models/search-multiple-query-params.model.ts
@@ -0,0 +1,18 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Interface for search multiple query parameters.
+ *
+ * @interface SearchMultipleQueryParams
+ */
+export interface SearchMultipleQueryParams {
+    cs?: string;
+    d?: string;
+}
diff --git a/client/src/app/instance/store/reducers/search-multiple.reducer.ts b/client/src/app/instance/store/reducers/search-multiple.reducer.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bd73fbba1df15c2853d55d21157e8a7f30fb1dbb
--- /dev/null
+++ b/client/src/app/instance/store/reducers/search-multiple.reducer.ts
@@ -0,0 +1,114 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { createReducer, on } from '@ngrx/store';
+
+import { SearchMultipleDatasetLength, SearchMultipleDatasetData } from '../models';
+import * as searchMultipleActions from '../actions/search-multiple.actions';
+
+export interface State {
+    pristine: boolean;
+    currentStep: string;
+    positionStepChecked: boolean;
+    datasetsStepChecked: boolean;
+    resultStepChecked: boolean;
+    selectedDatasets: string[];
+    dataLengthIsLoading: boolean;
+    dataLengthIsLoaded: boolean;
+    dataLength: SearchMultipleDatasetLength[];
+    dataIsLoading: boolean;
+    dataIsLoaded: boolean;
+    data: SearchMultipleDatasetData[];
+}
+
+export const initialState: State = {
+    pristine: true,
+    currentStep: null,
+    positionStepChecked: false,
+    datasetsStepChecked: false,
+    resultStepChecked: false,
+    selectedDatasets: [],
+    dataLengthIsLoading: false,
+    dataLengthIsLoaded: false,
+    dataLength: [],
+    dataIsLoading: false,
+    dataIsLoaded: false,
+    data: []
+};
+
+export const searchMultipleReducer = createReducer(
+    initialState,
+    on(searchMultipleActions.restartSearch, () => ({
+        ...initialState,
+        currentStep: 'position'
+    })),
+    on(searchMultipleActions.changeStep, (state, { step }) => ({
+        ...state,
+        currentStep: step
+    })),
+    on(searchMultipleActions.markAsDirty, state => ({
+        ...state,
+        pristine: false
+    })),
+    on(searchMultipleActions.checkDatasets, state => ({
+        ...state,
+        datasetsStepChecked: true
+    })),
+    on(searchMultipleActions.checkResult, state => ({
+        ...state,
+        resultStepChecked: true
+    })),
+    on(searchMultipleActions.updateSelectedDatasets, (state, { selectedDatasets }) => ({
+        ...state,
+        selectedDatasets
+    })),
+    on(searchMultipleActions.retrieveDataLength, state => ({
+        ...state,
+        dataLengthIsLoading: true,
+        dataLengthIsLoaded: false
+    })),
+    on(searchMultipleActions.retrieveDataLengthSuccess, (state, { dataLength }) => ({
+        ...state,
+        dataLength,
+        dataLengthIsLoading: false,
+        dataLengthIsLoaded: true
+    })),
+    on(searchMultipleActions.retrieveDataLengthFail, state => ({
+        ...state,
+        dataLengthIsLoading: false
+    })),
+    on(searchMultipleActions.retrieveData, state => ({
+        ...state,
+        dataIsLoading: true,
+        dataIsLoaded: false
+    })),
+    on(searchMultipleActions.retrieveDataSuccess, (state, { data }) => ({
+        ...state,
+        data,
+        dataIsLoading: false,
+        dataIsLoaded: true
+    })),
+    on(searchMultipleActions.retrieveDataFail, state => ({
+        ...state,
+        dataIsLoading: false
+    }))
+);
+
+export const selectPristine = (state: State) => state.pristine;
+export const selectCurrentStep = (state: State) => state.currentStep;
+export const selectPositionStepChecked = (state: State) => state.positionStepChecked;
+export const selectDatasetsStepChecked = (state: State) => state.datasetsStepChecked;
+export const selectResultStepChecked = (state: State) => state.resultStepChecked;
+export const selectSelectedDatasets = (state: State) => state.selectedDatasets;
+export const selectDataLengthIsLoading = (state: State) => state.dataLengthIsLoading;
+export const selectDataLengthIsLoaded = (state: State) => state.dataLengthIsLoaded;
+export const selectDataLength = (state: State) => state.dataLength;
+export const selectDataIsLoading = (state: State) => state.dataIsLoading;
+export const selectDataIsLoaded = (state: State) => state.dataIsLoaded;
+export const selectData = (state: State) => state.data;
diff --git a/client/src/app/instance/store/selectors/search-multiple.selector.ts b/client/src/app/instance/store/selectors/search-multiple.selector.ts
new file mode 100644
index 0000000000000000000000000000000000000000..61ae2e99501760bef036054717ac11ad5408ad42
--- /dev/null
+++ b/client/src/app/instance/store/selectors/search-multiple.selector.ts
@@ -0,0 +1,108 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { createSelector } from '@ngrx/store';
+
+import { SearchMultipleQueryParams, ConeSearch } from '../models';
+import * as reducer from '../../instance.reducer';
+import * as fromSearchMultiple from '../reducers/search-multiple.reducer';
+import * as coneSearchSelector from './cone-search.selector';
+
+export const selectSearchMultipleState = createSelector(
+    reducer.getInstanceState,
+    (state: reducer.State) => state.searchMultiple
+);
+
+export const selectPristine = createSelector(
+    selectSearchMultipleState,
+    fromSearchMultiple.selectPristine
+);
+
+export const selectCurrentStep = createSelector(
+    selectSearchMultipleState,
+    fromSearchMultiple.selectCurrentStep
+);
+
+export const selectPositionStepChecked = createSelector(
+    selectSearchMultipleState,
+    fromSearchMultiple.selectPositionStepChecked
+);
+
+export const selectDatasetsStepChecked = createSelector(
+    selectSearchMultipleState,
+    fromSearchMultiple.selectDatasetsStepChecked
+);
+
+export const selectResultStepChecked = createSelector(
+    selectSearchMultipleState,
+    fromSearchMultiple.selectResultStepChecked
+);
+
+export const selectSelectedDatasets = createSelector(
+    selectSearchMultipleState,
+    fromSearchMultiple.selectSelectedDatasets
+);
+
+export const selectDataLengthIsLoading = createSelector(
+    selectSearchMultipleState,
+    fromSearchMultiple.selectDataLengthIsLoading
+);
+
+export const selectDataLengthIsLoaded = createSelector(
+    selectSearchMultipleState,
+    fromSearchMultiple.selectDataLengthIsLoaded
+);
+
+export const selectDataLength = createSelector(
+    selectSearchMultipleState,
+    fromSearchMultiple.selectDataLength
+);
+
+export const selectDataIsLoading = createSelector(
+    selectSearchMultipleState,
+    fromSearchMultiple.selectDataIsLoading
+);
+
+export const selectDataIsLoaded = createSelector(
+    selectSearchMultipleState,
+    fromSearchMultiple.selectDataIsLoaded
+);
+
+export const selectData = createSelector(
+    selectSearchMultipleState,
+    fromSearchMultiple.selectData
+);
+
+export const selectQueryParams = createSelector(
+    coneSearchSelector.selectConeSearch,
+    selectSelectedDatasets,
+    (
+        coneSearch: ConeSearch,
+        selectedDatasets: string[]) => {
+        let queryParams: SearchMultipleQueryParams = { };
+        if (coneSearch) {
+            queryParams = {
+                ...queryParams,
+                cs: coneSearch.ra + ':' + coneSearch.dec + ':' + coneSearch.radius
+            };
+        }
+        if (selectedDatasets.length > 0) {
+            queryParams = {
+                ...queryParams,
+                d: selectedDatasets.join(';')
+            };
+        }
+        return queryParams;
+    }
+);
+
+export const selectSelectedDatasetsByRoute = createSelector(
+    reducer.selectRouterState,
+    router => router.state.queryParams.d as string
+);
diff --git a/client/src/app/metamodel/selectors/dataset.selector.ts b/client/src/app/metamodel/selectors/dataset.selector.ts
index 71294e9175bcb756fcbef983db7288edc3a18da3..d456b9e12640f2340cb11dd66d80c6edea60fb35 100644
--- a/client/src/app/metamodel/selectors/dataset.selector.ts
+++ b/client/src/app/metamodel/selectors/dataset.selector.ts
@@ -57,3 +57,8 @@ export const selectDatasetNameByRoute = createSelector(
     reducer.selectRouterState,
     router => router.state.params.dname as string
 );
+
+export const selectAllConeSearchDatasets = createSelector(
+    selectAllDatasets,
+    datasetList => datasetList.filter(dataset => dataset.config.cone_search.cone_search_enabled)
+);
\ No newline at end of file