diff --git a/client/package.json b/client/package.json
index 7e7cad96734364427fdcc7fe3022f1e64f022190..587d474ebd838b8a7878554cdc2c2c9b05da7944 100644
--- a/client/package.json
+++ b/client/package.json
@@ -26,6 +26,7 @@
     "@ngrx/store": "12.1.0",
     "@ngrx/store-devtools": "12.1.0",
     "bootstrap": "4.6",
+    "d3": "^5.15.1",
     "keycloak-angular": "^8.2.0",
     "keycloak-js": "^14.0.0",
     "ngx-bootstrap": "^7.0.0-rc.1",
@@ -39,6 +40,7 @@
     "@angular-devkit/build-angular": "~12.0.4",
     "@angular/cli": "~12.0.4",
     "@angular/compiler-cli": "~12.0.4",
+    "@types/d3": "^5.7.2",
     "@types/jasmine": "~3.6.0",
     "@types/node": "^12.11.1",
     "jasmine-core": "~3.7.0",
diff --git a/client/src/app/instance/detail/components/default/default-object.component.html b/client/src/app/instance/detail/components/default/default-object.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..bc6843ae7d94023d1cd8b39b9c9c57631d17822f
--- /dev/null
+++ b/client/src/app/instance/detail/components/default/default-object.component.html
@@ -0,0 +1,11 @@
+<div class="row justify-content-center">
+    <div class="col col-lg-10 col-xl-8 mt-4">
+        <app-object-data
+            [datasetSelected]="datasetSelected"
+            [outputFamilyList]="outputFamilyList"
+            [outputCategoryList]="outputCategoryList"
+            [attributeList]="attributeList"
+            [object]="object">
+        </app-object-data>
+    </div>
+</div>
diff --git a/client/src/app/instance/detail/components/default/default-object.component.ts b/client/src/app/instance/detail/components/default/default-object.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..058a6eda84682117c638f3b96313f04f6ae192bb
--- /dev/null
+++ b/client/src/app/instance/detail/components/default/default-object.component.ts
@@ -0,0 +1,29 @@
+/**
+ * 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 { Attribute, OutputFamily, OutputCategory } from 'src/app/metamodel/models';
+
+@Component({
+    selector: 'app-default-object',
+    templateUrl: 'default-object.component.html',
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+/**
+ * @class
+ * @classdesc Detail default object component.
+ */
+export class DefaultObjectComponent {
+    @Input() datasetSelected: string;
+    @Input() outputFamilyList: OutputFamily[];
+    @Input() outputCategoryList: OutputCategory[];
+    @Input() attributeList: Attribute[];
+    @Input() object: any;
+}
diff --git a/client/src/app/instance/detail/components/default/index.ts b/client/src/app/instance/detail/components/default/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f36eed1181aaad1346a0f96e9b5375b1f68979b2
--- /dev/null
+++ b/client/src/app/instance/detail/components/default/index.ts
@@ -0,0 +1,5 @@
+import { DefaultObjectComponent } from './default-object.component';
+
+export const defaultComponents = [
+    DefaultObjectComponent
+];
diff --git a/client/src/app/instance/detail/components/index.ts b/client/src/app/instance/detail/components/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e0d32c36208a417c4ec089814d619e25f5a69c6b
--- /dev/null
+++ b/client/src/app/instance/detail/components/index.ts
@@ -0,0 +1,9 @@
+import { defaultComponents } from './default';
+import { spectraComponents } from './spectra';
+import { ObjectDataComponent } from './object-data.component';
+
+export const dummiesComponents = [
+    defaultComponents,
+    spectraComponents,
+    ObjectDataComponent
+];
diff --git a/client/src/app/instance/detail/components/object-data.component.html b/client/src/app/instance/detail/components/object-data.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..8c469250b9ef97abbc24a6c81a407076bef0fa86
--- /dev/null
+++ b/client/src/app/instance/detail/components/object-data.component.html
@@ -0,0 +1,66 @@
+<div *ngIf="getAttributeBySearchFlag('RA') && getAttributeBySearchFlag('DEC')" class="row">
+    <div class="col-12">
+        <table class="table mb-1">
+            <tr>
+                <th>Alpha</th>
+                <th>Delta</th>
+                <th class="text-center" rowspan="2"><img src="assets/cesam_anis80.png" alt="CeSAM logo" /></th>
+            </tr>
+            <tr>
+                <td>{{ object[getAttributeBySearchFlag('RA').label] }}</td>
+                <td>{{ object[getAttributeBySearchFlag('DEC').label] }}</td>
+            </tr>
+        </table>
+        <hr class="mt-0 mb-4">
+    </div>
+</div>
+
+<!-- Accordion families -->
+<accordion [isAnimated]="true">
+    <accordion-group *ngFor="let family of outputFamilyList" #ag [isOpen]="true" class="pl-2">
+        <button class="btn btn-link btn-block clearfix pb-2" accordion-heading>
+            <span class="pull-left float-left text-primary">
+                {{ family.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>
+            </span>
+        </button>
+
+        <!-- Accordion categories -->
+        <accordion [isAnimated]="true">
+            <accordion-group *ngFor="let category of getCategoryByFamilySortedByDisplay(family.id)" #ag [isOpen]="true" class="pl-4">
+                <button class="btn btn-link btn-block clearfix pb-2" accordion-heading>
+                    <span class="pull-left float-left text-primary">
+                        {{ category.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>
+                    </span>
+                </button>
+
+                <!-- Output list -->
+                <div *ngFor="let attribute of getAttributesVisibleByCategory(category.id)" class="row pb-2">
+                    <div class="col-5 font-weight-bold">{{ attribute.form_label }}</div>
+                    <ng-container [ngSwitch]="attribute.renderer_detail">
+                        <div *ngSwitchCase="'download'" class="col">
+                            <a [href]="getDownloadHref(object[attribute.label])" role="button" class="btn btn-primary btn-sm">
+                                <span class="fas fa-download"></span>
+                                {{ object[attribute.label] }}
+                            </a>
+                        </div>
+                        <div *ngSwitchDefault class="col">{{ object[attribute.label] }}</div>
+                    </ng-container >
+                </div>
+            </accordion-group>
+        </accordion>
+    </accordion-group>
+</accordion>
diff --git a/client/src/app/instance/detail/components/object-data.component.ts b/client/src/app/instance/detail/components/object-data.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d834874bede64081c6f7b45b6c5f4f0e5b9a1ad9
--- /dev/null
+++ b/client/src/app/instance/detail/components/object-data.component.ts
@@ -0,0 +1,83 @@
+/**
+ * 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 { Attribute, OutputCategory, OutputFamily } from 'src/app/metamodel/models';
+import { getHost } from 'src/app/shared/utils';
+
+@Component({
+    selector: 'app-object-data',
+    templateUrl: 'object-data.component.html',
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+/**
+ * @class
+ * @classdesc Detail object data component.
+ */
+export class ObjectDataComponent {
+    @Input() datasetSelected: string;
+    @Input() outputFamilyList: OutputFamily[];
+    @Input() outputCategoryList: OutputCategory[];
+    @Input() attributeList: Attribute[];
+    @Input() object: any;
+
+    /**
+     * Returns category list sorted by display, for the given output family ID.
+     *
+     * @param  {number} idFamily - The output family ID.
+     *
+     * @return Category[]
+     */
+    getCategoryByFamilySortedByDisplay(idFamily: number): OutputCategory[] {
+        return this.outputCategoryList
+            .filter(category => category.id_output_family === idFamily)
+            //.sort(sortByDisplay);
+    }
+
+    /**
+     * Returns attribute list sorted by detail display, for the given output category ID.
+     *
+     * @param  {number} idCategory - The output category ID.
+     *
+     * @return Attribute[]
+     */
+    getAttributesVisibleByCategory(idCategory: number): Attribute[] {
+        return this.attributeList
+            .filter(a => a.detail)
+            .filter(a => a.id_output_category === idCategory)
+            .sort((a, b) => a.display_detail - b.display_detail);
+    }
+
+    /**
+     * Returns attribute list sorted by detail display.
+     *
+     * @return Attribute[]
+     */
+    getAttributesVisible(): Attribute[] {
+        return this.attributeList
+            .filter(a => a.detail)
+            .sort((a, b) => a.display_detail - b.display_detail);
+    }
+
+    /**
+     * Returns attribute for the given search flag.
+     *
+     * @param  {string} searchFlag - The search flag.
+     *
+     * @return Attribute
+     */
+    getAttributeBySearchFlag(searchFlag: string): Attribute {
+        return this.getAttributesVisible().find(attribute => attribute.search_flag === searchFlag);
+    }
+
+    getDownloadHref(value: string) {
+        return getHost() + '/download-file/' + this.datasetSelected + '/' + value;
+    }
+}
diff --git a/client/src/app/instance/detail/components/spectra/graph/point.ts b/client/src/app/instance/detail/components/spectra/graph/point.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b45ba9d36139645b8cbb14a7bf558423d614a976
--- /dev/null
+++ b/client/src/app/instance/detail/components/spectra/graph/point.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 Point {
+    x: number,
+    y: number
+}
diff --git a/client/src/app/instance/detail/components/spectra/graph/rays.ts b/client/src/app/instance/detail/components/spectra/graph/rays.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8dbba2bc26073ec94082ec9804d58755aa6beb17
--- /dev/null
+++ b/client/src/app/instance/detail/components/spectra/graph/rays.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.
+ */
+
+ export const emissionLines = [
+    {name: 'SII', wavelength: 10320},
+    {name: 'HeI', wavelength: 7065.2},
+    {name: 'SII', wavelength: 6732.68},
+    {name: 'SII', wavelength: 6718.39},
+    {name: 'NII', wavelength: 6585.27},
+    {name: 'Hα', wavelength: 6564.61},
+    {name: 'NII', wavelength: 6549.86},
+    {name: 'HeI', wavelength: 5876.0},
+    {name: 'OIII', wavelength: 5008.24},
+    {name: 'OIII', wavelength: 4960.29},
+    {name: 'Hβ', wavelength: 4862.70},
+    {name: 'OIII', wavelength: 4364.44},
+    {name: 'Hγ', wavelength: 4341.68},
+    {name: 'Hδ', wavelength: 4102.89},
+    {name: 'SII', wavelength: 4072.0},
+    {name: 'Hksi', wavelength: 3890.15},
+    {name: 'NeIII', wavelength: 3869.81},
+    {name: 'H9', wavelength: 3836.47},
+    {name: 'Hθ', wavelength: 3798.98},
+    {name: 'H11', wavelength: 3771.70},
+    {name: 'OII', wavelength: 3728.49},
+    {name: 'NeV', wavelength: 3426.5},
+    {name: 'NeV', wavelength: 3346.4},
+    {name: 'HeII', wavelength: 3204.03},
+    {name: 'FeII', wavelength: 2964.0},
+    {name: 'MgII', wavelength: 2799.0},
+    {name: 'NII', wavelength: 2142.0},
+    {name: 'CIII', wavelength: 1908.73},
+    {name: 'HeII', wavelength: 1640.42},
+    {name: 'CIV', wavelength: 1549.0},
+    {name: 'SiIV', wavelength: 1397.0},
+    {name: 'CII', wavelength: 1334.53},
+    {name: 'OI', wavelength: 1303.0},
+    {name: 'NV', wavelength: 1240.0},
+    {name: 'LyA', wavelength: 1215.67},
+    {name: 'OVI', wavelength: 1033.0},
+    {name: 'LyB', wavelength: 1025.72},
+    {name: 'LyG', wavelength: 972.53}
+];
+
+export const absorptionLines = [
+    {name: 'TiO', wavelength: 8863.0},
+    {name: 'TiO', wavelength: 8430.0},
+    {name: 'NaI', wavelength: 8197.05},
+    {name: 'NaI', wavelength: 8185.50},
+    {name: 'TiO', wavelength: 7590.0},
+    {name: 'HeI', wavelength: 7065.2},
+    {name: 'Hα', wavelength: 6564.61},
+    {name: 'TiO', wavelength: 6159.0},
+    {name: 'NaD', wavelength: 5892.5},
+    {name: 'TiO', wavelength: 5603.0},
+    {name: 'Ca,Fe', wavelength: 5269.0},
+    {name: 'MgI', wavelength: 5174.12},
+    {name: 'Hβ', wavelength: 4862.70},
+    {name: 'Hγ', wavelength: 4341.68},
+    {name: 'Gband', wavelength: 4304.4},
+    {name: 'CN', wavelength: 4216.0},
+    {name: 'Hδ', wavelength: 4102.89},
+    {name: 'CaII_H', wavelength: 3969.59},
+    {name: 'CaII_K', wavelength: 3934.78},
+    {name: 'Hksi', wavelength: 3890.15},
+    {name: 'H9', wavelength: 3836.47},
+    {name: 'Hθ', wavelength: 3798.98},
+    {name: 'H11', wavelength: 3771.70},
+    {name: 'FeI', wavelength: 3581.0},
+    {name: 'HeII', wavelength: 3204.03},
+    {name: 'FeII', wavelength: 2964.0},
+    {name: 'MgII', wavelength: 2796.35},
+    {name: 'MgII', wavelength: 2803.53},
+    {name: 'FeII', wavelength: 2626.45},
+    {name: 'FeII', wavelength: 2600.17},
+    {name: 'FeII', wavelength: 2586.65},
+    {name: 'FeII', wavelength: 2382.76},
+    {name: 'FeII', wavelength: 2374.46},
+    {name: 'FeII', wavelength: 2344.21},
+    {name: 'FeII', wavelength: 2260.78},
+    {name: 'AlIII', wavelength: 1854.72},
+    {name: 'AlII', wavelength: 1670.78},
+    {name: 'HeII', wavelength: 1640.42},
+    {name: 'FeII', wavelength: 1608.45},
+    {name: 'CIV', wavelength: 1548.20},
+    {name: 'CIV', wavelength: 1550.77},
+    {name: 'SiII', wavelength: 1533.43},
+    {name: 'SiII', wavelength: 1526.71},
+    {name: 'SiIV', wavelength: 1402.77},
+    {name: 'SiIV', wavelength: 1393.75},
+    {name: 'CII', wavelength: 1334.53},
+    {name: 'OI', wavelength: 1302.17},
+    {name: 'OI', wavelength: 1304.86},
+    {name: 'SiII', wavelength: 1260.42},
+    {name: 'NV', wavelength: 1238.82},
+    {name: 'NV', wavelength: 1242.80},
+    {name: 'LyA', wavelength: 1215.67},
+    {name: 'OVI', wavelength: 1037.62},
+    {name: 'OVI', wavelength: 1031.93},
+    {name: 'LyB', wavelength: 1025.72},
+    {name: 'LyG', wavelength: 972.53}
+];
diff --git a/client/src/app/instance/detail/components/spectra/graph/spectra-graph.component.html b/client/src/app/instance/detail/components/spectra/graph/spectra-graph.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..9a1c8a0aeab69b32d8cd98a5a5b96e4f54aa65d6
--- /dev/null
+++ b/client/src/app/instance/detail/components/spectra/graph/spectra-graph.component.html
@@ -0,0 +1,3 @@
+<div id="svg-container">
+    <svg id="mygraph"></svg>
+</div>
diff --git a/client/src/app/instance/detail/components/spectra/graph/spectra-graph.component.scss b/client/src/app/instance/detail/components/spectra/graph/spectra-graph.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..5a58e12529fd174067354db0f9434c1a177a302a
--- /dev/null
+++ b/client/src/app/instance/detail/components/spectra/graph/spectra-graph.component.scss
@@ -0,0 +1,119 @@
+/**
+ * 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.
+ */
+
+.titles text {
+    fill: #5a5a5a;
+    font-size: 24px;
+}
+
+.axis line {
+    fill: none;
+    stroke: black;
+    shape-rendering: crispEdges;
+}
+
+.grid line {
+    fill: none;
+    stroke: lightgray;
+    shape-rendering: crispEdges;
+}
+
+.ray line {
+    stroke-dasharray: 3, 3;
+    stroke-width: 1.5px;
+}
+
+.emission line {
+    stroke: steelblue;
+}
+
+.emission text {
+    fill: steelblue;
+}
+
+.absorption line {
+    stroke: tomato;
+}
+
+.absorption text {
+    fill: tomato;
+}
+
+.spectra-line, .line {
+    fill: none;
+    stroke: black;
+    stroke-width: 1px;
+}
+
+.spectra-area, .area {
+    fill: #D0D0D0;
+    opacity: 0.6;
+}
+
+.overlay {
+    fill: none;
+    pointer-events: all;
+}
+
+.big-circle-tootlip {
+    fill: #7ac29a;
+}
+
+.little-circle-tooltip {
+    fill: rgb(16, 75, 42);
+    stroke: #fff;
+    stroke-width: 1.5px;
+}
+
+.rect-tootlip {
+    fill: #fafafa;
+    stroke: #7ac29a;
+    opacity: 0.9;
+    stroke-width: 1;
+}
+
+.text-tooltip {
+    font-size: 15px;
+    color: #333333;
+    fill: #333333;
+}
+
+.text-y-value {
+    font-weight: bold;
+}
+
+.emission-button circle {
+    stroke: steelblue;
+    stroke-width: 3px;
+    cursor: pointer;
+}
+
+.button-off circle {
+    fill: transparent;
+}
+
+.emission-button-on circle {
+    fill: steelblue;
+} 
+
+.absorption-button circle {
+    stroke: tomato;
+    stroke-width: 3px;
+    cursor: pointer;
+}
+
+.absorption-button-on circle {
+    fill: tomato;
+}
+
+.emission-button text, .absorption-button text {
+    font-size: 15px;
+    color: #333333;
+    fill: #333333;
+}
diff --git a/client/src/app/instance/detail/components/spectra/graph/spectra-graph.component.ts b/client/src/app/instance/detail/components/spectra/graph/spectra-graph.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c4be29c7a18d65b933ad6285702f7d809a642adb
--- /dev/null
+++ b/client/src/app/instance/detail/components/spectra/graph/spectra-graph.component.ts
@@ -0,0 +1,444 @@
+/**
+ * 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, ChangeDetectionStrategy, ViewEncapsulation, OnInit, Input } from '@angular/core';
+
+import * as d3 from 'd3';
+
+import { emissionLines, absorptionLines } from './rays';
+import { SpectraType } from './spectra-type';
+import { Point } from './point';
+
+@Component({
+    selector: 'app-spectra-graph',
+    encapsulation: ViewEncapsulation.None,
+    templateUrl: 'spectra-graph.component.html',
+    styleUrls: ['spectra-graph.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class SpectraGraphComponent implements OnInit {
+    @Input() z: number;
+    @Input() spectraCSV: string;
+
+    svg: d3.Selection<d3.BaseType, unknown, HTMLElement, any>;
+    focus: d3.Selection<SVGGElement, unknown, HTMLElement, any>;
+    width: number;
+    height: number;
+    brushHeight: number;
+    margin = { top: 50, right: 10, bottom: 150, left: 100 };
+    x: d3.ScaleLinear<number, number>;
+    xAxis: d3.Axis<number | { valueOf(): number }>;
+    y: d3.ScaleLinear<number, number>;
+    spectraLine: d3.Line<Point>;
+    spectraArea: d3.Area<Point>;
+    displayEmissionLines = true;
+    dispayAbsorptionLines = true;
+
+    ngOnInit() {
+        this.width = document.getElementById("svg-container").offsetWidth * 1 - this.margin.left - this.margin.right;
+        this.height = 600 - this.margin.top - this.margin.bottom;
+        this.brushHeight = 50;
+        this.x = d3.scaleLinear().range([0, this.width]);
+        this.y = d3.scaleLinear().range([this.height, 0]);
+
+        this.initSvg();
+        this.addTitles();
+
+        const dataset = d3.csvParse<Point, SpectraType>(this.spectraCSV, (row) => {
+            return {
+                x: parseFloat(row.x),
+                y: parseFloat(row.Flux)
+            };
+        });
+
+        this.setupDomain(dataset);
+        this.addGrid();
+        this.addSpectraLine(dataset);
+        this.addSpectraArea(dataset);
+        this.addRays();
+        this.addAxis();
+        this.addRaysButtons();
+        this.addBrush(dataset);
+        this.addTooltip(this.width, this.height, dataset, this.x, this.y);
+    }
+
+    private initSvg(): void {
+        this.svg = d3.select("svg#mygraph")
+            .attr("width", this.width + this.margin.left + this.margin.right)
+            .attr("height", this.height + this.margin.top + this.margin.bottom);
+
+        this.svg.append("defs").append("clipPath")
+            .attr("id", "clip")
+            .append("rect")
+            .attr("width", this.width)
+            .attr("height", this.height);
+
+        this.focus = this.svg.append("g")
+            .attr("class", "focus")
+            .attr("transform", "translate(" + this.margin.left + "," + this.margin.top + ")");
+    }
+
+    private addTitles(): void {
+        const titles = this.focus.append("g")
+            .attr("class", "titles");
+
+        titles.append("text")
+            .attr("x", (this.width / 2))
+            .attr("y", this.height + this.margin.bottom - 20)
+            .attr("text-anchor", "middle")
+            .text("Wavelength [Ångströms]");
+
+        titles.append("text")
+            .attr("x", (this.margin.left * 0.75) * -1)
+            .attr("y", this.height / 2)
+            .attr("text-anchor", "middle")
+            .attr("transform", "rotate(-90," + ((this.margin.left * 0.75) * -1) + "," + this.height / 2 + ")")
+            .text("Flux Fλ (erg/sec/cm2/Å)");
+    }
+
+    private setupDomain(dataset: d3.DSVParsedArray<Point>): void {
+        const xMin = d3.min(dataset, (d) => d.x);
+        const xMax = d3.max(dataset, (d) => d.x);
+        this.x.domain([xMin, xMax]);
+
+        const yMin = d3.min(dataset, (d) => d.y);
+        const yMax = d3.max(dataset, (d) => d.y) * 1.1;
+        this.y.domain([yMin, yMax]);
+    }
+
+    private addGrid(): void {
+        const grid = this.focus.append("g")
+            .attr("class", "grid");
+
+        grid.append("g")
+            .attr("class", "grid-x")
+            .selectAll()
+            .data(this.x.ticks(10))
+            .enter()
+            .append("line")
+            .attr("class", "grid-line-x")
+            .attr("x1", (d: number) => this.x(d))
+            .attr("x2", (d: number) => this.x(d))
+            .attr("y1", 0)
+            .attr("y2", this.height);
+
+        grid.append("g")
+            .attr("class", "grid-y")
+            .selectAll()
+            .data(this.y.ticks(10))
+            .enter()
+            .append("line")
+            .attr("class", "grid-line-y")
+            .attr("x1", 0)
+            .attr("x2", this.width)
+            .attr("y1", (d: number) => this.y(d))
+            .attr("y2", (d: number) => this.y(d));
+    }
+
+    private addSpectraLine(dataset: d3.DSVParsedArray<Point>): void {
+        this.spectraLine = d3.line<Point>()
+            .x((d) => this.x(d.x))
+            .y((d) => this.y(d.y));
+
+        this.focus.append("g")
+            .attr("clip-path", "url(#clip)")
+            .append("path")
+            .datum(dataset)
+            .attr("class", "spectra-line")
+            .attr("d", this.spectraLine);
+    }
+
+    private addSpectraArea(dataset: d3.DSVParsedArray<Point>): void {
+        this.spectraArea = d3.area<Point>()
+            .x((d) => this.x(d.x))
+            .y0(this.height)
+            .y1((d) => this.y(d.y));
+
+        this.focus.append("g")
+            .attr("clip-path", "url(#clip)")
+            .append("path")
+            .datum(dataset)
+            .attr("class", "spectra-area")
+            .attr("d", this.spectraArea);
+    }
+
+    private addRays(): void {
+        const coef = 1 + this.z;
+
+        const er = this.focus.append("g")
+            .style("display", "block")
+            .attr("class", "ray emission")
+            .attr("clip-path", "url(#clip)");
+
+        er.selectAll()
+            .data(emissionLines)
+            .enter()
+            .append("line")
+            .attr("x1", r => this.x(r.wavelength * coef))
+            .attr("x2", r => this.x(r.wavelength * coef))
+            .attr("y1", this.height)
+            .attr("y2", 0);
+
+        er.selectAll()
+            .data(emissionLines)
+            .enter()
+            .append("text")
+            .attr("x", r => this.x(r.wavelength * coef) - 5)
+            .attr("y", this.height * 0.2)
+            .attr("transform", r => "rotate(-90, " + (this.x(r.wavelength * coef) - 5) + "," + this.height * 0.2 + ")")
+            .text(r => r.name);
+
+        const ar = this.focus.append("g")
+            .style("display", "block")
+            .attr("class", "ray absorption")
+            .attr("clip-path", "url(#clip)");
+
+        ar.selectAll()
+            .data(absorptionLines)
+            .enter()
+            .append("line")
+            .attr("x1", r => this.x(r.wavelength * coef))
+            .attr("x2", r => this.x(r.wavelength * coef))
+            .attr("y1", this.height)
+            .attr("y2", 0);
+
+        ar.selectAll()
+            .data(absorptionLines)
+            .enter()
+            .append("text")
+            .attr("x", r => this.x(r.wavelength * coef) - 5)
+            .attr("y", this.height * 0.8)
+            .attr("transform", r => "rotate(-90, " + (this.x(r.wavelength * coef) - 5) + "," + this.height * 0.8 + ")")
+            .text(r => r.name);
+    }
+
+    private addAxis(): void {
+        this.xAxis = d3.axisBottom(this.x);
+        this.focus.append("g")
+            .attr("class", "axis axis-x")
+            .attr("transform", "translate(0," + this.height + ")")
+            .call(this.xAxis);
+
+        this.focus.append("g")
+            .attr("class", "axis axis-y")
+            .call(d3.axisLeft(this.y).tickFormat(d3.format(".1e")))
+    }
+
+    private addRaysButtons(): void {
+        const gButtons = this.svg.append("g")
+            .attr("class", "rays-buttons")
+            .attr("transform", "translate(" + this.margin.left + "," + 25 + ")");
+
+        const emission = gButtons.append("g")
+            .attr("class", "emission-button emission-button-on");
+
+        const circleEmission = emission.append("circle")
+            .attr("cx", 20)
+            .attr("cy", 0)
+            .attr("r", 10);
+
+        circleEmission.on("click", () => {
+            const e = this.focus.select(".emission");
+            if (this.displayEmissionLines) {
+                e.style("display", "none");
+                emission.attr("class", "emission-button button-off");
+            } else {
+                e.style("display", "block");
+                emission.attr("class", "emission-button emission-button-on");
+            }
+            this.displayEmissionLines = !this.displayEmissionLines;
+        });
+
+        emission.append("text")
+            .attr("x", 35)
+            .attr("y", 5)
+            .text("Display emission lines")
+        
+        const absorption = gButtons.append("g")
+            .attr("class", "absorption-button absorption-button-on");
+
+        const circleAbsorption = absorption.append("circle")
+            .attr("cx", 215)
+            .attr("cy", 0)
+            .attr("r", 10);
+
+        circleAbsorption.on("click", () => {
+            const a = this.focus.select(".absorption");
+            if (this.dispayAbsorptionLines) {
+                a.style("display", "none");
+                absorption.attr("class", "absorption-button button-off");
+            } else {
+                a.style("display", "block");
+                absorption.attr("class", "absorption-button absorption-button-on");
+            }
+            this.dispayAbsorptionLines = !this.dispayAbsorptionLines;
+        });
+
+        absorption.append("text")
+            .attr("x", 230)
+            .attr("y", 5)
+            .text("Display absorption lines")
+
+    }
+
+    private addTooltip(
+        width: number,
+        height: number,
+        dataset: d3.DSVParsedArray<Point>,
+        x: d3.ScaleLinear<number, number>,
+        y: d3.ScaleLinear<number, number>
+    ): void {
+        const tooltip = this.focus.append("g")
+            .style("display", "none");
+        
+        tooltip.append("circle")
+            .attr("class", "big-circle-tootlip")
+            .attr("r", 10);
+
+        tooltip.append("circle")
+            .attr("class", "little-circle-tooltip")
+            .attr("r", 4);
+
+        tooltip.append("polyline")
+            .attr("points","0,0 0,40 55,40 60,45 65,40 135,40 135,0 0,0")
+            .attr("class", "rect-tootlip")
+            .attr("transform", "translate(-60, -55)");
+
+        const xValue = tooltip.append("text")
+            .attr("class", "text-tooltip")
+            .attr("transform", "translate(-55, -40)")
+            .append("tspan")
+            .text("X : ")
+            .append("tspan");
+        
+        const yValue = tooltip.append("text")
+            .attr("class", "text-tooltip")
+            .attr("transform", "translate(-55, -24)")
+            .append("tspan")
+            .text("Flux : ")
+            .append("tspan")
+            .attr("class", "text-y-value")
+            
+        const bisectX = d3.bisector((p: Point) => p.x).left;
+        this.focus.append("rect")
+            .attr("class", "overlay")
+            .attr("width", width)
+            .attr("height", height)
+            .on("mouseover", () => tooltip.style("display", null))
+            .on("mouseout", () => tooltip.style("display", "none"))
+            .on("mousemove", (d, i, n) => {
+                const node = n[i];
+                const mouse = d3.mouse(node);
+                const x0 = x.invert(mouse[0]);
+                const index = bisectX(dataset, x0);
+                const datum = dataset[index];
+                tooltip.attr("transform", "translate(" + x(datum.x) + "," + y(datum.y) + ")");
+                xValue.text(datum.x);
+                yValue.text(datum.y);
+            });
+    }
+
+    private addBrush(dataset: d3.DSVParsedArray<Point>): void {
+        const context = this.svg.append("g")
+            .attr("class", "context")
+            .attr("transform", "translate(" + this.margin.left + "," + 480 + ")");
+
+        const xBrush = d3.scaleLinear().range([0, this.width]);
+        const yBrush = d3.scaleLinear().range([this.brushHeight, 0]);
+        xBrush.domain(this.x.domain());
+        yBrush.domain(this.y.domain());
+
+        const xBrushAxis = d3.axisBottom(xBrush);
+
+        const lineBrush = d3.line<Point>()
+            .x((d) => xBrush(d.x))
+            .y((d) => yBrush(d.y));
+
+        context.append("g")
+            .attr("class", "line")
+            .append("path")
+            .datum(dataset)
+            .attr("d", lineBrush);
+
+        const areaBrush = d3.area<Point>()
+            .x((d) => xBrush(d.x))
+            .y0(this.brushHeight)
+            .y1((d) => yBrush(d.y));
+
+        context.append("g")
+            .attr("class", "area")
+            .append("path")
+            .datum(dataset)
+            .attr("d", areaBrush);
+
+        context.append("g")
+            .attr("class", "axis")
+            .attr("transform", "translate(0," + this.brushHeight + ")")
+            .call(xBrushAxis);
+
+        const brush = d3.brushX()
+            .extent([[0, 0], [this.width, this.brushHeight]])
+            .on("end", () => {
+                const selection = d3.event.selection || xBrush.range();
+                this.x.domain(selection.map(xBrush.invert, xBrush));
+
+                // Update spectra graph
+                this.focus.select(".spectra-line")
+                    .attr("d", this.spectraLine);
+                this.focus.select(".spectra-area")
+                    .attr("d", this.spectraArea);
+
+                // Update axis
+                this.focus.select(".axis-x").call(this.xAxis);
+
+                // Update grid
+                this.focus.selectAll(".grid-line-x")
+                    .remove();
+                this.focus.select(".grid-x")
+                    .selectAll()
+                    .data(this.x.ticks())
+                    .enter()
+                    .append("line")
+                    .attr("class", "grid-line-x")
+                    .attr("x1", (d: number) => this.x(d))
+                    .attr("x2", (d: number) => this.x(d))
+                    .attr("y1", 0)
+                    .attr("y2", this.height);
+
+                // Update rays
+                const coef = 1 + this.z;
+                this.focus.select(".emission")
+                    .selectAll("line")
+                    .data(emissionLines)
+                    .attr("x1", r => this.x(r.wavelength * coef))
+                    .attr("x2", r => this.x(r.wavelength * coef));
+                this.focus.select(".emission")
+                    .selectAll("text")
+                    .data(emissionLines)
+                    .attr("x", r => this.x(r.wavelength * coef) - 5)
+                    .attr("transform", r => "rotate(-90, " + (this.x(r.wavelength * coef) - 5) + "," + this.height * 0.2 + ")")
+
+                this.focus.select(".absorption")
+                    .selectAll("line")
+                    .data(absorptionLines)
+                    .attr("x1", r => this.x(r.wavelength * coef))
+                    .attr("x2", r => this.x(r.wavelength * coef));
+                this.focus.select(".absorption")
+                    .selectAll("text")
+                    .data(absorptionLines)
+                    .attr("x", r => this.x(r.wavelength * coef) - 5)
+                    .attr("transform", r => "rotate(-90, " + (this.x(r.wavelength * coef) - 5) + "," + this.height * 0.8 + ")")
+            });
+
+        context.append("g")
+            .attr("class", "brush")
+            .call(brush)
+            .call(brush.move, this.x.range());
+    }
+}
diff --git a/client/src/app/instance/detail/components/spectra/graph/spectra-type.ts b/client/src/app/instance/detail/components/spectra/graph/spectra-type.ts
new file mode 100644
index 0000000000000000000000000000000000000000..180320a2cb8bfb3061fc46289f3d2ee574464c53
--- /dev/null
+++ b/client/src/app/instance/detail/components/spectra/graph/spectra-type.ts
@@ -0,0 +1,10 @@
+/**
+ * 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 type SpectraType = "x" | "Flux";
diff --git a/client/src/app/instance/detail/components/spectra/index.ts b/client/src/app/instance/detail/components/spectra/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..77b5e03935e6f4321da07852a1d56a460bc70c3a
--- /dev/null
+++ b/client/src/app/instance/detail/components/spectra/index.ts
@@ -0,0 +1,7 @@
+import { SpectraObjectComponent } from "./spectra-object.component";
+import { SpectraGraphComponent } from "./graph/spectra-graph.component";
+
+export const spectraComponents = [
+    SpectraObjectComponent,
+    SpectraGraphComponent
+];
diff --git a/client/src/app/instance/detail/components/spectra/spectra-object.component.html b/client/src/app/instance/detail/components/spectra/spectra-object.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..a5c472ccf3df9c49290d85d91f3e7890bf6df7ea
--- /dev/null
+++ b/client/src/app/instance/detail/components/spectra/spectra-object.component.html
@@ -0,0 +1,36 @@
+<div class="row">
+    <div *ngIf="getAttributeSpectraGraph()" class="col col-md-8 col-sm-12">
+        <div *ngIf="spectraIsLoading" id="div-spinner" class="text-center">
+            <span class="fas fa-circle-notch fa-spin fa-3x"></span>
+            <span class="sr-only">Loading...</span>
+        </div>
+        <app-spectra-graph *ngIf="spectraIsLoaded" [z]="getZ()" [spectraCSV]="spectraCSV"></app-spectra-graph>
+    </div>
+
+    <div [ngClass]="{'col-md-4 col-sm-12': getAttributeSpectraGraph()}" class="col mt-4">
+        <div *ngIf="getSpectra()" class="jumbotron row mb-3 p-4">
+            <div class="col-auto align-self-center">
+                <p>Download:</p>
+            </div>
+            <div class="w-100 d-block d-xl-none"></div>
+            <div class="col">
+                <div class="row justify-content-center">
+                    <div class="col-auto">
+                        <a [href]="getSpectra()" target="_blank" class="btn btn-lg btn-block dl-btn">
+                            <!-- <span [inlineSVG]="'assets/logo_spectra.svg'" title="Download SPECTRA archive"></span> -->
+                            Download SPECTRA archive
+                        </a>
+                    </div>
+                </div>
+            </div>
+        </div>
+
+        <app-object-data
+            [datasetSelected]="datasetSelected"
+            [outputFamilyList]="outputFamilyList"
+            [outputCategoryList]="outputCategoryList"
+            [attributeList]="attributeList"
+            [object]="object">
+        </app-object-data>
+    </div>
+</div>
diff --git a/client/src/app/instance/detail/components/spectra/spectra-object.component.scss b/client/src/app/instance/detail/components/spectra/spectra-object.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..260a2036246abdb58c05276b1a262e50360ac3f3
--- /dev/null
+++ b/client/src/app/instance/detail/components/spectra/spectra-object.component.scss
@@ -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.
+ */
+
+.dl-btn {
+    height: 80px;
+    display: inline-block;
+}
diff --git a/client/src/app/instance/detail/components/spectra/spectra-object.component.ts b/client/src/app/instance/detail/components/spectra/spectra-object.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..df397e7ea50f131d916162665963e277f9401229
--- /dev/null
+++ b/client/src/app/instance/detail/components/spectra/spectra-object.component.ts
@@ -0,0 +1,83 @@
+/**
+ * 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, Output, EventEmitter, OnInit } from '@angular/core';
+
+import { Attribute, OutputCategory, OutputFamily } from 'src/app/metamodel/models';
+import { getHost } from 'src/app/shared/utils';
+
+@Component({
+    selector: 'app-spectra-object',
+    templateUrl: 'spectra-object.component.html',
+    styleUrls: ['spectra-object.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+/**
+ * @class
+ * @classdesc Detail spectra object component.
+ *
+ * @implements OnInit
+ */
+export class SpectraObjectComponent implements OnInit {
+    @Input() datasetSelected: string;
+    @Input() outputFamilyList: OutputFamily[];
+    @Input() outputCategoryList: OutputCategory[];
+    @Input() attributeList: Attribute[];
+    @Input() object: any;
+    @Input() spectraIsLoading: boolean;
+    @Input() spectraIsLoaded: boolean;
+    @Input() spectraCSV: string;
+    @Output() getSpectraCSV: EventEmitter<string> = new EventEmitter();
+
+    ngOnInit() {
+        const attributeSpectraGraph = this.getAttributeSpectraGraph();
+        if (attributeSpectraGraph) {
+            Promise.resolve(null).then(() => this.getSpectraCSV.emit(this.object[attributeSpectraGraph.label]));
+        }
+    }
+
+    /**
+     * Returns spectra file URL.
+     *
+     * @return string
+     */
+    getSpectra(): string {
+        const spectraAttribute: Attribute = this.attributeList
+            .filter(a => a.detail)
+            .find(attribute => attribute.search_flag === 'SPECTRUM_1D');
+        return getHost() + '/download-file/' + this.datasetSelected + '/' + this.object[spectraAttribute.label];
+    }
+
+    /**
+     * Returns detail rendered spectra graph attribute.
+     *
+     * @return Attribute
+     */
+    getAttributeSpectraGraph(): Attribute {
+        return this.attributeList
+            .filter(a => a.detail)
+            .find(attribute => attribute.renderer_detail === 'spectra_graph');
+    }
+
+    /**
+     * Returns Z.
+     *
+     * @return number
+     */
+    getZ(): number {
+        const attributeZ = this.attributeList
+            .filter(a => a.detail)
+            .find(attribute => attribute.search_flag === 'Z');
+        if (attributeZ) {
+            return +this.object[attributeZ.label];
+        } else {
+            return 0;
+        }
+    }
+}
diff --git a/client/src/app/instance/detail/containers/detail.component.html b/client/src/app/instance/detail/containers/detail.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..2899b8cf78fc9f670a81366d3efd8b049e074bc6
--- /dev/null
+++ b/client/src/app/instance/detail/containers/detail.component.html
@@ -0,0 +1,34 @@
+<div class="container-fluid">
+    <div *ngIf="!(pristine | async)" class="row mt-2 mb-2 justify-content-between">
+        <div class="col">
+            <button (click)="goBackToResult()" class="btn btn-outline-secondary">
+                <span class="fa fa-backward"></span> Back to search results
+            </button>
+        </div>
+    </div>
+    <app-spinner *ngIf="(attributeListIsLoading | async)
+        || (outputFamilyListIsLoading | async)
+        || (outputCategoryListIsLoading | async)
+        || (objectIsLoading | async)">
+    </app-spinner>
+    <div *ngIf="(attributeListIsLoaded | async) && (outputFamilyListIsLoaded | async) && (outputCategoryListIsLoaded | async) && (objectIsLoaded | async)" [ngSwitch]="getObjectType(attributeList | async)">
+        <app-spectra-object *ngSwitchCase="'spectra'"
+            [datasetSelected]="datasetSelected | async"
+            [outputFamilyList]="outputFamilyList | async"
+            [outputCategoryList]="outputCategoryList | async"
+            [attributeList]="attributeList | async"
+            [object]="object | async"
+            [spectraCSV]="spectraCSV | async"
+            [spectraIsLoading]="spectraIsLoading | async"
+            [spectraIsLoaded]="spectraIsLoaded | async"
+            (getSpectraCSV)="getSpectraCSV($event)">
+        </app-spectra-object>
+        <app-default-object *ngSwitchDefault
+            [datasetSelected]="datasetSelected | async"
+            [outputFamilyList]="outputFamilyList | async"
+            [outputCategoryList]="outputCategoryList | async"
+            [attributeList]="attributeList | async"
+            [object]="object | async">
+        </app-default-object>
+    </div>
+</div>
diff --git a/client/src/app/instance/detail/containers/detail.component.ts b/client/src/app/instance/detail/containers/detail.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..331290c257df23a6736d98e8f7f702e3ab2da687
--- /dev/null
+++ b/client/src/app/instance/detail/containers/detail.component.ts
@@ -0,0 +1,143 @@
+/**
+ * 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, OnInit, OnDestroy } from '@angular/core';
+import { Location } from '@angular/common';
+
+import { Store } from '@ngrx/store';
+import { Observable, Subscription } from 'rxjs';
+
+import { Attribute, OutputCategory, OutputFamily } from 'src/app/metamodel/models';
+import * as fromDetail from 'src/app/instance/store/reducers/detail.reducer';
+import * as detailActions from 'src/app/instance/store/actions/detail.actions';
+import * as detailSelector from 'src/app/instance/store/selectors/detail.selector';
+import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector';
+import * as searchSelector from 'src/app/instance/store/selectors/search.selector';
+import * as outputFamilyActions from 'src/app/metamodel/actions/output-family.actions';
+import * as outputFamilySelector from 'src/app/metamodel/selectors/output-family.selector';
+import * as outputCategoryActions from 'src/app/metamodel/actions/output-category.actions';
+import * as outputCategorySelector from 'src/app/metamodel/selectors/output-category.selector';
+import * as attributeActions from 'src/app/metamodel/actions/attribute.actions';
+import * as attributeSelector from 'src/app/metamodel/selectors/attribute.selector';
+
+/**
+ * Interface for store state.
+ *
+ * @interface StoreState
+ */
+interface StoreState {
+    detail: fromDetail.State;
+}
+
+@Component({
+    selector: 'app-detail-page',
+    templateUrl: 'detail.component.html'
+})
+/**
+ * @class
+ * @classdesc Detail container.
+ *
+ * @implements OnInit
+ * @implements OnDestroy
+ */
+export class DetailComponent implements OnInit, OnDestroy {
+    public datasetSelected: Observable<string>;
+    public pristine: Observable<boolean>;
+    public attributeList: Observable<Attribute[]>;
+    public attributeListIsLoading: Observable<boolean>;
+    public attributeListIsLoaded: Observable<boolean>;
+    public outputFamilyList: Observable<OutputFamily[]>;
+    public outputFamilyListIsLoading: Observable<boolean>;
+    public outputFamilyListIsLoaded: Observable<boolean>;
+    public outputCategoryList: Observable<OutputCategory[]>;
+    public outputCategoryListIsLoading: Observable<boolean>;
+    public outputCategoryListIsLoaded: Observable<boolean>;
+    public object: Observable<any>;
+    public objectIsLoading: Observable<boolean>;
+    public objectIsLoaded: Observable<boolean>;
+    public spectraCSV: Observable<string>;
+    public spectraIsLoading: Observable<boolean>;
+    public spectraIsLoaded: Observable<boolean>;
+
+    private attributeListIsLoadedSubscription: Subscription;
+
+    constructor(private location: Location, private store: Store<StoreState>) {
+        this.datasetSelected = store.select(datasetSelector.selectDatasetNameByRoute);
+        this.pristine = store.select(searchSelector.selectPristine);
+        this.attributeList = store.select(attributeSelector.selectAllAttributes);
+        this.attributeListIsLoading = store.select(attributeSelector.selectAttributeListIsLoading);
+        this.attributeListIsLoaded = store.select(attributeSelector.selectAttributeListIsLoaded);
+        this.outputFamilyList = store.select(outputFamilySelector.selectAllOutputFamilies);
+        this.outputFamilyListIsLoading = store.select(outputFamilySelector.selectOutputFamilyListIsLoading);
+        this.outputFamilyListIsLoaded = store.select(outputFamilySelector.selectOutputFamilyListIsLoaded);
+        this.outputCategoryList = store.select(outputCategorySelector.selectAllOutputCategories);
+        this.outputCategoryListIsLoading = store.select(outputCategorySelector.selectOutputCategoryListIsLoading);
+        this.outputCategoryListIsLoaded = store.select(outputCategorySelector.selectOutputCategoryListIsLoaded);
+        this.object = this.store.select(detailSelector.selectObject);
+        this.objectIsLoading = this.store.select(detailSelector.selectObjectIsLoading);
+        this.objectIsLoaded = this.store.select(detailSelector.selectObjectIsLoaded);
+        this.spectraCSV = this.store.select(detailSelector.selectSpectraCSV);
+        this.spectraIsLoading = this.store.select(detailSelector.selectObjectIsLoading);
+        this.spectraIsLoaded = this.store.select(detailSelector.selectSpectraIsLoaded);
+    }
+
+    ngOnInit() {
+        // Create a micro task that is processed after the current synchronous code
+        // This micro task prevent the expression has changed after view init error
+        Promise.resolve(null).then(() => this.store.dispatch(attributeActions.loadAttributeList()));
+        Promise.resolve(null).then(() => this.store.dispatch(outputFamilyActions.loadOutputFamilyList()));
+        Promise.resolve(null).then(() => this.store.dispatch(outputCategoryActions.loadOutputCategoryList()));
+        this.attributeListIsLoadedSubscription = this.attributeListIsLoaded.subscribe(attributeListIsLoaded => {
+            if (attributeListIsLoaded) {
+                Promise.resolve(null).then(() => this.store.dispatch(detailActions.retrieveObject()));
+            }
+        });
+    }
+
+    /**
+     * Returns the object type.
+     *
+     * @param  {Attribute[]} attributeList - The attribute list.
+     *
+     * @return string
+     */
+    getObjectType(attributeList: Attribute[]): string {
+        const spectrumAttribute: Attribute = attributeList
+            .filter(a => a.detail)
+            .find(attribute => attribute.search_flag === 'SPECTRUM_1D');
+        if (spectrumAttribute) {
+            return 'spectra';
+        }
+        return 'default';
+    }
+
+    /**
+     * Gets back to result page.
+     */
+    goBackToResult(): void {
+        this.location.back();
+    }
+
+    /**
+     * Dispatches action to retrieve spectra file.
+     *
+     * @param  {string} spectraFile - The spectra file name.
+     */
+    getSpectraCSV(spectraFile: string): void {
+        this.store.dispatch(detailActions.retrieveSpectra({ filename: spectraFile }));
+    }
+
+    /**
+     * Resets detail information.
+     */
+    ngOnDestroy() {
+        this.attributeListIsLoadedSubscription.unsubscribe();
+        // this.store.dispatch(new detailActions.DestroyDetailAction());
+    }
+}
diff --git a/client/src/app/instance/detail/detail-routing.module.ts b/client/src/app/instance/detail/detail-routing.module.ts
index ee1308922ba3f0b402a1a1bee41c26a5c08dce7e..8f88b99bb56b2ad21b228b818173bdb623ed7f6d 100644
--- a/client/src/app/instance/detail/detail-routing.module.ts
+++ b/client/src/app/instance/detail/detail-routing.module.ts
@@ -10,10 +10,11 @@
 import { NgModule } from '@angular/core';
 import { Routes, RouterModule } from '@angular/router';
 
-//import { DetailComponent } from './detail.component';
+import { DetailComponent } from './containers/detail.component';
 
 const routes: Routes = [
-    //{ path: '', component: DetailComponent }
+    { path: '', redirectTo: ':/dname/:id', pathMatch: 'full' },
+    { path: ':dname/:id', component: DetailComponent }
 ];
 
 @NgModule({
@@ -23,5 +24,5 @@ const routes: Routes = [
 export class DetailRoutingModule { }
 
 export const routedComponents = [
-    //DetailComponent
+    DetailComponent
 ];
diff --git a/client/src/app/instance/detail/detail.module.ts b/client/src/app/instance/detail/detail.module.ts
index 7e1605ceeda56e6d572a37d69b6fb11fb8e8bc7d..fbaaabfa3e090c12c51338d99b4ec7ebd6f32bdc 100644
--- a/client/src/app/instance/detail/detail.module.ts
+++ b/client/src/app/instance/detail/detail.module.ts
@@ -11,12 +11,16 @@ import { NgModule } from '@angular/core';
 
 import { SharedModule } from 'src/app/shared/shared.module';
 import { DetailRoutingModule, routedComponents } from './detail-routing.module';
+import { dummiesComponents } from './components';
 
 @NgModule({
     imports: [
         SharedModule,
         DetailRoutingModule
     ],
-    declarations: [routedComponents]
+    declarations: [
+        routedComponents,
+        dummiesComponents
+    ]
 })
 export class DetailModule { }
diff --git a/client/src/app/instance/instance-routing.module.ts b/client/src/app/instance/instance-routing.module.ts
index 37baa1075d4b111e51c541b0cad73beb09937cb2..f893d0721faffd7374ec7316e0030e4e5fbbb12c 100644
--- a/client/src/app/instance/instance-routing.module.ts
+++ b/client/src/app/instance/instance-routing.module.ts
@@ -18,7 +18,8 @@ const routes: Routes = [
             { path: '', redirectTo: 'home', pathMatch: 'full' },
             { path: 'home', loadChildren: () => import('./home/home.module').then(m => m.HomeModule) },
             { path: 'documentation', loadChildren: () => import('./documentation/documentation.module').then(m => m.DocumentationModule) },
-            { path: 'search', loadChildren: () => import('./search/search.module').then(m => m.SearchModule) }
+            { path: 'search', loadChildren: () => import('./search/search.module').then(m => m.SearchModule) },
+            { path: 'detail', loadChildren: () => import('./detail/detail.module').then(m => m.DetailModule) }
         ]
     }
 ];
diff --git a/client/src/app/instance/instance.reducer.ts b/client/src/app/instance/instance.reducer.ts
index f4454161d4f8074368ddc3d583a050798ea7abf4..f660ab9ab6c3dd59f99ddf24e047eac5cd2b82a7 100644
--- a/client/src/app/instance/instance.reducer.ts
+++ b/client/src/app/instance/instance.reducer.ts
@@ -14,19 +14,22 @@ import * as metamodel from './store/reducers/metamodel.reducer';
 import * as search from './store/reducers/search.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 {
     metamodel: metamodel.State,
     search: search.State,
     samp: samp.State,
     coneSearch: coneSearch.State
+    detail: detail.State
 }
 
 const reducers = {
     metamodel: metamodel.metamodelReducer,
     search: search.searchReducer,
     samp: samp.sampReducer,
-    coneSearch: coneSearch.coneSearchReducer
+    coneSearch: coneSearch.coneSearchReducer,
+    detail: detail.detailReducer
 };
 
 export const instanceReducer = combineReducers(reducers);
diff --git a/client/src/app/instance/search/components/result/datatable-tab.component.html b/client/src/app/instance/search/components/result/datatable-tab.component.html
index 66316d7af426d1a63ecfb6c0e2078d6b9d6d5605..26dd36d1be6a174d083b150ebb580194511e8e9c 100644
--- a/client/src/app/instance/search/components/result/datatable-tab.component.html
+++ b/client/src/app/instance/search/components/result/datatable-tab.component.html
@@ -10,6 +10,7 @@
         </button>
         <app-datatable
                 [dataset]="datasetList | datasetByName:datasetSelected"
+                [instanceSelected]="instanceSelected"
                 [attributeList]="attributeList"
                 [outputList]="outputList"
                 [dataLength]="dataLength"
diff --git a/client/src/app/instance/search/components/result/datatable-tab.component.ts b/client/src/app/instance/search/components/result/datatable-tab.component.ts
index c2d7dd1dd0ce224bd65ad333c910a84809bc0c1d..c24e1979d36d8710d5efe016bd9ba69a5e4c35d2 100644
--- a/client/src/app/instance/search/components/result/datatable-tab.component.ts
+++ b/client/src/app/instance/search/components/result/datatable-tab.component.ts
@@ -23,6 +23,7 @@ import { Pagination } from 'src/app/instance/store/models';
  */
 export class DatatableTabComponent {
     @Input() datasetSelected: string;
+    @Input() instanceSelected: string;
     @Input() datasetList: Dataset[];
     @Input() attributeList: Attribute[];
     @Input() outputList: number[];
diff --git a/client/src/app/instance/search/containers/result.component.html b/client/src/app/instance/search/containers/result.component.html
index ab045ae224c68d9ae47765843967539f45be1aa7..9e4fec28e05e52c08e8baab91789b911b6bbf5f6 100644
--- a/client/src/app/instance/search/containers/result.component.html
+++ b/client/src/app/instance/search/containers/result.component.html
@@ -64,6 +64,7 @@
             </app-cone-search-plot-tab> -->
             <app-datatable-tab
                 [datasetSelected]="datasetSelected | async"
+                [instanceSelected]="instanceSelected | async"
                 [datasetList]="datasetList | async"
                 [attributeList]="attributeList | async"
                 [outputList]="outputList | async"
diff --git a/client/src/app/instance/shared-search/components/datatable/datatable.component.html b/client/src/app/instance/shared-search/components/datatable/datatable.component.html
index 8a373adc94235177078fdbe92c629b1bb642024b..881f79065975906242993d75e2239f945bd4ff4c 100644
--- a/client/src/app/instance/shared-search/components/datatable/datatable.component.html
+++ b/client/src/app/instance/shared-search/components/datatable/datatable.component.html
@@ -45,6 +45,7 @@
                             <app-detail-renderer
                                 [value]="datum[attribute.label]"
                                 [datasetName]="dataset.name"
+                                [instanceSelected]="instanceSelected"
                                 [config]="getRendererConfig(attribute)">
                             </app-detail-renderer>
                         </div>
diff --git a/client/src/app/instance/shared-search/components/datatable/datatable.component.ts b/client/src/app/instance/shared-search/components/datatable/datatable.component.ts
index 56d115ee220a55a674fc8b3a6343871b3c6c8178..698cf0403bf1ea003acb77f3b9da144bb38132db 100644
--- a/client/src/app/instance/shared-search/components/datatable/datatable.component.ts
+++ b/client/src/app/instance/shared-search/components/datatable/datatable.component.ts
@@ -26,6 +26,7 @@ import { Pagination, PaginationOrder } from 'src/app/instance/store/models';
  */
 export class DatatableComponent implements OnInit {
     @Input() dataset: Dataset;
+    @Input() instanceSelected: string;
     @Input() attributeList: Attribute[];
     @Input() outputList: number[];
     @Input() dataLength: number;
diff --git a/client/src/app/instance/shared-search/components/datatable/renderer/detail-renderer.component.html b/client/src/app/instance/shared-search/components/datatable/renderer/detail-renderer.component.html
index 0b6336b1e1acb9cf348dc857b43bd2fedc62ddc2..37c9f3133e39d52496faf4585956836050038ccc 100644
--- a/client/src/app/instance/shared-search/components/datatable/renderer/detail-renderer.component.html
+++ b/client/src/app/instance/shared-search/components/datatable/renderer/detail-renderer.component.html
@@ -1,4 +1,4 @@
-<a routerLink="/detail/{{ datasetName }}/{{ value }}" [target]="config.blank == true ? '_blank' : '_self'"
+<a routerLink="/instance/{{ instanceSelected }}/detail/{{ datasetName }}/{{ value }}" [target]="config.blank == true ? '_blank' : '_self'"
     [ngClass]="{'btn btn-outline-primary btn-sm' : config.display == 'text-button'}">
     {{ value }}
 </a>
diff --git a/client/src/app/instance/shared-search/components/datatable/renderer/detail-renderer.component.ts b/client/src/app/instance/shared-search/components/datatable/renderer/detail-renderer.component.ts
index 06411ef9ec5a95251f5cc49da809f0490eb95839..d6df4dfd8384e2931696c6807a5a0c97bfdeaefb 100644
--- a/client/src/app/instance/shared-search/components/datatable/renderer/detail-renderer.component.ts
+++ b/client/src/app/instance/shared-search/components/datatable/renderer/detail-renderer.component.ts
@@ -23,5 +23,6 @@ import { DetailRendererConfig } from 'src/app/metamodel/models/renderers/detail-
 export class DetailRendererComponent {
     @Input() value: string | number;
     @Input() datasetName: string;
+    @Input() instanceSelected: string;
     @Input() config: DetailRendererConfig;
 }
diff --git a/client/src/app/instance/store/actions/detail.actions.ts b/client/src/app/instance/store/actions/detail.actions.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c88210bc8e4e24fb0ee165d34c38c07a90792322
--- /dev/null
+++ b/client/src/app/instance/store/actions/detail.actions.ts
@@ -0,0 +1,17 @@
+/**
+ * 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';
+
+export const retrieveObject = createAction('[Detail] Retrieve Object');
+export const retrieveObjectSuccess = createAction('[Detail] Retrieve Object Success', props<{ object: any }>());
+export const retrieveObjectFail = createAction('[Detail] Retrieve Object Fail');
+export const retrieveSpectra = createAction('[Detail] Retrieve Spectra', props<{ filename: string }>());
+export const retrieveSpectraSuccess = createAction('[Detail] Retrieve Spectra Success', props<{ spectraCSV: string }>());
+export const retrieveSpectraFail = createAction('[Detail] Retrieve Spectra Fail');
diff --git a/client/src/app/instance/store/effects/detail.effects.ts b/client/src/app/instance/store/effects/detail.effects.ts
new file mode 100644
index 0000000000000000000000000000000000000000..256412aea966b727115af6394a73795b181e22e7
--- /dev/null
+++ b/client/src/app/instance/store/effects/detail.effects.ts
@@ -0,0 +1,80 @@
+/**
+ * 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  } from '@ngrx/store';
+import { of } from 'rxjs';
+import { map, tap, mergeMap, catchError } from 'rxjs/operators';
+import { ToastrService } from 'ngx-toastr';
+
+import { DetailService } from '../services/detail.service';
+import * as detailActions from '../actions/detail.actions';
+import * as detailSelector from '../selectors/detail.selector';
+import * as attributeSelector from 'src/app/metamodel/selectors/attribute.selector';
+import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector';
+ 
+@Injectable()
+export class DetailEffects {
+    retrieveObject$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(detailActions.retrieveObject),
+            concatLatestFrom(() => [
+                this.store.select(datasetSelector.selectDatasetNameByRoute),
+                this.store.select(attributeSelector.selectAllAttributes),
+                this.store.select(detailSelector.selectIdByRoute)
+            ]),
+            mergeMap(([action, datasetName, attributeList, id]) => this.detailService.retrieveObject(
+                datasetName,
+                attributeList.find(attribute => attribute.order_by).id,
+                id,
+                attributeList.filter(attribute => attribute.detail).map(attribute => attribute.id)
+            ).pipe(
+                map(object => detailActions.retrieveObjectSuccess({ object: object[0] })),
+                catchError(() => of(detailActions.retrieveObjectFail()))
+            ))
+        )
+    );
+
+    retrieveObjectFail$ = createEffect(() => 
+        this.actions$.pipe(
+            ofType(detailActions.retrieveObjectFail),
+            tap(() => this.toastr.error('Loading Failed!', 'Unable to load the object'))
+        ), { dispatch: false}
+    );
+
+    retrieveSpectra$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(detailActions.retrieveSpectra),
+            concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)),
+            mergeMap(([action, datasetName]) => this.detailService.retrieveSpectra(
+                datasetName,
+                action.filename
+            ).pipe(
+                map(spectraCSV => detailActions.retrieveSpectraSuccess({ spectraCSV })),
+                catchError(() => of(detailActions.retrieveSpectraFail()))
+            ))
+        )
+    );
+
+    retrieveSpectraFail$ = createEffect(() => 
+        this.actions$.pipe(
+            ofType(detailActions.retrieveSpectraFail),
+            tap(() => this.toastr.error('Loading Failed!', 'Unable to load spectra'))
+        ), { dispatch: false}
+    );
+    
+    constructor(
+        private actions$: Actions,
+        private detailService: DetailService,
+        private store: Store<{ }>,
+        private toastr: ToastrService
+    ) {}
+}
diff --git a/client/src/app/instance/store/effects/index.ts b/client/src/app/instance/store/effects/index.ts
index 3d2ecd77ad3f9e6f15ba5a0e1938aac5679cb49d..29c6402a3c50c5a7138b9d239ae5723e6421e3ec 100644
--- a/client/src/app/instance/store/effects/index.ts
+++ b/client/src/app/instance/store/effects/index.ts
@@ -2,10 +2,12 @@ import { MetamodelEffects } from './metamodel.effects';
 import { SampEffects } from './samp.effects';
 import { SearchEffects } from './search.effects';
 import { ConeSearchEffects } from './cone-search.effects';
+import { DetailEffects } from './detail.effects';
 
 export const instanceEffects = [
     MetamodelEffects,
     SampEffects,
     SearchEffects,
-    ConeSearchEffects
+    ConeSearchEffects,
+    DetailEffects
 ];
diff --git a/client/src/app/instance/store/reducers/detail.reducer.ts b/client/src/app/instance/store/reducers/detail.reducer.ts
new file mode 100644
index 0000000000000000000000000000000000000000..466fcfc1838c6e79d790cf87fafb944b96081189
--- /dev/null
+++ b/client/src/app/instance/store/reducers/detail.reducer.ts
@@ -0,0 +1,71 @@
+/**
+ * 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 * as detailActions from '../actions/detail.actions';
+
+export interface State {
+    object: any;
+    objectIsLoading: boolean;
+    objectIsLoaded: boolean;
+    spectraCSV: string;
+    spectraIsLoading: boolean;
+    spectraIsLoaded: boolean;
+}
+
+export const initialState: State = {
+    object: null,
+    objectIsLoading: false,
+    objectIsLoaded: false,
+    spectraCSV: null,
+    spectraIsLoading: false,
+    spectraIsLoaded: false,
+};
+
+export const detailReducer = createReducer(
+    initialState,
+    on(detailActions.retrieveObject, state => ({
+        ...state,
+        objectIsLoading: true,
+        objectIsLoaded: false
+    })),
+    on(detailActions.retrieveObjectSuccess, (state, { object }) => ({
+        ...state,
+        objectIsLoading: false,
+        objectIsLoaded: true,
+        object
+    })),
+    on(detailActions.retrieveObjectFail, state => ({
+        ...state,
+        objectIsLoading: false
+    })),
+    on(detailActions.retrieveSpectra, state => ({
+        ...state,
+        spectraIsLoading: true,
+        spectraIsLoaded: false
+    })),
+    on(detailActions.retrieveSpectraSuccess, (state, { spectraCSV }) => ({
+        ...state,
+        spectraIsLoading: false,
+        spectraIsLoaded: true,
+        spectraCSV
+    })),
+    on(detailActions.retrieveSpectraFail, state => ({
+        ...state,
+        spectraIsLoading: false
+    }))
+);
+
+export const selectObject = (state: State) => state.object;
+export const selectObjectIsLoading = (state: State) => state.objectIsLoading;
+export const selectObjectIsLoaded = (state: State) => state.objectIsLoaded;
+export const selectSpectraCSV = (state: State) => state.spectraCSV;
+export const selectSpectraIsLoading = (state: State) => state.spectraIsLoading;
+export const selectSpectraIsLoaded = (state: State) => state.spectraIsLoaded;
diff --git a/client/src/app/instance/store/selectors/detail.selector.ts b/client/src/app/instance/store/selectors/detail.selector.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2fe62d5722257342c255c5b4680e6883ecad19f5
--- /dev/null
+++ b/client/src/app/instance/store/selectors/detail.selector.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 { createSelector } from '@ngrx/store';
+
+import * as reducer from '../../instance.reducer';
+import * as fromDetail from '../reducers/detail.reducer';
+
+export const selectDetailState = createSelector(
+    reducer.getInstanceState,
+    (state: reducer.State) => state.detail
+);
+
+export const selectObject = createSelector(
+    selectDetailState,
+    fromDetail.selectObject
+);
+
+export const selectObjectIsLoading = createSelector(
+    selectDetailState,
+    fromDetail.selectObjectIsLoading
+);
+
+export const selectObjectIsLoaded = createSelector(
+    selectDetailState,
+    fromDetail.selectObjectIsLoaded
+);
+
+export const selectSpectraCSV = createSelector(
+    selectDetailState,
+    fromDetail.selectSpectraCSV
+);
+
+export const selectSpectraIsLoading = createSelector(
+    selectDetailState,
+    fromDetail.selectSpectraIsLoading
+);
+
+export const selectSpectraIsLoaded = createSelector(
+    selectDetailState,
+    fromDetail.selectSpectraIsLoaded
+);
+
+export const selectIdByRoute = createSelector(
+    reducer.selectRouterState,
+    router => router.state.params.id
+);
diff --git a/client/src/app/instance/store/selectors/search.selector.ts b/client/src/app/instance/store/selectors/search.selector.ts
index 094c36733e1f0aed698df8503b56aaf91977b642..44ebe388b9cde19e8bbfe3eff35a5bb9e7b68ecd 100644
--- a/client/src/app/instance/store/selectors/search.selector.ts
+++ b/client/src/app/instance/store/selectors/search.selector.ts
@@ -14,88 +14,88 @@ import * as reducer from '../../instance.reducer';
 import * as fromSearch from '../reducers/search.reducer';
 import * as coneSearchSelector from './cone-search.selector';
 
-export const selectInstanceState = createSelector(
+export const selectSearchState = createSelector(
     reducer.getInstanceState,
     (state: reducer.State) => state.search
 );
 
 export const selectPristine = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectPristine
 );
 
 export const selectCurrentDataset = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectCurrentDataset
 );
 
 export const selectCurrentStep = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectCurrentStep
 );
 
 export const selectCriteriaStepChecked = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectCriteriaStepChecked
 );
 
 export const selectOutputStepChecked = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectOutputStepChecked
 );
 
 export const selectResultStepChecked = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectResultStepChecked
 );
 
 export const selectIsConeSearchAdded = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectIsConeSearchAdded
 );
 
 export const selectCriteriaList = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectCriteriaList
 );
 
 export const selectOutputList = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectOutputList
 );
 
 export const selectDataLengthIsLoading = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectDataLengthIsLoading
 );
 
 export const selectDataLengthIsLoaded = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectDataLengthIsLoaded
 );
 
 export const selectDataLength = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectDataLength
 );
 
 export const selectDataIsLoading = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectDataIsLoading
 );
 
 export const selectDataIsLoaded = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectDataIsLoaded
 );
 
 export const selectData = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectData
 );
 
 export const selectSelectedData = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectSelectedData
 );
 
diff --git a/client/src/app/instance/store/services/detail.service.ts b/client/src/app/instance/store/services/detail.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e1b5db35066ac3cdcf46e331755d201835014ab4
--- /dev/null
+++ b/client/src/app/instance/store/services/detail.service.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 { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+
+import { Observable } from 'rxjs';
+
+import { environment } from 'src/environments/environment';
+
+@Injectable()
+/**
+ * @class
+ * @classdesc Detail service.
+ */
+export class DetailService {
+    private API_PATH: string = environment.apiUrl + '/search/';
+    private SERVICES_PATH: string = environment.servicesUrl;
+
+    constructor(private http: HttpClient) { }
+
+    /**
+     * Retrieves object details for the given parameters.
+     *
+     * @param  {string} dname - The dataset name.
+     * @param  {number} criterionId - The criterion ID.
+     * @param  {string} objectSelected - The selected object ID.
+     * @param  {number[]} outputList - The output list.
+     *
+     * @return Observable<any[]>
+     */
+    retrieveObject(dname: string, criterionId: number, objectSelected: string, outputList: number[]): Observable<any[]> {
+        const query = dname + '?c=' + criterionId + '::eq::' + objectSelected + '&a=' + outputList.join(';');
+        return this.http.get<any[]>(this.API_PATH + query);
+    }
+
+    /**
+     * Retrieves object details for the given parameters.
+     *
+     * @param  {string} spectraFile - The spectra file name.
+     *
+     * @return Observable<string>
+     */
+    retrieveSpectra(dname: string, spectraFile: string): Observable<string> {
+        return this.http.get(this.SERVICES_PATH + '/spectra-to-csv/' + dname + '?filename=' + spectraFile, { responseType: 'text' });
+    }
+}
diff --git a/client/src/app/instance/store/services/index.ts b/client/src/app/instance/store/services/index.ts
index 4707c60a165f5e0b28a886c148854579537703d5..cabc079690c1bfea8a44fddb9baa7123796937b2 100644
--- a/client/src/app/instance/store/services/index.ts
+++ b/client/src/app/instance/store/services/index.ts
@@ -1,9 +1,11 @@
 import { SearchService } from './search.service';
 import { SampService } from './samp.service';
 import { ConeSearchService } from './cone-search.service';
+import { DetailService } from './detail.service';
 
 export const instanceServices = [
     SearchService,
     SampService,
-    ConeSearchService
+    ConeSearchService,
+    DetailService
 ];
diff --git a/client/yarn.lock b/client/yarn.lock
index 2a005f9358ac87f05bedd31cb41ab6f7bf727db2..374022b1be318ba6228961cb9dc4dcfcd78a8d14 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -1344,6 +1344,222 @@
   resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.10.tgz#61cc8469849e5bcdd0c7044122265c39cec10cf4"
   integrity sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==
 
+"@types/d3-array@^1":
+  version "1.2.9"
+  resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-1.2.9.tgz#c7dc78992cd8ca5c850243a265fd257ea56df1fa"
+  integrity sha512-E/7RgPr2ylT5dWG0CswMi9NpFcjIEDqLcUSBgNHe/EMahfqYaTx4zhcggG3khqoEB/leY4Vl6nTSbwLUPjXceA==
+
+"@types/d3-axis@^1":
+  version "1.0.16"
+  resolved "https://registry.yarnpkg.com/@types/d3-axis/-/d3-axis-1.0.16.tgz#93d7a28795c2f8b0e2fd550fcc4d29b7f174e693"
+  integrity sha512-p7085weOmo4W+DzlRRVC/7OI/jugaKbVa6WMQGCQscaMylcbuaVEGk7abJLNyGVFLeCBNrHTdDiqRGnzvL0nXQ==
+  dependencies:
+    "@types/d3-selection" "^1"
+
+"@types/d3-brush@^1":
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/@types/d3-brush/-/d3-brush-1.1.5.tgz#c7cfb58decbfd53ad3e47f0376345e3640a68186"
+  integrity sha512-4zGkBafJf5zCsBtLtvDj/pNMo5X9+Ii/1hUz0GvQ+wEwelUBm2AbIDAzJnp2hLDFF307o0fhxmmocHclhXC+tw==
+  dependencies:
+    "@types/d3-selection" "^1"
+
+"@types/d3-chord@^1":
+  version "1.0.11"
+  resolved "https://registry.yarnpkg.com/@types/d3-chord/-/d3-chord-1.0.11.tgz#5760765db1b1a4b936c0d9355a821dde9dd25da2"
+  integrity sha512-0DdfJ//bxyW3G9Nefwq/LDgazSKNN8NU0lBT3Cza6uVuInC2awMNsAcv1oKyRFLn9z7kXClH5XjwpveZjuz2eg==
+
+"@types/d3-collection@*":
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/@types/d3-collection/-/d3-collection-1.0.10.tgz#bca161e336156968f267c077f7f2bfa8ff224e58"
+  integrity sha512-54Fdv8u5JbuXymtmXm2SYzi1x/Svt+jfWBU5junkhrCewL92VjqtCBDn97coBRVwVFmYNnVTNDyV8gQyPYfm+A==
+
+"@types/d3-color@^1":
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-1.4.2.tgz#944f281d04a0f06e134ea96adbb68303515b2784"
+  integrity sha512-fYtiVLBYy7VQX+Kx7wU/uOIkGQn8aAEY8oWMoyja3N4dLd8Yf6XgSIR/4yWvMuveNOH5VShnqCgRqqh/UNanBA==
+
+"@types/d3-contour@^1":
+  version "1.3.3"
+  resolved "https://registry.yarnpkg.com/@types/d3-contour/-/d3-contour-1.3.3.tgz#44529d498bbc1db78b195d75e1c9bb889edd647a"
+  integrity sha512-LxwmGIfVJIc1cKs7ZFRQ1FbtXpfH7QTXYRdMIJsFP71uCMdF6jJ0XZakYDX6Hn4yZkLf+7V8FgD34yCcok+5Ww==
+  dependencies:
+    "@types/d3-array" "^1"
+    "@types/geojson" "*"
+
+"@types/d3-dispatch@^1":
+  version "1.0.9"
+  resolved "https://registry.yarnpkg.com/@types/d3-dispatch/-/d3-dispatch-1.0.9.tgz#c5a180f1e251de853b399cfbfbb6dd7f8bf842ae"
+  integrity sha512-zJ44YgjqALmyps+II7b1mZLhrtfV/FOxw9owT87mrweGWcg+WK5oiJX2M3SYJ0XUAExBduarysfgbR11YxzojQ==
+
+"@types/d3-drag@^1":
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/@types/d3-drag/-/d3-drag-1.2.5.tgz#0b1b852cb41577075aa625ae6149379ea6c34dfd"
+  integrity sha512-7NeTnfolst1Js3Vs7myctBkmJWu6DMI3k597AaHUX98saHjHWJ6vouT83UrpE+xfbSceHV+8A0JgxuwgqgmqWw==
+  dependencies:
+    "@types/d3-selection" "^1"
+
+"@types/d3-dsv@^1":
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/@types/d3-dsv/-/d3-dsv-1.2.1.tgz#1524fee9f19d689c2f76aa0e24e230762bf96994"
+  integrity sha512-LLmJmjiqp/fTNEdij5bIwUJ6P6TVNk5hKM9/uk5RPO2YNgEu9XvKO0dJ7Iqd3psEdmZN1m7gB1bOsjr4HmO2BA==
+
+"@types/d3-ease@^1":
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/@types/d3-ease/-/d3-ease-1.0.10.tgz#09910e8558439b6038a7ed620650e510394ffa6d"
+  integrity sha512-fMFTCzd8DOwruE9zlu2O8ci5ct+U5jkGcDS+cH+HCidnJlDs0MZ+TuSVCFtEzh4E5MasItwy+HvgoFtxPHa5Cw==
+
+"@types/d3-fetch@^1":
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/@types/d3-fetch/-/d3-fetch-1.2.2.tgz#b93bfe248b8b761af82f4dac57959c989f67da3e"
+  integrity sha512-rtFs92GugtV/NpiJQd0WsmGLcg52tIL0uF0bKbbJg231pR9JEb6HT4AUwrtuLq3lOeKdLBhsjV14qb0pMmd0Aw==
+  dependencies:
+    "@types/d3-dsv" "^1"
+
+"@types/d3-force@^1":
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/@types/d3-force/-/d3-force-1.2.4.tgz#6e274c72288c2db08fbdb8f5b87b9aa83e55a9e8"
+  integrity sha512-fkorLTKvt6AQbFBQwn4aq7h9rJ4c7ZVcPMGB8X6eFFveAyMZcv7t7m6wgF4Eg93rkPgPORU7sAho1QSHNcZu6w==
+
+"@types/d3-format@^1":
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/@types/d3-format/-/d3-format-1.4.2.tgz#ea17bf559b71d9afd569ae9bfe4c544dab863baa"
+  integrity sha512-WeGCHAs7PHdZYq6lwl/+jsl+Nfc1J2W1kNcMeIMYzQsT6mtBDBgtJ/rcdjZ0k0rVIvqEZqhhuD5TK/v3P2gFHQ==
+
+"@types/d3-geo@^1":
+  version "1.12.3"
+  resolved "https://registry.yarnpkg.com/@types/d3-geo/-/d3-geo-1.12.3.tgz#512ebe735cb1cdf5f87ad59608416e2e9e868c5a"
+  integrity sha512-yZbPb7/5DyL/pXkeOmZ7L5ySpuGr4H48t1cuALjnJy5sXQqmSSAYBiwa6Ya/XpWKX2rJqGDDubmh3nOaopOpeA==
+  dependencies:
+    "@types/geojson" "*"
+
+"@types/d3-hierarchy@^1":
+  version "1.1.8"
+  resolved "https://registry.yarnpkg.com/@types/d3-hierarchy/-/d3-hierarchy-1.1.8.tgz#50657f420d565a06c0b950a4b82eee0a369f2dea"
+  integrity sha512-AbStKxNyWiMDQPGDguG2Kuhlq1Sv539pZSxYbx4UZeYkutpPwXCcgyiRrlV4YH64nIOsKx7XVnOMy9O7rJsXkg==
+
+"@types/d3-interpolate@^1":
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-1.4.2.tgz#88902a205f682773a517612299a44699285eed7b"
+  integrity sha512-ylycts6llFf8yAEs1tXzx2loxxzDZHseuhPokrqKprTQSTcD3JbJI1omZP1rphsELZO3Q+of3ff0ZS7+O6yVzg==
+  dependencies:
+    "@types/d3-color" "^1"
+
+"@types/d3-path@^1":
+  version "1.0.9"
+  resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-1.0.9.tgz#73526b150d14cd96e701597cbf346cfd1fd4a58c"
+  integrity sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ==
+
+"@types/d3-polygon@^1":
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/@types/d3-polygon/-/d3-polygon-1.0.8.tgz#127ee83fccda5bf57384011da90f31367fea1530"
+  integrity sha512-1TOJPXCBJC9V3+K3tGbTqD/CsqLyv/YkTXAcwdsZzxqw5cvpdnCuDl42M4Dvi8XzMxZNCT9pL4ibrK2n4VmAcw==
+
+"@types/d3-quadtree@^1":
+  version "1.0.9"
+  resolved "https://registry.yarnpkg.com/@types/d3-quadtree/-/d3-quadtree-1.0.9.tgz#c7c3b795b5af06e5b043d1d34e754a434b3bae59"
+  integrity sha512-5E0OJJn2QVavITFEc1AQlI8gLcIoDZcTKOD3feKFckQVmFV4CXhqRFt83tYNVNIN4ZzRkjlAMavJa1ldMhf5rA==
+
+"@types/d3-random@^1":
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/@types/d3-random/-/d3-random-1.1.3.tgz#8f7fdc23f92d1561e0694eb49567e8ab50537a19"
+  integrity sha512-XXR+ZbFCoOd4peXSMYJzwk0/elP37WWAzS/DG+90eilzVbUSsgKhBcWqylGWe+lA2ubgr7afWAOBaBxRgMUrBQ==
+
+"@types/d3-scale-chromatic@^1":
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/@types/d3-scale-chromatic/-/d3-scale-chromatic-1.5.1.tgz#e2b7c3401e5c13809f831911eb820e444f4fc67a"
+  integrity sha512-7FtJYrmXTEWLykShjYhoGuDNR/Bda0+tstZMkFj4RRxUEryv16AGh3be21tqg84B6KfEwiZyEpBcTyPyU+GWjg==
+
+"@types/d3-scale@^2":
+  version "2.2.6"
+  resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-2.2.6.tgz#28540b4dfc99d978970e873e4138a6bea2ea6ab8"
+  integrity sha512-CHu34T5bGrJOeuhGxyiz9Xvaa9PlsIaQoOqjDg7zqeGj2x0rwPhGquiy03unigvcMxmvY0hEaAouT0LOFTLpIw==
+  dependencies:
+    "@types/d3-time" "^1"
+
+"@types/d3-selection@^1":
+  version "1.4.3"
+  resolved "https://registry.yarnpkg.com/@types/d3-selection/-/d3-selection-1.4.3.tgz#36928bbe64eb8e0bbcbaa01fb05c21ff6c71fa93"
+  integrity sha512-GjKQWVZO6Sa96HiKO6R93VBE8DUW+DDkFpIMf9vpY5S78qZTlRRSNUsHr/afDpF7TvLDV7VxrUFOWW7vdIlYkA==
+
+"@types/d3-shape@^1":
+  version "1.3.8"
+  resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-1.3.8.tgz#c3c15ec7436b4ce24e38de517586850f1fea8e89"
+  integrity sha512-gqfnMz6Fd5H6GOLYixOZP/xlrMtJms9BaS+6oWxTKHNqPGZ93BkWWupQSCYm6YHqx6h9wjRupuJb90bun6ZaYg==
+  dependencies:
+    "@types/d3-path" "^1"
+
+"@types/d3-time-format@^2":
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-2.3.1.tgz#87a30e4513b9d1d53b920327a361f87255bf3372"
+  integrity sha512-fck0Z9RGfIQn3GJIEKVrp15h9m6Vlg0d5XXeiE/6+CQiBmMDZxfR21XtjEPuDeg7gC3bBM0SdieA5XF3GW1wKA==
+
+"@types/d3-time@^1":
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-1.1.1.tgz#6cf3a4242c3bbac00440dfb8ba7884f16bedfcbf"
+  integrity sha512-ULX7LoqXTCYtM+tLYOaeAJK7IwCT+4Gxlm2MaH0ErKLi07R5lh8NHCAyWcDkCCmx1AfRcBEV6H9QE9R25uP7jw==
+
+"@types/d3-timer@^1":
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/@types/d3-timer/-/d3-timer-1.0.10.tgz#329c51c2c931f44ed0acff78b8c84571acf0ed21"
+  integrity sha512-ZnAbquVqy+4ZjdW0cY6URp+qF/AzTVNda2jYyOzpR2cPT35FTXl78s15Bomph9+ckOiI1TtkljnWkwbIGAb6rg==
+
+"@types/d3-transition@^1":
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/@types/d3-transition/-/d3-transition-1.3.2.tgz#ed59beca7b4d679cfa52f88a6a50e5bbeb7e0a3c"
+  integrity sha512-J+a3SuF/E7wXbOSN19p8ZieQSFIm5hU2Egqtndbc54LXaAEOpLfDx4sBu/PKAKzHOdgKK1wkMhINKqNh4aoZAg==
+  dependencies:
+    "@types/d3-selection" "^1"
+
+"@types/d3-voronoi@*":
+  version "1.1.9"
+  resolved "https://registry.yarnpkg.com/@types/d3-voronoi/-/d3-voronoi-1.1.9.tgz#7bbc210818a3a5c5e0bafb051420df206617c9e5"
+  integrity sha512-DExNQkaHd1F3dFPvGA/Aw2NGyjMln6E9QzsiqOcBgnE+VInYnFBHBBySbZQts6z6xD+5jTfKCP7M4OqMyVjdwQ==
+
+"@types/d3-zoom@^1":
+  version "1.8.3"
+  resolved "https://registry.yarnpkg.com/@types/d3-zoom/-/d3-zoom-1.8.3.tgz#00237900c6fdc2bb4fe82679ee4d74eb8fbe7b3c"
+  integrity sha512-3kHkL6sPiDdbfGhzlp5gIHyu3kULhtnHTTAl3UBZVtWB1PzcLL8vdmz5mTx7plLiUqOA2Y+yT2GKjt/TdA2p7Q==
+  dependencies:
+    "@types/d3-interpolate" "^1"
+    "@types/d3-selection" "^1"
+
+"@types/d3@^5.7.2":
+  version "5.16.4"
+  resolved "https://registry.yarnpkg.com/@types/d3/-/d3-5.16.4.tgz#a7dc24a3dc1c19922eee72ba16144fd5bcea987a"
+  integrity sha512-2u0O9iP1MubFiQ+AhR1id4Egs+07BLtvRATG6IL2Gs9+KzdrfaxCKNq5hxEyw1kxwsqB/lCgr108XuHcKtb/5w==
+  dependencies:
+    "@types/d3-array" "^1"
+    "@types/d3-axis" "^1"
+    "@types/d3-brush" "^1"
+    "@types/d3-chord" "^1"
+    "@types/d3-collection" "*"
+    "@types/d3-color" "^1"
+    "@types/d3-contour" "^1"
+    "@types/d3-dispatch" "^1"
+    "@types/d3-drag" "^1"
+    "@types/d3-dsv" "^1"
+    "@types/d3-ease" "^1"
+    "@types/d3-fetch" "^1"
+    "@types/d3-force" "^1"
+    "@types/d3-format" "^1"
+    "@types/d3-geo" "^1"
+    "@types/d3-hierarchy" "^1"
+    "@types/d3-interpolate" "^1"
+    "@types/d3-path" "^1"
+    "@types/d3-polygon" "^1"
+    "@types/d3-quadtree" "^1"
+    "@types/d3-random" "^1"
+    "@types/d3-scale" "^2"
+    "@types/d3-scale-chromatic" "^1"
+    "@types/d3-selection" "^1"
+    "@types/d3-shape" "^1"
+    "@types/d3-time" "^1"
+    "@types/d3-time-format" "^2"
+    "@types/d3-timer" "^1"
+    "@types/d3-transition" "^1"
+    "@types/d3-voronoi" "*"
+    "@types/d3-zoom" "^1"
+
 "@types/eslint-scope@^3.7.0":
   version "3.7.0"
   resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.0.tgz#4792816e31119ebd506902a482caec4951fabd86"
@@ -1370,6 +1586,11 @@
   resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.47.tgz#d7a51db20f0650efec24cd04994f523d93172ed4"
   integrity sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg==
 
+"@types/geojson@*":
+  version "7946.0.8"
+  resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.8.tgz#30744afdb385e2945e22f3b033f897f76b1f12ca"
+  integrity sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==
+
 "@types/glob@^7.1.1":
   version "7.1.3"
   resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183"
@@ -2402,7 +2623,7 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
   dependencies:
     delayed-stream "~1.0.0"
 
-commander@^2.20.0:
+commander@2, commander@^2.20.0:
   version "2.20.3"
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
   integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
@@ -2780,6 +3001,254 @@ custom-event@~1.0.0:
   resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425"
   integrity sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=
 
+d3-array@1, d3-array@^1.1.1, d3-array@^1.2.0:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f"
+  integrity sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==
+
+d3-axis@1:
+  version "1.0.12"
+  resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-1.0.12.tgz#cdf20ba210cfbb43795af33756886fb3638daac9"
+  integrity sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ==
+
+d3-brush@1:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-1.1.6.tgz#b0a22c7372cabec128bdddf9bddc058592f89e9b"
+  integrity sha512-7RW+w7HfMCPyZLifTz/UnJmI5kdkXtpCbombUSs8xniAyo0vIbrDzDwUJB6eJOgl9u5DQOt2TQlYumxzD1SvYA==
+  dependencies:
+    d3-dispatch "1"
+    d3-drag "1"
+    d3-interpolate "1"
+    d3-selection "1"
+    d3-transition "1"
+
+d3-chord@1:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-1.0.6.tgz#309157e3f2db2c752f0280fedd35f2067ccbb15f"
+  integrity sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==
+  dependencies:
+    d3-array "1"
+    d3-path "1"
+
+d3-collection@1:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.7.tgz#349bd2aa9977db071091c13144d5e4f16b5b310e"
+  integrity sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==
+
+d3-color@1:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.4.1.tgz#c52002bf8846ada4424d55d97982fef26eb3bc8a"
+  integrity sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==
+
+d3-contour@1:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/d3-contour/-/d3-contour-1.3.2.tgz#652aacd500d2264cb3423cee10db69f6f59bead3"
+  integrity sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==
+  dependencies:
+    d3-array "^1.1.1"
+
+d3-dispatch@1:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.6.tgz#00d37bcee4dd8cd97729dd893a0ac29caaba5d58"
+  integrity sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==
+
+d3-drag@1:
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-1.2.5.tgz#2537f451acd39d31406677b7dc77c82f7d988f70"
+  integrity sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w==
+  dependencies:
+    d3-dispatch "1"
+    d3-selection "1"
+
+d3-dsv@1:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-1.2.0.tgz#9d5f75c3a5f8abd611f74d3f5847b0d4338b885c"
+  integrity sha512-9yVlqvZcSOMhCYzniHE7EVUws7Fa1zgw+/EAV2BxJoG3ME19V6BQFBwI855XQDsxyOuG7NibqRMTtiF/Qup46g==
+  dependencies:
+    commander "2"
+    iconv-lite "0.4"
+    rw "1"
+
+d3-ease@1:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.7.tgz#9a834890ef8b8ae8c558b2fe55bd57f5993b85e2"
+  integrity sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==
+
+d3-fetch@1:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/d3-fetch/-/d3-fetch-1.2.0.tgz#15ce2ecfc41b092b1db50abd2c552c2316cf7fc7"
+  integrity sha512-yC78NBVcd2zFAyR/HnUiBS7Lf6inSCoWcSxFfw8FYL7ydiqe80SazNwoffcqOfs95XaLo7yebsmQqDKSsXUtvA==
+  dependencies:
+    d3-dsv "1"
+
+d3-force@1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-1.2.1.tgz#fd29a5d1ff181c9e7f0669e4bd72bdb0e914ec0b"
+  integrity sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==
+  dependencies:
+    d3-collection "1"
+    d3-dispatch "1"
+    d3-quadtree "1"
+    d3-timer "1"
+
+d3-format@1:
+  version "1.4.5"
+  resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.4.5.tgz#374f2ba1320e3717eb74a9356c67daee17a7edb4"
+  integrity sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==
+
+d3-geo@1:
+  version "1.12.1"
+  resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.12.1.tgz#7fc2ab7414b72e59fbcbd603e80d9adc029b035f"
+  integrity sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg==
+  dependencies:
+    d3-array "1"
+
+d3-hierarchy@1:
+  version "1.1.9"
+  resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz#2f6bee24caaea43f8dc37545fa01628559647a83"
+  integrity sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==
+
+d3-interpolate@1:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.4.0.tgz#526e79e2d80daa383f9e0c1c1c7dcc0f0583e987"
+  integrity sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==
+  dependencies:
+    d3-color "1"
+
+d3-path@1:
+  version "1.0.9"
+  resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.9.tgz#48c050bb1fe8c262493a8caf5524e3e9591701cf"
+  integrity sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==
+
+d3-polygon@1:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-1.0.6.tgz#0bf8cb8180a6dc107f518ddf7975e12abbfbd38e"
+  integrity sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ==
+
+d3-quadtree@1:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-1.0.7.tgz#ca8b84df7bb53763fe3c2f24bd435137f4e53135"
+  integrity sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA==
+
+d3-random@1:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-1.1.2.tgz#2833be7c124360bf9e2d3fd4f33847cfe6cab291"
+  integrity sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ==
+
+d3-scale-chromatic@1:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz#54e333fc78212f439b14641fb55801dd81135a98"
+  integrity sha512-ACcL46DYImpRFMBcpk9HhtIyC7bTBR4fNOPxwVSl0LfulDAwyiHyPOTqcDG1+t5d4P9W7t/2NAuWu59aKko/cg==
+  dependencies:
+    d3-color "1"
+    d3-interpolate "1"
+
+d3-scale@2:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-2.2.2.tgz#4e880e0b2745acaaddd3ede26a9e908a9e17b81f"
+  integrity sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==
+  dependencies:
+    d3-array "^1.2.0"
+    d3-collection "1"
+    d3-format "1"
+    d3-interpolate "1"
+    d3-time "1"
+    d3-time-format "2"
+
+d3-selection@1, d3-selection@^1.1.0:
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.4.2.tgz#dcaa49522c0dbf32d6c1858afc26b6094555bc5c"
+  integrity sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==
+
+d3-shape@1:
+  version "1.3.7"
+  resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.7.tgz#df63801be07bc986bc54f63789b4fe502992b5d7"
+  integrity sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==
+  dependencies:
+    d3-path "1"
+
+d3-time-format@2:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.3.0.tgz#107bdc028667788a8924ba040faf1fbccd5a7850"
+  integrity sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==
+  dependencies:
+    d3-time "1"
+
+d3-time@1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.1.0.tgz#b1e19d307dae9c900b7e5b25ffc5dcc249a8a0f1"
+  integrity sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==
+
+d3-timer@1:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.10.tgz#dfe76b8a91748831b13b6d9c793ffbd508dd9de5"
+  integrity sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==
+
+d3-transition@1:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-1.3.2.tgz#a98ef2151be8d8600543434c1ca80140ae23b398"
+  integrity sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA==
+  dependencies:
+    d3-color "1"
+    d3-dispatch "1"
+    d3-ease "1"
+    d3-interpolate "1"
+    d3-selection "^1.1.0"
+    d3-timer "1"
+
+d3-voronoi@1:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.4.tgz#dd3c78d7653d2bb359284ae478645d95944c8297"
+  integrity sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==
+
+d3-zoom@1:
+  version "1.8.3"
+  resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-1.8.3.tgz#b6a3dbe738c7763121cd05b8a7795ffe17f4fc0a"
+  integrity sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ==
+  dependencies:
+    d3-dispatch "1"
+    d3-drag "1"
+    d3-interpolate "1"
+    d3-selection "1"
+    d3-transition "1"
+
+d3@^5.15.1:
+  version "5.16.0"
+  resolved "https://registry.yarnpkg.com/d3/-/d3-5.16.0.tgz#9c5e8d3b56403c79d4ed42fbd62f6113f199c877"
+  integrity sha512-4PL5hHaHwX4m7Zr1UapXW23apo6pexCgdetdJ5kTmADpG/7T9Gkxw0M0tf/pjoB63ezCCm0u5UaFYy2aMt0Mcw==
+  dependencies:
+    d3-array "1"
+    d3-axis "1"
+    d3-brush "1"
+    d3-chord "1"
+    d3-collection "1"
+    d3-color "1"
+    d3-contour "1"
+    d3-dispatch "1"
+    d3-drag "1"
+    d3-dsv "1"
+    d3-ease "1"
+    d3-fetch "1"
+    d3-force "1"
+    d3-format "1"
+    d3-geo "1"
+    d3-hierarchy "1"
+    d3-interpolate "1"
+    d3-path "1"
+    d3-polygon "1"
+    d3-quadtree "1"
+    d3-random "1"
+    d3-scale "2"
+    d3-scale-chromatic "1"
+    d3-selection "1"
+    d3-shape "1"
+    d3-time "1"
+    d3-time-format "2"
+    d3-timer "1"
+    d3-transition "1"
+    d3-voronoi "1"
+    d3-zoom "1"
+
 dashdash@^1.12.0:
   version "1.14.1"
   resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
@@ -3851,7 +4320,7 @@ humanize-ms@^1.2.1:
   dependencies:
     ms "^2.0.0"
 
-iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4:
+iconv-lite@0.4, iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4:
   version "0.4.24"
   resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
   integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
@@ -6618,6 +7087,11 @@ run-parallel@^1.1.9:
   dependencies:
     queue-microtask "^1.2.2"
 
+rw@1:
+  version "1.3.3"
+  resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4"
+  integrity sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=
+
 rxjs@6.6.7, rxjs@^6.6.6, rxjs@~6.6.0:
   version "6.6.7"
   resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9"