diff --git a/client/src/app/admin/instance/dataset/components/attribute/detail/index.ts b/client/src/app/admin/instance/dataset/components/attribute/detail/index.ts
index 4db9273e1ec22368184b553c46b2f8f4a5433a02..d1581ea5b138546e26d7eba21a7cd6d624ce1612 100644
--- a/client/src/app/admin/instance/dataset/components/attribute/detail/index.ts
+++ b/client/src/app/admin/instance/dataset/components/attribute/detail/index.ts
@@ -9,10 +9,8 @@
 
 import { TableDetailComponent } from './table-detail.component';
 import { TrDetailComponent } from './tr-detail.component';
-import { detailRenderers } from './renderers';
 
 export const detailComponents = [
     TableDetailComponent,
-    TrDetailComponent,
-    detailRenderers
+    TrDetailComponent
 ];
diff --git a/client/src/app/admin/instance/dataset/components/attribute/detail/renderers/index.ts b/client/src/app/admin/instance/dataset/components/attribute/detail/renderers/index.ts
deleted file mode 100644
index 5d88e779669df93b295514cb7302eede2baab9ad..0000000000000000000000000000000000000000
--- a/client/src/app/admin/instance/dataset/components/attribute/detail/renderers/index.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * 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 { SpectraGraphRendererComponent } from './spectra-graph-renderer.component';
-
-export const detailRenderers = [
-    SpectraGraphRendererComponent
-];
-
-export * from './renderer-detail-form-factory';
-
-export const rendererDetailList = [
-    { label: 'Image', value: 'img', display: 10 },
-    { label: 'Spectra graph', value: 'spectra_graph', display: 20 }
-];
diff --git a/client/src/app/admin/instance/dataset/components/attribute/detail/renderers/renderer-detail-form-factory.ts b/client/src/app/admin/instance/dataset/components/attribute/detail/renderers/renderer-detail-form-factory.ts
deleted file mode 100644
index 5108b97da5409e606ea69db631ba93ad80f32454..0000000000000000000000000000000000000000
--- a/client/src/app/admin/instance/dataset/components/attribute/detail/renderers/renderer-detail-form-factory.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/**
- * 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 { FormControl } from "@angular/forms";
-
-export abstract class RendererDetailFormFactory {
-    static create(renderer: string) {
-        switch (renderer) {
-            case 'spectra_graph':
-                return {
-                    z: new FormControl(null)
-                };
-            default:
-                return {};
-        }
-    }
-}
diff --git a/client/src/app/admin/instance/dataset/components/attribute/detail/renderers/spectra-graph-renderer.component.html b/client/src/app/admin/instance/dataset/components/attribute/detail/renderers/spectra-graph-renderer.component.html
deleted file mode 100644
index 0769a05e96659585e1c99f879f98f9e7256c51c8..0000000000000000000000000000000000000000
--- a/client/src/app/admin/instance/dataset/components/attribute/detail/renderers/spectra-graph-renderer.component.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<ng-container [formGroup]="form">
-    <div class="form-group row align-items-center">
-        <label for="{{id}}_z" class="col-md-5 col-sm-12 col-form-label">Attribute z:</label>
-        <div class="col-md-7 col-sm-12">
-            <select class="form-control" name="{{id}}_z" formControlName="z">
-                <option *ngFor="let attribute of attributeList" [ngValue]="attribute.id">{{ attribute.form_label }}</option>
-            </select>
-        </div>
-    </div>
-</ng-container>
\ No newline at end of file
diff --git a/client/src/app/admin/instance/dataset/components/attribute/detail/renderers/spectra-graph-renderer.component.ts b/client/src/app/admin/instance/dataset/components/attribute/detail/renderers/spectra-graph-renderer.component.ts
deleted file mode 100644
index fa350b065baa875530522459154044c58e830c58..0000000000000000000000000000000000000000
--- a/client/src/app/admin/instance/dataset/components/attribute/detail/renderers/spectra-graph-renderer.component.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * 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 { FormGroup } from '@angular/forms';
-
-import { Attribute } from 'src/app/metamodel/models';
-
-@Component({
-    selector: 'app-spectra-graph-renderer',
-    templateUrl: 'spectra-graph-renderer.component.html',
-    changeDetection: ChangeDetectionStrategy.OnPush
-})
-export class SpectraGraphRendererComponent {
-    @Input() id: number;
-    @Input() form: FormGroup;
-    @Input() attributeList: Attribute[];
-}
diff --git a/client/src/app/admin/instance/dataset/components/attribute/detail/table-detail.component.html b/client/src/app/admin/instance/dataset/components/attribute/detail/table-detail.component.html
index b3ee075f0186ae8463790000f23a5d44d651e55a..ae53a43249c09f7380c6d5f6537b6ea13dbaf4bf 100644
--- a/client/src/app/admin/instance/dataset/components/attribute/detail/table-detail.component.html
+++ b/client/src/app/admin/instance/dataset/components/attribute/detail/table-detail.component.html
@@ -3,10 +3,8 @@
         <thead>
             <tr>
                 <th scope="col" style="min-width:150px">Name</th>
-                <th scope="col" style="width:50px">Visible</th>
-                <th scope="col" style="min-width:140px;width:140px;">Display detail</th>
-                <th scope="col" style="min-width:150px">Renderer</th>
-                <th scope="col" style="min-width:200px">Renderer config</th>
+                <th scope="col" style="min-width:150px">Output category</th>
+                <th scope="col" style="min-width:100px;width:100px;">Display</th>
                 <th scope="col" style="width:50px">Save</th>
             </tr>
         </thead>
diff --git a/client/src/app/admin/instance/dataset/components/attribute/detail/tr-detail.component.html b/client/src/app/admin/instance/dataset/components/attribute/detail/tr-detail.component.html
index 9cf9a69b1fcd8c9d1c97a8292dbc552eacd61cd9..ecb4ec71877ba64a3137d3bd0bcdc813d8c680b0 100644
--- a/client/src/app/admin/instance/dataset/components/attribute/detail/tr-detail.component.html
+++ b/client/src/app/admin/instance/dataset/components/attribute/detail/tr-detail.component.html
@@ -3,25 +3,13 @@
         <input type="text" class="form-control" name="name" formControlName="name">
     </td>
     <td>
-        <input type="checkbox" class="form-control" name="detail" formControlName="detail">
-    </td>
-    <td>
-        <input *ngIf="form.controls.detail.value" type="number" class="form-control" name="display_detail" (change)="rendererDetailOnChange()" formControlName="display_detail">
-    </td>
-    <td>
-        <select *ngIf="form.controls.detail.value" class="form-control" name="renderer_detail" formControlName="renderer_detail">
-            <option></option>
-            <option *ngFor="let rendererDetail of rendererDetailList" [ngValue]="rendererDetail.value">{{ rendererDetail.label }}</option>
+        <select class="form-control" name="id_detail_output_category" (change)="detailOutputCategoryOnChange()" formControlName="id_detail_output_category">
+            <option value=""></option>
+            <option *ngFor="let category of outputCategoryList" [ngValue]="category.id">{{category.label}}</option>
         </select>
     </td>
     <td>
-        <ng-container *ngIf="form.controls.renderer_detail.value" [ngSwitch]="form.controls.renderer_detail.value">
-            <app-spectra-graph-renderer *ngSwitchCase="'spectra_graph'"
-                [id]="attribute.id"
-                [form]="getRendererDetailConfigForm()"
-                [attributeList]="attributeList">
-            </app-spectra-graph-renderer>
-        </ng-container>
+        <input *ngIf="form.controls.id_detail_output_category.value" type="number" class="form-control" name="detail_display" formControlName="detail_display">
     </td>
     <td class="text-center align-middle">
         <button (click)="submit()" [disabled]="form.invalid || form.pristine" class="btn btn-outline-primary">
diff --git a/client/src/app/admin/instance/dataset/components/attribute/detail/tr-detail.component.ts b/client/src/app/admin/instance/dataset/components/attribute/detail/tr-detail.component.ts
index c9f2246ff43a3effc112e53e23d869d15db9025d..d8e82e1779660878b2abbcb775a4e36924daa10d 100644
--- a/client/src/app/admin/instance/dataset/components/attribute/detail/tr-detail.component.ts
+++ b/client/src/app/admin/instance/dataset/components/attribute/detail/tr-detail.component.ts
@@ -10,8 +10,7 @@
 import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, OnInit } from '@angular/core';
 import { FormControl, FormGroup } from '@angular/forms';
 
-import { Attribute } from 'src/app/metamodel/models';
-import { RendererDetailFormFactory, rendererDetailList } from './renderers';
+import { Attribute, OutputCategory } from 'src/app/metamodel/models';
 
 @Component({
     selector: '[detail]',
@@ -21,36 +20,24 @@ import { RendererDetailFormFactory, rendererDetailList } from './renderers';
 })
 export class TrDetailComponent implements OnInit {
     @Input() attribute: Attribute
-    @Input() attributeList: Attribute[];
+    @Input() outputCategoryList: OutputCategory[];
     @Output() save: EventEmitter<Attribute> = new EventEmitter();
 
-    rendererDetailList = rendererDetailList;
-
     public form = new FormGroup({
         name: new FormControl({ value: '', disabled: true }),
-        detail: new FormControl(),
-        renderer_detail: new FormControl(),
-        display_detail: new FormControl()
+        id_detail_output_category: new FormControl(),
+        detail_display: new FormControl()
     });
 
     ngOnInit() {
         if (this.attribute) {
-            const rendererDetailConfigForm = RendererDetailFormFactory.create(this.attribute.renderer_detail);
-            this.form.addControl('renderer_detail_config', new FormGroup(rendererDetailConfigForm));
             this.form.patchValue(this.attribute);
         }
     }
 
-    getRendererDetailConfigForm() {
-        return this.form.controls['renderer_detail_config'] as FormGroup;
-    }
-
-    rendererDetailOnChange() {
-        if (this.form.controls.renderer_detail.value === '') {
-            this.form.controls.renderer_detail.setValue(null);
-            this.form.setControl('renderer_detail_config', new FormGroup({}));
-        } else {
-            this.form.setControl('renderer_detail_config', new FormGroup(RendererDetailFormFactory.create(this.form.controls.renderer_detail.value)));
+    detailOutputCategoryOnChange(): void {
+        if (this.form.controls.id.value === '') {
+            this.form.controls.id_detail_output_category.setValue(null);
         }
     }
 
diff --git a/client/src/app/admin/instance/dataset/components/attribute/general/add-attribute.component.ts b/client/src/app/admin/instance/dataset/components/attribute/general/add-attribute.component.ts
index c9e6986bd336d361006512e8deea53fc7b213c82..c1f4241c0dd4a8f7130aa5dd27166ab2fa35a3a2 100644
--- a/client/src/app/admin/instance/dataset/components/attribute/general/add-attribute.component.ts
+++ b/client/src/app/admin/instance/dataset/components/attribute/general/add-attribute.component.ts
@@ -69,10 +69,7 @@ export class AddAttributeComponent {
             renderer_config: null,
             order_by: true,
             archive: false,
-            detail: true,
-            display_detail: id * 10,
-            renderer_detail: null,
-            renderer_detail_config: null,
+            detail_display: id * 10,
             options: null,
             vo_utype: null,
             vo_ucd: null,
@@ -81,7 +78,8 @@ export class AddAttributeComponent {
             vo_datatype: null,
             vo_size: null,
             id_criteria_family: null,
-            id_output_category: null
+            id_output_category: null,
+            id_detail_output_category: null
         })
     }
 }
diff --git a/client/src/app/admin/instance/dataset/components/attribute/result/renderers/detail-link-renderer.component.html b/client/src/app/admin/instance/dataset/components/attribute/result/renderers/detail-link-renderer.component.html
index a2f24533af1fd2bed4f66f9acae993525049df9b..aa36946e1b8201cb61c123451d367bd39cebf838 100644
--- a/client/src/app/admin/instance/dataset/components/attribute/result/renderers/detail-link-renderer.component.html
+++ b/client/src/app/admin/instance/dataset/components/attribute/result/renderers/detail-link-renderer.component.html
@@ -9,14 +9,4 @@
             </select>
         </div>
     </div>
-    <div class="form-group row">
-        <label for="{{id}}_component" class="col-md-5 col-sm-12 col-form-label">Component:</label>
-        <div class="col-md-7 col-sm-12">
-            <select class="form-control" name="{{id}}_component" formControlName="component">
-                <option></option>
-                <option value="default">Default</option>
-                <option value="spectra">Spectra</option>
-            </select>
-        </div>
-    </div>
 </ng-container>
diff --git a/client/src/app/admin/instance/dataset/components/attribute/result/renderers/renderer-form-factory.ts b/client/src/app/admin/instance/dataset/components/attribute/result/renderers/renderer-form-factory.ts
index a4324fecf84bb41a833870393e7a3784f038fde5..27374e39be6600755a91ee067b84d34141d1f5d1 100644
--- a/client/src/app/admin/instance/dataset/components/attribute/result/renderers/renderer-form-factory.ts
+++ b/client/src/app/admin/instance/dataset/components/attribute/result/renderers/renderer-form-factory.ts
@@ -14,8 +14,7 @@ export abstract class RendererFormFactory {
         switch (renderer) {
             case 'detail-link':
                 return {
-                    display: new FormControl('text'),
-                    component: new FormControl('default')
+                    display: new FormControl('text')
                 };
             case 'link':
                 return {
diff --git a/client/src/app/admin/instance/dataset/components/detail-page/detail-config-form.component.html b/client/src/app/admin/instance/dataset/components/detail-page/detail-config-form.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..d27924f7b1316cc8a0f8f705255f46c8a7d73f72
--- /dev/null
+++ b/client/src/app/admin/instance/dataset/components/detail-page/detail-config-form.component.html
@@ -0,0 +1,10 @@
+<form [formGroup]="form" (ngSubmit)="submit()" novalidate>
+    <div class="custom-control custom-switch">
+        <input class="custom-control-input" type="checkbox" id="enabled" name="enabled" formControlName="enabled">
+        <label class="custom-control-label" for="enabled">Enabled</label>
+    </div>
+    <app-webpage-form-content [form]="form" [controlName]="'content'" [controlLabel]="'Content'"></app-webpage-form-content>
+    <div class="form-group mt-3">
+        <ng-content></ng-content>
+    </div>
+</form>
diff --git a/client/src/app/admin/instance/dataset/components/detail-page/detail-config-form.component.ts b/client/src/app/admin/instance/dataset/components/detail-page/detail-config-form.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a0b257653e8489688f7067296111dfb82fd2702d
--- /dev/null
+++ b/client/src/app/admin/instance/dataset/components/detail-page/detail-config-form.component.ts
@@ -0,0 +1,39 @@
+/**
+ * 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, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
+import { FormGroup, FormControl } from '@angular/forms';
+
+import { Attribute, DetailConfig } from 'src/app/metamodel/models';
+
+@Component({
+    selector: 'app-detail-config-form',
+    templateUrl: 'detail-config-form.component.html',
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class DetailConfigFormComponent implements OnInit {
+    @Input() detailConfig: DetailConfig;
+    @Output() onSubmit: EventEmitter<DetailConfig> = new EventEmitter();
+
+    public form = new FormGroup({
+        enabled: new FormControl(false),
+        content: new FormControl()
+    });
+
+    ngOnInit() {
+        this.form.patchValue(this.detailConfig);
+    }
+
+    submit() {
+        this.onSubmit.emit({
+            ...this.form.getRawValue()
+        });
+        this.form.markAsPristine();
+    }
+}
diff --git a/client/src/app/admin/instance/dataset/components/detail-page/detail-config.component.html b/client/src/app/admin/instance/dataset/components/detail-page/detail-config.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..57ad9f57db4d0128c7b7f6469cbcdd33610eeb6c
--- /dev/null
+++ b/client/src/app/admin/instance/dataset/components/detail-page/detail-config.component.html
@@ -0,0 +1,8 @@
+<app-detail-config-form
+    [detailConfig]="detailConfig"
+    (onSubmit)="save($event)"
+    #formDetailConfig>
+    <button [disabled]="!formDetailConfig.form.valid || formDetailConfig.form.pristine" type="submit" class="btn btn-primary">
+        <span class="fa fa-database"></span> Save detail config
+    </button>
+</app-detail-config-form>
\ No newline at end of file
diff --git a/client/src/app/admin/instance/dataset/components/detail-page/detail-config.component.ts b/client/src/app/admin/instance/dataset/components/detail-page/detail-config.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0d46c4f1a7f131e8442b18454475b3e1daed43eb
--- /dev/null
+++ b/client/src/app/admin/instance/dataset/components/detail-page/detail-config.component.ts
@@ -0,0 +1,34 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
+
+import { DetailConfig } from 'src/app/metamodel/models';
+
+@Component({
+    selector: 'app-detail-config',
+    templateUrl: 'detail-config.component.html',
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class DetailConfigComponent {
+    @Input() detailConfig: DetailConfig;
+    @Output() addDetailConfig: EventEmitter<DetailConfig> = new EventEmitter();
+    @Output() editDetailConfig: EventEmitter<DetailConfig> = new EventEmitter();
+
+    save(detailConfig: DetailConfig) {
+        if (this.detailConfig) {
+            this.editDetailConfig.emit({
+                ...this.detailConfig,
+                ...detailConfig
+            });
+        } else {
+            this.addDetailConfig.emit(detailConfig);
+        }
+    }
+}
diff --git a/client/src/app/admin/instance/dataset/components/detail-page/index.ts b/client/src/app/admin/instance/dataset/components/detail-page/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..27ce4989d84201ac51a3ac2a01cd652bc16f634d
--- /dev/null
+++ b/client/src/app/admin/instance/dataset/components/detail-page/index.ts
@@ -0,0 +1,7 @@
+import { DetailConfigComponent } from './detail-config.component';
+import { DetailConfigFormComponent } from './detail-config-form.component';
+
+export const detailConfigComponents = [
+    DetailConfigComponent,
+    DetailConfigFormComponent
+];
diff --git a/client/src/app/admin/instance/dataset/components/index.ts b/client/src/app/admin/instance/dataset/components/index.ts
index b28b04af274518cf75b5d4a0ffaf4e3e91654f87..dc81a716e436811b972e13bbf28a8eca3a9ab063 100644
--- a/client/src/app/admin/instance/dataset/components/index.ts
+++ b/client/src/app/admin/instance/dataset/components/index.ts
@@ -14,6 +14,7 @@ import { datasetFamilyComponents } from './dataset-family';
 import { imageComponents } from './image';
 import { fileComponents } from './file';
 import { coneSearchConfigComponents } from './cone-search-config';
+import { detailConfigComponents } from './detail-page';
 import { outputCategoryComponents } from './output-category';
 import { outputFamilyComponents } from './output-family';
 import { InstanceButtonsComponent } from './instance-buttons.component';
@@ -26,6 +27,7 @@ export const dummiesComponents = [
     imageComponents,
     fileComponents,
     coneSearchConfigComponents,
+    detailConfigComponents,
     outputCategoryComponents,
     outputFamilyComponents,
     InstanceButtonsComponent
diff --git a/client/src/app/admin/instance/dataset/containers/configure-dataset.component.html b/client/src/app/admin/instance/dataset/containers/configure-dataset.component.html
index 48e2723501dc743bce9fd66de2d468043d32068c..bc48f6ba520392a1e1e886e0125b305e847d8eb8 100644
--- a/client/src/app/admin/instance/dataset/containers/configure-dataset.component.html
+++ b/client/src/app/admin/instance/dataset/containers/configure-dataset.component.html
@@ -4,6 +4,7 @@
     || (outputFamilyListIsLoading | async)
     || (outputCategoryListIsLoading | async)
     || (imageListIsLoading | async)
+    || (detailConfigIsLoading | async)
     || (coneSearchConfigIsLoading | async)">
 </app-spinner>
 
@@ -13,6 +14,7 @@
     && (outputFamilyListIsLoaded | async)
     && (outputCategoryListIsLoaded | async)
     && (imageListIsLoaded | async)
+    && (detailConfigIsLoaded | async)
     && (coneSearchConfigIsLoaded | async)" class="container-fluid">
     <nav aria-label="breadcrumb">
         <ol class="breadcrumb">
@@ -75,6 +77,9 @@
                         <li class="nav-item">
                             <a class="nav-link" routerLink="./" [queryParams]="{tab_selected: 'cone-search'}" [ngClass]="{'active': (tabSelected | async) === 'cone-search'}">Cone-search</a>
                         </li>
+                        <li class="nav-item">
+                            <a class="nav-link" routerLink="./" [queryParams]="{tab_selected: 'detail-page'}" [ngClass]="{'active': (tabSelected | async) === 'detail-page'}">Detail page</a>
+                        </li>
                     </ul>
                 </div>
                 <div class="card-body" [ngSwitch]="tabSelected | async">
@@ -124,7 +129,7 @@
                         <tr *ngFor="let attribute of (attributeList | async)"
                             detail
                             [attribute]="attribute"
-                            [attributeList]="attributeList | async"
+                            [outputCategoryList]="outputCategoryList | async"
                             (save)="editAttribute($event)">
                         </tr>
                     </app-table-detail>
@@ -195,6 +200,12 @@
                         (addConeSearchConfig)="addConeSearchConfig($event)"
                         (editConeSearchConfig)="editConeSearchConfig($event)">
                     </app-cone-search-config>
+                    <app-detail-config
+                        *ngSwitchCase="'detail-page'"
+                        [detailConfig]="detailConfig | async"
+                        (addDetailConfig)="addDetailConfig($event)"
+                        (editDetailConfig)="editDetailConfig($event)">
+                    </app-detail-config>
                 </div>
             </div>
         </div>
diff --git a/client/src/app/admin/instance/dataset/containers/configure-dataset.component.ts b/client/src/app/admin/instance/dataset/containers/configure-dataset.component.ts
index 51d97035d02561273765a3b1840712e39408b701..d24b7307be2287b1c696328bb840a6e0e8d46a88 100644
--- a/client/src/app/admin/instance/dataset/containers/configure-dataset.component.ts
+++ b/client/src/app/admin/instance/dataset/containers/configure-dataset.component.ts
@@ -14,7 +14,7 @@ import { Observable } from 'rxjs';
 import { map } from 'rxjs/operators';
 import { Store } from '@ngrx/store';
 
-import { Instance, Dataset, Attribute, CriteriaFamily, OutputCategory, OutputFamily, Image, File, ConeSearchConfig } from 'src/app/metamodel/models';
+import { Instance, Dataset, Attribute, CriteriaFamily, OutputCategory, OutputFamily, Image, File, ConeSearchConfig, DetailConfig } from 'src/app/metamodel/models';
 import { Column, FileInfo, FitsImageLimits } from 'src/app/admin/store/models';
 import * as instanceSelector from 'src/app/metamodel/selectors/instance.selector';
 import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector';
@@ -40,6 +40,8 @@ import * as fitsImageActions from 'src/app/admin/store/actions/fits-image.action
 import * as fitsImageSelector from 'src/app/admin/store/selectors/fits-image.selector';
 import * as coneSearchConfigActions from 'src/app/metamodel/actions/cone-search-config.actions';
 import * as coneSearchConfigSelector from 'src/app/metamodel/selectors/cone-search-config.selector';
+import * as detailConfigActions from 'src/app/metamodel/actions/detail-config.actions';
+import * as detailConfigSelector from 'src/app/metamodel/selectors/detail-config.selector';
 
 @Component({
     selector: 'app-configure-dataset',
@@ -85,6 +87,9 @@ export class ConfigureDatasetComponent implements OnInit {
     public coneSearchConfig: Observable<ConeSearchConfig>;
     public coneSearchConfigIsLoading: Observable<boolean>;
     public coneSearchConfigIsLoaded: Observable<boolean>;
+    public detailConfig: Observable<DetailConfig>;
+    public detailConfigIsLoading: Observable<boolean>;
+    public detailConfigIsLoaded: Observable<boolean>;
 
     constructor(private store: Store<{ }>, private route: ActivatedRoute) {
         this.instance = store.select(instanceSelector.selectInstanceByRouteName);
@@ -124,6 +129,9 @@ export class ConfigureDatasetComponent implements OnInit {
         this.coneSearchConfig = store.select(coneSearchConfigSelector.selectConeSearchConfig);
         this.coneSearchConfigIsLoading = store.select(coneSearchConfigSelector.selectConeSearchConfigIsLoading);
         this.coneSearchConfigIsLoaded = store.select(coneSearchConfigSelector.selectConeSearchConfigIsLoaded);
+        this.detailConfig = store.select(detailConfigSelector.selectDetailConfig);
+        this.detailConfigIsLoading = store.select(detailConfigSelector.selectDetailConfigIsLoading);
+        this.detailConfigIsLoaded = store.select(detailConfigSelector.selectDetailConfigIsLoaded);
     }
 
     ngOnInit() {
@@ -133,6 +141,7 @@ export class ConfigureDatasetComponent implements OnInit {
         Promise.resolve(null).then(() => this.store.dispatch(outputCategoryActions.loadOutputCategoryList()));
         Promise.resolve(null).then(() => this.store.dispatch(imageActions.loadImageList()));
         Promise.resolve(null).then(() => this.store.dispatch(coneSearchConfigActions.loadConeSearchConfig()));
+        Promise.resolve(null).then(() => this.store.dispatch(detailConfigActions.loadDetailConfig()));
         this.tabSelected = this.route.queryParamMap.pipe(
             map(params => params.get('tab_selected'))
         );
@@ -237,4 +246,12 @@ export class ConfigureDatasetComponent implements OnInit {
     editConeSearchConfig(coneSearchConfig: ConeSearchConfig) {
         this.store.dispatch(coneSearchConfigActions.editConeSearchConfig({ coneSearchConfig }));
     }
+
+    addDetailConfig(detailConfig: DetailConfig) {
+        this.store.dispatch(detailConfigActions.addDetailConfig({ detailConfig }));
+    }
+
+    editDetailConfig(detailConfig: DetailConfig) {
+        this.store.dispatch(detailConfigActions.editDetailConfig({ detailConfig }));
+    }
 }
diff --git a/client/src/app/admin/instance/webpage/components/webpage-form.component.ts b/client/src/app/admin/instance/webpage/components/webpage-form.component.ts
index e473ea5338604cc3fc87b8ee7d71d4e72b99ef58..7c2d4cd833e72b389a8913991ca5ebc5a358e98a 100644
--- a/client/src/app/admin/instance/webpage/components/webpage-form.component.ts
+++ b/client/src/app/admin/instance/webpage/components/webpage-form.component.ts
@@ -41,11 +41,4 @@ export class WebpageFormComponent implements OnInit {
             this.onSubmit.emit(this.form.getRawValue());
         }
     }
-
-    getEditorConfig() {
-        return {
-            plugins: "lists link image table code help wordcount media",
-            content_css: "https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css"
-        };
-    }
 }
diff --git a/client/src/app/instance/dynamic-content/dynamic-components/index.ts b/client/src/app/instance/dynamic-content/dynamic-components/index.ts
deleted file mode 100644
index 81f64b144b7758770edc2f0a2f79810bedc1f3b0..0000000000000000000000000000000000000000
--- a/client/src/app/instance/dynamic-content/dynamic-components/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { DynamicRouterLinkComponent } from './dynamic-router-link.component';
-import { DatasetSampleComponent } from './dataset-sample.component';
-
-export const DynamicComponents = [
-    DynamicRouterLinkComponent,
-    DatasetSampleComponent
-];
diff --git a/client/src/app/instance/dynamic-content/dynamic-content.module.ts b/client/src/app/instance/dynamic-content/dynamic-content.module.ts
deleted file mode 100644
index 8f4f27818c3c0ea4ef559d24a8a79de8f825e191..0000000000000000000000000000000000000000
--- a/client/src/app/instance/dynamic-content/dynamic-content.module.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * 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 { NgModule } from '@angular/core';
-import { RouterModule } from '@angular/router';
-
-import { DynamicHooksModule, HookParserEntry } from 'ngx-dynamic-hooks';
-
-import { SharedModule } from 'src/app/shared/shared.module';
-import { hookParsers } from './parsers';
-import { DynamicComponents } from './dynamic-components';
-import { dummiesComponents } from './components';
-
-export const componentParsers: Array<HookParserEntry> = [
-    ...hookParsers,
-    ...DynamicComponents.map(component => {
-        return { component };
-    })
-];
-
-/**
- * @class
- * @classdesc Dynamic content module.
- */
-@NgModule({
-    imports: [
-        SharedModule,
-        RouterModule,
-        DynamicHooksModule.forRoot({
-            globalParsers: componentParsers
-        }),
-    ],
-    exports: [
-        DynamicHooksModule
-    ],
-    providers: [
-        hookParsers
-    ],
-    declarations: [
-        DynamicComponents,
-        dummiesComponents
-    ],
-    entryComponents: [
-        DynamicComponents
-    ]
-})
-export class DynamicContentModule { }
diff --git a/client/src/app/instance/search/components/criteria/criteria-by-family.component.spec.ts b/client/src/app/instance/search/components/criteria/criteria-by-family.component.spec.ts
index 6bcf967ca0b37f1df5418240da083483200cf17b..ec52326679ba7ec5580710e9e52959627a5f8048 100644
--- a/client/src/app/instance/search/components/criteria/criteria-by-family.component.spec.ts
+++ b/client/src/app/instance/search/components/criteria/criteria-by-family.component.spec.ts
@@ -187,10 +187,7 @@ describe('[Instance][Search][Component][Criteria] CriteriaByFamilyComponent', ()
                 renderer_config: null,
                 order_by: true,
                 archive: false,
-                detail: true,
-                display_detail: 2,
-                renderer_detail: null,
-                renderer_detail_config: null,
+                detail_display: 2,
                 options: [
                     { label: 'Three', value: 'three', display: 3 },
                     { label: 'One', value: 'one', display: 1 },
@@ -203,7 +200,8 @@ describe('[Instance][Search][Component][Criteria] CriteriaByFamilyComponent', ()
                 vo_datatype: null,
                 vo_size: null,
                 id_criteria_family: null,
-                id_output_category: null
+                id_output_category: null,
+                id_detail_output_category: null
             }
         ];
         const attributeId = 1;
diff --git a/client/src/app/instance/search/components/detail/default-object.component.html b/client/src/app/instance/search/components/detail/default-object.component.html
deleted file mode 100644
index bc6843ae7d94023d1cd8b39b9c9c57631d17822f..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/detail/default-object.component.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<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/search/components/detail/default-object.component.spec.ts b/client/src/app/instance/search/components/detail/default-object.component.spec.ts
deleted file mode 100644
index 24698efed39c3aa1726a35003d06811c4100fe2f..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/detail/default-object.component.spec.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
-import { Component, Input } from '@angular/core';
-
-import { DefaultObjectComponent } from './default-object.component';
-import { Attribute, OutputCategory, OutputFamily } from 'src/app/metamodel/models';
-
-describe('[Instance][Search][Component][Detail][Default] DefaultObjectComponent', () => {
-    @Component({ selector: 'app-object-data', template: '' })
-    class ObjectDataStubComponent {
-        @Input() datasetSelected: string;
-        @Input() outputFamilyList: OutputFamily[];
-        @Input() outputCategoryList: OutputCategory[];
-        @Input() attributeList: Attribute[];
-        @Input() object: any;
-    }
-
-    let component: DefaultObjectComponent;
-    let fixture: ComponentFixture<DefaultObjectComponent>;
-
-    beforeEach(waitForAsync(() => {
-        TestBed.configureTestingModule({
-            declarations: [
-                DefaultObjectComponent,
-                ObjectDataStubComponent
-            ]
-        });
-        fixture = TestBed.createComponent(DefaultObjectComponent);
-        component = fixture.componentInstance;
-    }));
-
-    it('should create the component', () => {
-        expect(component).toBeTruthy();
-    });
-});
diff --git a/client/src/app/instance/search/components/detail/default-object.component.ts b/client/src/app/instance/search/components/detail/default-object.component.ts
deleted file mode 100644
index b690b814ea75139b70aeb8457d44e465ebdd063c..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/detail/default-object.component.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * 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';
-
-/**
- * @class
- * @classdesc Detail default object component.
- */
-@Component({
-    selector: 'app-default-object',
-    templateUrl: 'default-object.component.html',
-    changeDetection: ChangeDetectionStrategy.OnPush
-})
-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/search/components/detail/index.ts b/client/src/app/instance/search/components/detail/index.ts
deleted file mode 100644
index 86a0c1e9c69b6eec1dd7fcaf5564e0f907c3ba38..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/detail/index.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { ObjectDataComponent } from './object-data.component';
-import { DefaultObjectComponent } from './default-object.component';
-import { spectraComponents } from './spectra';
-
-export const detailsComponents = [
-    ObjectDataComponent,
-    DefaultObjectComponent,
-    spectraComponents
-];
diff --git a/client/src/app/instance/search/components/detail/object-data.component.html b/client/src/app/instance/search/components/detail/object-data.component.html
deleted file mode 100644
index 6715883945caaabe47b4e59b39d05e6cf014deea..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/detail/object-data.component.html
+++ /dev/null
@@ -1,66 +0,0 @@
-<div *ngIf="getAttributeRa() && getAttributeDec()" class="row">
-    <div class="col-12">
-        <table class="table mb-1" aria-describedby="Object coordinates">
-            <tr>
-                <th scope="col">Alpha</th>
-                <th scope="col">Delta</th>
-                <th scope="col" class="text-center" rowspan="2"><img src="assets/cesam_anis80.png" alt="CeSAM logo" /></th>
-            </tr>
-            <tr>
-                <td>{{ object[getAttributeRa().label] }}</td>
-                <td>{{ object[getAttributeDec().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 getCategoryByFamily(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/search/components/detail/object-data.component.spec.ts b/client/src/app/instance/search/components/detail/object-data.component.spec.ts
deleted file mode 100644
index cdc9ba295ddcb75021fd693e230c9a04b7275d0d..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/detail/object-data.component.spec.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
-
-import { AccordionModule } from 'ngx-bootstrap/accordion';
-
-import { ObjectDataComponent } from './object-data.component';
-import { AppConfigService } from '../../../../app-config.service';
-import { ATTRIBUTE_LIST, CATEGORY_LIST } from '../../../../../test-data';
-
-describe('[Instance][Search][Component][Detail] ObjectDataComponent', () => {
-    let component: ObjectDataComponent;
-    let fixture: ComponentFixture<ObjectDataComponent>;
-    let appConfigServiceStub = new AppConfigService();
-
-    beforeEach(waitForAsync(() => {
-        TestBed.configureTestingModule({
-            declarations: [ObjectDataComponent],
-            imports: [AccordionModule.forRoot()],
-            providers: [{ provide: AppConfigService, useValue: appConfigServiceStub }]
-        });
-        fixture = TestBed.createComponent(ObjectDataComponent);
-        component = fixture.componentInstance;
-    }));
-
-    it('should create the component', () => {
-        expect(component).toBeTruthy();
-    });
-
-    it('#getCategoryByFamily() should return categories for the given family', () => {
-        component.outputCategoryList = CATEGORY_LIST;
-        expect(component.getCategoryByFamily(1).length).toEqual(2);
-    });
-
-    it('#getAttributesVisibleByCategory() should return visible attributes for the given category', () => {
-        component.attributeList = ATTRIBUTE_LIST;
-        expect(component.getAttributesVisibleByCategory(1).length).toEqual(1);
-        expect(component.getAttributesVisibleByCategory(1)[0].id).toEqual(2);
-    });
-
-    it('#getAttributesVisible() should return visible attributes', () => {
-        component.attributeList = ATTRIBUTE_LIST;
-        expect(component.getAttributesVisible().length).toEqual(2);
-    });
-
-    it('#getDownloadHref() should return URL', () => {
-        appConfigServiceStub.apiUrl = 'http://test.com';
-        component.datasetSelected = 'myDataset';
-        expect(component.getDownloadHref('myAttributeLabel')).toEqual('http://test.com/download-file/myDataset/myAttributeLabel');
-    });
-});
diff --git a/client/src/app/instance/search/components/detail/object-data.component.ts b/client/src/app/instance/search/components/detail/object-data.component.ts
deleted file mode 100644
index 0b8a4a21ff2045107ec3cf10cdd8e3e7d6a1dd9f..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/detail/object-data.component.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-/**
- * 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';
-import { AppConfigService } from 'src/app/app-config.service';
-
-/**
- * @class
- * @classdesc Detail object data component.
- */
-@Component({
-    selector: 'app-object-data',
-    templateUrl: 'object-data.component.html',
-    changeDetection: ChangeDetectionStrategy.OnPush
-})
-export class ObjectDataComponent {
-    @Input() datasetSelected: string;
-    @Input() outputFamilyList: OutputFamily[];
-    @Input() outputCategoryList: OutputCategory[];
-    @Input() attributeList: Attribute[];
-    @Input() object: any;
-
-    constructor(private appConfig: AppConfigService) { }
-
-    /**
-     * Returns category list for the given output family ID.
-     *
-     * @param  {number} idFamily - The output family ID.
-     *
-     * @return OutputCategory[]
-     */
-    getCategoryByFamily(idFamily: number): OutputCategory[] {
-        return this.outputCategoryList
-            .filter(category => category.id_output_family === idFamily);
-    }
-
-    /**
-     * Returns attribute list for the given output category ID that are visible in detail page.
-     *
-     * @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);
-    }
-
-    /**
-     * Returns attribute list that are visible in detail page.
-     *
-     * @return Attribute[]
-     */
-    getAttributesVisible(): Attribute[] {
-        return this.attributeList.filter(a => a.detail);
-    }
-
-    /**
-     * Returns URL where download object.
-     *
-     * @param  {string} attributeLabel - The attribute label.
-     *
-     * @return string
-     */
-    getDownloadHref(attributeLabel: string): string {
-        return `${getHost(this.appConfig.apiUrl)}/download-file/${this.datasetSelected}/${attributeLabel}`;
-    }
-
-    getAttributeRa() {
-        return this.attributeList.find(attribute => attribute.renderer_detail === 'ra');
-    }
-
-    getAttributeDec() {
-        return this.attributeList.find(attribute => attribute.renderer_detail === 'dec');
-    }
-}
diff --git a/client/src/app/instance/search/components/detail/spectra/index.ts b/client/src/app/instance/search/components/detail/spectra/index.ts
deleted file mode 100644
index 77b5e03935e6f4321da07852a1d56a460bc70c3a..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/detail/spectra/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-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/search/components/detail/spectra/spectra-object.component.html b/client/src/app/instance/search/components/detail/spectra/spectra-object.component.html
deleted file mode 100644
index 8e1360dc72b1e7d49099e2aa51a59a1ff270e996..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/detail/spectra/spectra-object.component.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<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()" class="btn btn-lg btn-block dl-btn">
-                            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/search/components/detail/spectra/spectra-object.component.scss b/client/src/app/instance/search/components/detail/spectra/spectra-object.component.scss
deleted file mode 100644
index 260a2036246abdb58c05276b1a262e50360ac3f3..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/detail/spectra/spectra-object.component.scss
+++ /dev/null
@@ -1,13 +0,0 @@
-/**
- * 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/search/components/detail/spectra/spectra-object.component.spec.ts b/client/src/app/instance/search/components/detail/spectra/spectra-object.component.spec.ts
deleted file mode 100644
index 890caacf18ba43e6417ac904bdcb27099628aba1..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/detail/spectra/spectra-object.component.spec.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { Component, Input } from '@angular/core';
-
-import { SpectraObjectComponent } from './spectra-object.component';
-import { Attribute, OutputCategory, OutputFamily } from 'src/app/metamodel/models';
-import { AppConfigService } from 'src/app/app-config.service';
-import { ATTRIBUTE_LIST, OBJECT_DETAIL } from 'src/test-data';
-
-describe('[Instance][Search][Component][Detail][Spectra] SpectraObjectComponent', () => {
-    @Component({ selector: 'app-spectra-graph', template: '' })
-    class SpectraGraphStubComponent {
-        @Input() z: number;
-        @Input() spectraCSV: string;
-    }
-
-    @Component({ selector: 'app-object-data', template: '' })
-    class ObjectDataStubComponent {
-        @Input() datasetSelected: string;
-        @Input() outputFamilyList: OutputFamily[];
-        @Input() outputCategoryList: OutputCategory[];
-        @Input() attributeList: Attribute[];
-        @Input() object: any;
-    }
-
-    let component: SpectraObjectComponent;
-    let fixture: ComponentFixture<SpectraObjectComponent>;
-    let appConfigServiceStub = new AppConfigService();
-
-    beforeEach(() => {
-        TestBed.configureTestingModule({
-            declarations: [
-                SpectraObjectComponent,
-                SpectraGraphStubComponent,
-                ObjectDataStubComponent
-            ],
-            providers: [{ provide: AppConfigService, useValue: appConfigServiceStub }]
-        });
-        fixture = TestBed.createComponent(SpectraObjectComponent);
-        component = fixture.componentInstance;
-    });
-
-    it('should create the component', () => {
-        expect(component).toBeTruthy();
-    });
-
-    it('#ngOnInit() should emit getSpectraCSV event if an attribute have spectra_graph renderer_detail', () => {
-        component.attributeList = ATTRIBUTE_LIST;
-        component.object = OBJECT_DETAIL;
-        component.getSpectraCSV.subscribe((event: string) => expect(event).toEqual('spec1d'));
-        component.ngOnInit();
-    });
-});
diff --git a/client/src/app/instance/search/components/detail/spectra/spectra-object.component.ts b/client/src/app/instance/search/components/detail/spectra/spectra-object.component.ts
deleted file mode 100644
index 97aacb376ba9676c9f0252a7fd6f6acf3a5956f1..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/components/detail/spectra/spectra-object.component.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-/**
- * 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, SpectraGraphRendererConfig } from 'src/app/metamodel/models';
-import { getHost } from 'src/app/shared/utils';
-import { AppConfigService } from 'src/app/app-config.service';
-
-/**
- * @class
- * @classdesc Detail spectra object component.
- *
- * @implements OnInit
- */
-@Component({
-    selector: 'app-spectra-object',
-    templateUrl: 'spectra-object.component.html',
-    styleUrls: ['spectra-object.component.scss'],
-    changeDetection: ChangeDetectionStrategy.OnPush
-})
-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();
-
-    constructor(private appConfig: AppConfigService) { }
-
-    ngOnInit(): void {
-        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 = this.attributeList[0];
-        return `${getHost(this.appConfig.apiUrl)}/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 spectraGraphRendererConfig = this.getAttributeSpectraGraph().renderer_detail_config as SpectraGraphRendererConfig;
-        if (spectraGraphRendererConfig.z) {
-            const attributeZ = this.attributeList.find(attribute => attribute.id === spectraGraphRendererConfig.z);
-            return +this.object[attributeZ.label];
-        } else {
-            return 0;
-        }
-    }
-}
diff --git a/client/src/app/instance/search/components/index.ts b/client/src/app/instance/search/components/index.ts
index f4057d78828f281aaaf1903fdc286bdd24d240bd..9a0df624bd3563eecf4e0efb3ef5ab367ae7c861 100644
--- a/client/src/app/instance/search/components/index.ts
+++ b/client/src/app/instance/search/components/index.ts
@@ -4,7 +4,6 @@ import { datasetComponents } from './dataset';
 import { criteriaComponents } from './criteria';
 import { outputComponents } from './output';
 import { resultComponents } from './result';
-import { detailsComponents } from './detail';
 
 export const dummiesComponents = [
     ProgressBarComponent,
@@ -12,6 +11,5 @@ export const dummiesComponents = [
     datasetComponents,
     criteriaComponents,
     outputComponents,
-    resultComponents,
-    detailsComponents
+    resultComponents
 ];
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/output/output-by-category.component.spec.ts b/client/src/app/instance/search/components/output/output-by-category.component.spec.ts
index 31e0a45f565e90afa4a41ef9d03f65c65acba485..994328f8ea0eede0e5157b32ae82d3444cdb2d05 100644
--- a/client/src/app/instance/search/components/output/output-by-category.component.spec.ts
+++ b/client/src/app/instance/search/components/output/output-by-category.component.spec.ts
@@ -68,10 +68,7 @@ describe('[Instance][Search][Component][Output] OutputByCategoryComponent', () =
                 renderer_config: null,
                 order_by: true,
                 archive: false,
-                detail: true,
-                display_detail: 1,
-                renderer_detail: null,
-                renderer_detail_config: null,
+                detail_display: 1,
                 options: null,
                 vo_utype: null,
                 vo_ucd: null,
@@ -80,7 +77,8 @@ describe('[Instance][Search][Component][Output] OutputByCategoryComponent', () =
                 vo_datatype: null,
                 vo_size: null,
                 id_criteria_family: null,
-                id_output_category: null
+                id_output_category: null,
+                id_detail_output_category: null
             },
             {
                 id: 1,
@@ -104,10 +102,7 @@ describe('[Instance][Search][Component][Output] OutputByCategoryComponent', () =
                 renderer_config: null,
                 order_by: true,
                 archive: false,
-                detail: true,
-                display_detail: 2,
-                renderer_detail: null,
-                renderer_detail_config: null,
+                detail_display: 2,
                 options: null,
                 vo_utype: null,
                 vo_ucd: null,
@@ -116,7 +111,8 @@ describe('[Instance][Search][Component][Output] OutputByCategoryComponent', () =
                 vo_datatype: null,
                 vo_size: null,
                 id_criteria_family: null,
-                id_output_category: null
+                id_output_category: null,
+                id_detail_output_category: null
             }
         ];
         component.outputList = [];
@@ -149,10 +145,7 @@ describe('[Instance][Search][Component][Output] OutputByCategoryComponent', () =
                 renderer_config: null,
                 order_by: true,
                 archive: false,
-                detail: true,
-                display_detail: 1,
-                renderer_detail: null,
-                renderer_detail_config: null,
+                detail_display: 1,
                 options: null,
                 vo_utype: null,
                 vo_ucd: null,
@@ -161,7 +154,8 @@ describe('[Instance][Search][Component][Output] OutputByCategoryComponent', () =
                 vo_datatype: null,
                 vo_size: null,
                 id_criteria_family: null,
-                id_output_category: null
+                id_output_category: null,
+                id_detail_output_category: null
             },
             {
                 id: 1,
@@ -185,10 +179,7 @@ describe('[Instance][Search][Component][Output] OutputByCategoryComponent', () =
                 renderer_config: null,
                 order_by: true,
                 archive: false,
-                detail: true,
-                display_detail: 2,
-                renderer_detail: null,
-                renderer_detail_config: null,
+                detail_display: 2,
                 options: null,
                 vo_utype: null,
                 vo_ucd: null,
@@ -197,7 +188,8 @@ describe('[Instance][Search][Component][Output] OutputByCategoryComponent', () =
                 vo_datatype: null,
                 vo_size: null,
                 id_criteria_family: null,
-                id_output_category: null
+                id_output_category: null,
+                id_detail_output_category: null
             }
         ];
         component.outputList = [1, 2];
diff --git a/client/src/app/instance/search/components/output/output-by-family.component.spec.ts b/client/src/app/instance/search/components/output/output-by-family.component.spec.ts
index 1b9d0df47b5c8003ead847e10c9aa1eb0b3f9218..00b112324ce32f4266b172804219afd1e03dd8b8 100644
--- a/client/src/app/instance/search/components/output/output-by-family.component.spec.ts
+++ b/client/src/app/instance/search/components/output/output-by-family.component.spec.ts
@@ -91,10 +91,7 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 renderer_config: null,
                 order_by: true,
                 archive: false,
-                detail: true,
-                display_detail: 1,
-                renderer_detail: null,
-                renderer_detail_config: null,
+                detail_display: 1,
                 options: null,
                 vo_utype: null,
                 vo_ucd: null,
@@ -103,7 +100,8 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 vo_datatype: null,
                 vo_size: null,
                 id_criteria_family: null,
-                id_output_category: 2
+                id_output_category: 2,
+                id_detail_output_category: null
             },
             {
                 id: 1,
@@ -127,10 +125,7 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 renderer_config: null,
                 order_by: true,
                 archive: false,
-                detail: true,
-                display_detail: 2,
-                renderer_detail: null,
-                renderer_detail_config: null,
+                detail_display: 2,
                 options: null,
                 vo_utype: null,
                 vo_ucd: null,
@@ -139,7 +134,8 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 vo_datatype: null,
                 vo_size: null,
                 id_criteria_family: null,
-                id_output_category: 1
+                id_output_category: 1,
+                id_detail_output_category: null
             }
         ];
         expect(component.getAttributeByCategory(1).length).toBe(1);
@@ -170,10 +166,7 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 renderer_config: null,
                 order_by: true,
                 archive: false,
-                detail: true,
-                display_detail: 1,
-                renderer_detail: null,
-                renderer_detail_config: null,
+                detail_display: 1,
                 options: null,
                 vo_utype: null,
                 vo_ucd: null,
@@ -182,7 +175,8 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 vo_datatype: null,
                 vo_size: null,
                 id_criteria_family: null,
-                id_output_category: 1
+                id_output_category: 1,
+                id_detail_output_category: null
             },
             {
                 id: 1,
@@ -206,10 +200,7 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 renderer_config: null,
                 order_by: true,
                 archive: false,
-                detail: true,
-                display_detail: 2,
-                renderer_detail: null,
-                renderer_detail_config: null,
+                detail_display: 2,
                 options: null,
                 vo_utype: null,
                 vo_ucd: null,
@@ -218,7 +209,8 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 vo_datatype: null,
                 vo_size: null,
                 id_criteria_family: null,
-                id_output_category: 1
+                id_output_category: 1,
+                id_detail_output_category: null
             }
         ];
         component.outputList = [1, 2];
@@ -249,10 +241,7 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 renderer_config: null,
                 order_by: true,
                 archive: false,
-                detail: true,
-                display_detail: 1,
-                renderer_detail: null,
-                renderer_detail_config: null,
+                detail_display: 1,
                 options: null,
                 vo_utype: null,
                 vo_ucd: null,
@@ -261,7 +250,8 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 vo_datatype: null,
                 vo_size: null,
                 id_criteria_family: null,
-                id_output_category: 1
+                id_output_category: 1,
+                id_detail_output_category: null
             },
             {
                 id: 1,
@@ -285,10 +275,7 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 renderer_config: null,
                 order_by: true,
                 archive: false,
-                detail: true,
-                display_detail: 2,
-                renderer_detail: null,
-                renderer_detail_config: null,
+                detail_display: 2,
                 options: null,
                 vo_utype: null,
                 vo_ucd: null,
@@ -297,7 +284,8 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 vo_datatype: null,
                 vo_size: null,
                 id_criteria_family: null,
-                id_output_category: 1
+                id_output_category: 1,
+                id_detail_output_category: null
             }
         ];
         component.outputList = [1];
@@ -328,10 +316,7 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 renderer_config: null,
                 order_by: true,
                 archive: false,
-                detail: true,
-                display_detail: 1,
-                renderer_detail: null,
-                renderer_detail_config: null,
+                detail_display: 1,
                 options: null,
                 vo_utype: null,
                 vo_ucd: null,
@@ -340,7 +325,8 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 vo_datatype: null,
                 vo_size: null,
                 id_criteria_family: null,
-                id_output_category: 1
+                id_output_category: 1,
+                id_detail_output_category: null
             },
             {
                 id: 1,
@@ -364,10 +350,7 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 renderer_config: null,
                 order_by: true,
                 archive: false,
-                detail: true,
-                display_detail: 2,
-                renderer_detail: null,
-                renderer_detail_config: null,
+                detail_display: 2,
                 options: null,
                 vo_utype: null,
                 vo_ucd: null,
@@ -376,7 +359,8 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 vo_datatype: null,
                 vo_size: null,
                 id_criteria_family: null,
-                id_output_category: 1
+                id_output_category: 1,
+                id_detail_output_category: null
             }
         ];
         component.outputList = [];
@@ -407,10 +391,7 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 renderer_config: null,
                 order_by: true,
                 archive: false,
-                detail: true,
-                display_detail: 1,
-                renderer_detail: null,
-                renderer_detail_config: null,
+                detail_display: 1,
                 options: null,
                 vo_utype: null,
                 vo_ucd: null,
@@ -419,7 +400,8 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 vo_datatype: null,
                 vo_size: null,
                 id_criteria_family: null,
-                id_output_category: 1
+                id_output_category: 1,
+                id_detail_output_category: null
             },
             {
                 id: 1,
@@ -443,10 +425,7 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 renderer_config: null,
                 order_by: true,
                 archive: false,
-                detail: true,
-                display_detail: 2,
-                renderer_detail: null,
-                renderer_detail_config: null,
+                detail_display: 2,
                 options: null,
                 vo_utype: null,
                 vo_ucd: null,
@@ -455,7 +434,8 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 vo_datatype: null,
                 vo_size: null,
                 id_criteria_family: null,
-                id_output_category: 1
+                id_output_category: 1,
+                id_detail_output_category: null
             }
         ];
         component.outputList = [1];
@@ -486,10 +466,7 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 renderer_config: null,
                 order_by: true,
                 archive: false,
-                detail: true,
-                display_detail: 1,
-                renderer_detail: null,
-                renderer_detail_config: null,
+                detail_display: 1,
                 options: null,
                 vo_utype: null,
                 vo_ucd: null,
@@ -498,7 +475,8 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 vo_datatype: null,
                 vo_size: null,
                 id_criteria_family: null,
-                id_output_category: 1
+                id_output_category: 1,
+                id_detail_output_category: null
             },
             {
                 id: 1,
@@ -522,10 +500,7 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 renderer_config: null,
                 order_by: true,
                 archive: false,
-                detail: true,
-                display_detail: 2,
-                renderer_detail: null,
-                renderer_detail_config: null,
+                detail_display: 2,
                 options: null,
                 vo_utype: null,
                 vo_ucd: null,
@@ -534,7 +509,8 @@ describe('[Instance][Search][Component][Output] OutputByFamilyComponent', () =>
                 vo_datatype: null,
                 vo_size: null,
                 id_criteria_family: null,
-                id_output_category: 1
+                id_output_category: 1,
+                id_detail_output_category: null
             }
         ];
         const expectedOutputList = [1];
diff --git a/client/src/app/instance/search/components/result/datatable.component.spec.ts b/client/src/app/instance/search/components/result/datatable.component.spec.ts
index b10c5be17bdb795885b2b3f837dbcf881f421470..9f07d6c7c3999d222d57a9d3ec752181ddbcc12b 100644
--- a/client/src/app/instance/search/components/result/datatable.component.spec.ts
+++ b/client/src/app/instance/search/components/result/datatable.component.spec.ts
@@ -133,10 +133,7 @@ describe('[Instance][Search][Component][Result] DatatableComponent', () => {
             renderer_config: null,
             order_by: true,
             archive: false,
-            detail: true,
-            display_detail: 1,
-            renderer_detail: null,
-            renderer_detail_config: null,
+            detail_display: 1,
             options: null,
             vo_utype: null,
             vo_ucd: null,
@@ -145,7 +142,8 @@ describe('[Instance][Search][Component][Result] DatatableComponent', () => {
             vo_datatype: null,
             vo_size: null,
             id_criteria_family: 1,
-            id_output_category: 1
+            id_output_category: 1,
+            id_detail_output_category: null
         };
         expect(component.getRendererConfig(attribute)).toBeNull();
         const detailLinkRendererConfig: DetailLinkRendererConfig = {
diff --git a/client/src/app/instance/search/components/result/renderer/download-renderer.component.spec.ts b/client/src/app/instance/search/components/result/renderer/download-renderer.component.spec.ts
index 54e7be5af0a4a0aa2ae76dfcda88a656120cc959..0516b4015abd995f9304ae7962876c5a66a58bd0 100644
--- a/client/src/app/instance/search/components/result/renderer/download-renderer.component.spec.ts
+++ b/client/src/app/instance/search/components/result/renderer/download-renderer.component.spec.ts
@@ -80,10 +80,7 @@ describe('[Instance][Search][Component][Result][Renderer] DownloadRendererCompon
             } as DownloadRendererConfig,
             order_by: true,
             archive: false,
-            detail: true,
-            display_detail: 2,
-            renderer_detail: null,
-            renderer_detail_config: null,
+            detail_display: 2,
             options: [
                 { label: 'Three', value: 'three', display: 3 },
                 { label: 'One', value: 'one', display: 1 },
@@ -96,7 +93,8 @@ describe('[Instance][Search][Component][Result][Renderer] DownloadRendererCompon
             vo_datatype: null,
             vo_size: null,
             id_criteria_family: null,
-            id_output_category: 2
+            id_output_category: 2,
+            id_detail_output_category: null
         };
 
         component.attribute = attribute;
diff --git a/client/src/app/instance/search/components/result/renderer/image-renderer.component.spec.ts b/client/src/app/instance/search/components/result/renderer/image-renderer.component.spec.ts
index e240308ad06a85de0f056744c9b57a5f7277e235..4f833cf3cc525f1bb42becbfc9c91a38eb623b10 100644
--- a/client/src/app/instance/search/components/result/renderer/image-renderer.component.spec.ts
+++ b/client/src/app/instance/search/components/result/renderer/image-renderer.component.spec.ts
@@ -54,10 +54,7 @@ describe('[Instance][Search][Component][Result][Renderer] ImageRendererComponent
             } as ImageRendererConfig,
             order_by: true,
             archive: false,
-            detail: true,
-            display_detail: 2,
-            renderer_detail: null,
-            renderer_detail_config: null,
+            detail_display: 2,
             options: [
                 { label: 'Three', value: 'three', display: 3 },
                 { label: 'One', value: 'one', display: 1 },
@@ -70,7 +67,8 @@ describe('[Instance][Search][Component][Result][Renderer] ImageRendererComponent
             vo_datatype: null,
             vo_size: null,
             id_criteria_family: null,
-            id_output_category: 2
+            id_output_category: 2,
+            id_detail_output_category: null
         };
 
         const dataset = {
diff --git a/client/src/app/instance/search/components/result/renderer/link-renderer.component.spec.ts b/client/src/app/instance/search/components/result/renderer/link-renderer.component.spec.ts
index dcd3aae95c735fcb1a1f80915a8c6045e396cf2d..752d6be14174f9add83bac7486b8be3e7015b801 100644
--- a/client/src/app/instance/search/components/result/renderer/link-renderer.component.spec.ts
+++ b/client/src/app/instance/search/components/result/renderer/link-renderer.component.spec.ts
@@ -49,10 +49,7 @@ describe('[Instance][Search][Component][Result][Renderer] LinkRendererComponent'
             } as LinkRendererConfig,
             order_by: true,
             archive: false,
-            detail: true,
-            display_detail: 2,
-            renderer_detail: null,
-            renderer_detail_config: null,
+            detail_display: 2,
             options: [
                 { label: 'Three', value: 'three', display: 3 },
                 { label: 'One', value: 'one', display: 1 },
@@ -65,7 +62,8 @@ describe('[Instance][Search][Component][Result][Renderer] LinkRendererComponent'
             vo_datatype: null,
             vo_size: null,
             id_criteria_family: null,
-            id_output_category: 2
+            id_output_category: 2,
+            id_detail_output_category: null
         };
 
         component.attribute = attribute;
@@ -103,10 +101,7 @@ describe('[Instance][Search][Component][Result][Renderer] LinkRendererComponent'
             } as LinkRendererConfig,
             order_by: true,
             archive: false,
-            detail: true,
-            display_detail: 2,
-            renderer_detail: null,
-            renderer_detail_config: null,
+            detail_display: 2,
             options: [
                 { label: 'Three', value: 'three', display: 3 },
                 { label: 'One', value: 'one', display: 1 },
@@ -119,7 +114,8 @@ describe('[Instance][Search][Component][Result][Renderer] LinkRendererComponent'
             vo_datatype: null,
             vo_size: null,
             id_criteria_family: null,
-            id_output_category: 2
+            id_output_category: 2,
+            id_detail_output_category: null
         };
 
         component.attribute = attribute;
diff --git a/client/src/app/instance/search/containers/detail.component.html b/client/src/app/instance/search/containers/detail.component.html
deleted file mode 100644
index 3ceca60ed2a736979571553b040484ca49c9861d..0000000000000000000000000000000000000000
--- a/client/src/app/instance/search/containers/detail.component.html
+++ /dev/null
@@ -1,40 +0,0 @@
-<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>
-    <ng-container *ngIf="(attributeListIsLoaded | async) 
-        && (outputFamilyListIsLoaded | async) 
-        && (outputCategoryListIsLoaded | async) 
-        && (objectIsLoaded | async)" [ngSwitch]="getDetailComponent(attributeList | async)">
-        <app-spectra-object *ngSwitchCase="'spectra'"
-            [datasetSelected]="datasetSelected | async"
-            [outputFamilyList]="outputFamilyList | async"
-            [outputCategoryList]="outputCategoryList | async"
-            [attributeList]="attributeList | async | sortByDetailDisplay"
-            [object]="object | async"
-            [spectraCSV]="spectraCSV | async"
-            [spectraIsLoading]="spectraIsLoading | async"
-            [spectraIsLoaded]="spectraIsLoaded | async"
-            (getSpectraCSV)="getSpectraCSV($event)">
-        </app-spectra-object>
-        <app-default-object *ngSwitchCase="'default'"
-            [datasetSelected]="datasetSelected | async"
-            [outputFamilyList]="outputFamilyList | async"
-            [outputCategoryList]="outputCategoryList | async"
-            [attributeList]="attributeList | async | sortByDetailDisplay"
-            [object]="object | async">
-        </app-default-object>
-        <div *ngSwitchDefault>
-            Detail component <span class="font-weight-bold">{{ getDetailComponent(attributeList | async) }}</span> not supported !
-        </div>
-    </ng-container>
-</div>
diff --git a/client/src/app/instance/search/detail/components/detail-content.component.html b/client/src/app/instance/search/detail/components/detail-content.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..b0961d8fb18ce48ec3ba35de925aa73c81ba1435
--- /dev/null
+++ b/client/src/app/instance/search/detail/components/detail-content.component.html
@@ -0,0 +1,5 @@
+<ngx-dynamic-hooks 
+    [content]="detailConfig.content"
+    [parsers]="getParsers()"
+    [context]="getContext()">
+</ngx-dynamic-hooks>
diff --git a/client/src/app/instance/search/detail/components/detail-content.component.ts b/client/src/app/instance/search/detail/components/detail-content.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ca7dfd6084994540ef89324a035ded66e5620c1f
--- /dev/null
+++ b/client/src/app/instance/search/detail/components/detail-content.component.ts
@@ -0,0 +1,49 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
+
+import { DetailConfig, Dataset, Attribute, OutputFamily, OutputCategory } from 'src/app/metamodel/models';
+import { globalParsers } from 'src/app/shared/dynamic-content';
+import { componentParsers } from '../dynamic-content';
+
+/**
+ * @class
+ * @classdesc Detail content component.
+ */
+@Component({
+    selector: 'app-detail-content',
+    templateUrl: 'detail-content.component.html',
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class DetailContentComponent {
+    @Input() detailConfig: DetailConfig;
+    @Input() object: any;
+    @Input() datasetName: string;
+    @Input() attributeList: Attribute[];
+    @Input() outputFamilyList: OutputFamily[];
+    @Input() outputCategoryList: OutputCategory[];
+
+    getParsers() {
+        return [
+            ...globalParsers,
+            ...componentParsers
+        ];
+    }
+
+    getContext() {
+        return {
+            object: this.object,
+            datasetName: this.datasetName,
+            attributeList: this.attributeList,
+            outputFamilyList: this.outputFamilyList,
+            outputCategoryList: this.outputCategoryList
+        };
+    }
+}
diff --git a/client/src/app/instance/dynamic-content/components/index.ts b/client/src/app/instance/search/detail/components/index.ts
similarity index 74%
rename from client/src/app/instance/dynamic-content/components/index.ts
rename to client/src/app/instance/search/detail/components/index.ts
index b3594ebb9a4ea93fe6a85fd9f3df57b631d27e0d..0dcbf58e5e5f4fdf19dfc73aac0bc8039807a695 100644
--- a/client/src/app/instance/dynamic-content/components/index.ts
+++ b/client/src/app/instance/search/detail/components/index.ts
@@ -7,8 +7,8 @@
  * file that was distributed with this source code.
  */
 
-import { DatatableComponent } from './datatable.component';
+import { DetailContentComponent } from './detail-content.component';
 
 export const dummiesComponents = [
-    DatatableComponent
+    DetailContentComponent
 ];
diff --git a/client/src/app/instance/search/detail/containers/detail.component.html b/client/src/app/instance/search/detail/containers/detail.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..9c0a7eb8e8cc8c977e3eb39e08d854bb28642503
--- /dev/null
+++ b/client/src/app/instance/search/detail/containers/detail.component.html
@@ -0,0 +1,29 @@
+<div class="container-fluid">
+    <div *ngIf="!(pristine | async)" class="row mt-2 mb-2 justify-content-between">
+        <div class="col">
+            <a routerLink="/instance/{{ instanceSelected | async }}/search/result/{{ datasetSelected | async }}" [queryParams]="queryParams | async" class="btn btn-outline-secondary">
+                <span class="fa fa-backward"></span> Back to search results
+            </a>
+        </div>
+    </div>
+    <app-spinner *ngIf="(detailConfigIsLoading | async)
+        || (attributeListIsLoading | async)
+        || (outputFamilyListIsLoading | async)
+        || (outputCategoryListIsLoading | async)
+        || (objectIsLoading | async)">
+    </app-spinner>
+    <ng-container *ngIf="(detailConfigIsLoaded | async)
+        && (attributeListIsLoaded | async) 
+        && (outputFamilyListIsLoaded | async) 
+        && (outputCategoryListIsLoaded | async) 
+        && (objectIsLoaded | async)">
+        <app-detail-content 
+            [detailConfig]="detailConfig | async"
+            [object]="object | async"
+            [datasetName]="datasetSelected | async"
+            [attributeList]="attributeList | async"
+            [outputFamilyList]="outputFamilyList | async"
+            [outputCategoryList]="outputCategoryList | async">
+        </app-detail-content>
+    </ng-container>
+</div>
diff --git a/client/src/app/instance/search/containers/detail.component.spec.ts b/client/src/app/instance/search/detail/containers/detail.component.spec.ts
similarity index 53%
rename from client/src/app/instance/search/containers/detail.component.spec.ts
rename to client/src/app/instance/search/detail/containers/detail.component.spec.ts
index eabd6e35c007e264f019a083d8b618dbe86916da..acfea7bc7bd8a71a33c6a72469aa17ab001ecdeb 100644
--- a/client/src/app/instance/search/containers/detail.component.spec.ts
+++ b/client/src/app/instance/search/detail/containers/detail.component.spec.ts
@@ -1,59 +1,43 @@
 import { Component, InjectionToken, Input } from '@angular/core';
-import { ComponentFixture, TestBed, inject, waitForAsync } from '@angular/core/testing';
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
 import { Location } from '@angular/common';
 
 import { provideMockStore, MockStore } from '@ngrx/store/testing';
 import { of } from 'rxjs';
 
 import { DetailComponent } from './detail.component';
-import { Attribute, OutputCategory, OutputFamily } from 'src/app/metamodel/models';
-import { SortByDetailDisplay } from '../pipes/sort-by-detail-display';
-import { ATTRIBUTE_LIST } from 'src/test-data';
+import { Attribute, DetailConfig, OutputCategory, OutputFamily } from 'src/app/metamodel/models';
 import * as detailActions from 'src/app/instance/store/actions/detail.actions';
+import * as detailConfigActions from 'src/app/metamodel/actions/detail-config.actions';
 import * as searchActions from 'src/app/instance/store/actions/search.actions';
 
 describe('[Instance][Search][Container] DetailComponent', () => {
     @Component({ selector: 'app-spinner', template: '' })
     class SpinnerStubComponent { }
 
-    @Component({ selector: 'app-spectra-object', template: '' })
-    class SpectraObjectStubComponent {
-        @Input() datasetSelected: string;
-        @Input() outputFamilyList: OutputFamily[];
-        @Input() outputCategoryList: OutputCategory[];
-        @Input() attributeList: Attribute[];
+    @Component({ selector: 'app-detail-content ', template: '' })
+    class DetailContentStubComponent {
+        @Input() detailConfig: DetailConfig
         @Input() object: any;
-        @Input() spectraIsLoading: boolean;
-        @Input() spectraIsLoaded: boolean;
-        @Input() spectraCSV: string;
-    }
-
-    @Component({ selector: 'app-default-object', template: '' })
-    class DefaultObjectStubComponent {
-        @Input() datasetSelected: string;
+        @Input() datasetName: string;
+        @Input() attributeList: Attribute[];
         @Input() outputFamilyList: OutputFamily[];
         @Input() outputCategoryList: OutputCategory[];
-        @Input() attributeList: Attribute[];
-        @Input() object: any;
     }
 
     let component: DetailComponent;
     let fixture: ComponentFixture<DetailComponent>;
     let store: MockStore;
-    const LOCATION_TOKEN = new InjectionToken<Location>('Window location object');
 
     beforeEach(waitForAsync(() => {
         TestBed.configureTestingModule({
             declarations: [
                 DetailComponent,
                 SpinnerStubComponent,
-                SpectraObjectStubComponent,
-                DefaultObjectStubComponent,
-                SortByDetailDisplay
+                DetailContentStubComponent
             ],
             providers: [
-                provideMockStore({ }),
-                { provide: LOCATION_TOKEN, useValue: window.history }
+                provideMockStore({ })
             ]
         });
         fixture = TestBed.createComponent(DetailComponent);
@@ -70,28 +54,15 @@ describe('[Instance][Search][Container] DetailComponent', () => {
         const spy = jest.spyOn(store, 'dispatch');
         component.ngOnInit();
         Promise.resolve(null).then(function() {
-            expect(spy).toHaveBeenCalledTimes(3);
+            expect(spy).toHaveBeenCalledTimes(4);
             expect(spy).toHaveBeenCalledWith(searchActions.initSearch());
             expect(spy).toHaveBeenCalledWith(searchActions.loadDefaultFormParameters());
             expect(spy).toHaveBeenCalledWith(detailActions.retrieveObject());
+            expect(spy).toHaveBeenCalledWith(detailConfigActions.loadDetailConfig());
             done();
         });
     });
 
-    it ('#goBackToResult() should go to the previous location', inject([LOCATION_TOKEN], (location: Location) => {
-        jest.spyOn(location, 'back');
-        component.goBackToResult();
-        expect(location.back).toHaveBeenCalledTimes(1);
-    }));
-
-    it('#getSpectraCSV() should dispatch RetrieveSpectraAction', () => {
-        // const retrieveSpectraAction = new detailActions.retrieveSpectra({ filename: 'toto' });
-        const spy = jest.spyOn(store, 'dispatch');
-        component.getSpectraCSV('toto');
-        expect(spy).toHaveBeenCalledTimes(1);
-        expect(spy).toHaveBeenCalledWith(detailActions.retrieveSpectra({ filename: 'toto' }));
-    });
-
     it('#ngOnDestroy() should unsubscribe from attributeListIsLoadedSubscription', () => {
         component.attributeListIsLoadedSubscription = of().subscribe();
         const spy = jest.spyOn(component.attributeListIsLoadedSubscription, 'unsubscribe');
diff --git a/client/src/app/instance/search/containers/detail.component.ts b/client/src/app/instance/search/detail/containers/detail.component.ts
similarity index 78%
rename from client/src/app/instance/search/containers/detail.component.ts
rename to client/src/app/instance/search/detail/containers/detail.component.ts
index 78d4b5f69732f88b4fa2cff913292cf63a78dc27..7e354d22926ce2574faebca4e53b688021e022de 100644
--- a/client/src/app/instance/search/containers/detail.component.ts
+++ b/client/src/app/instance/search/detail/containers/detail.component.ts
@@ -13,15 +13,19 @@ import { Location } from '@angular/common';
 import { Store } from '@ngrx/store';
 import { Observable, Subscription } from 'rxjs';
 
-import { Attribute, OutputCategory, OutputFamily, DetailLinkRendererConfig } from 'src/app/metamodel/models';
+import { Attribute, OutputCategory, OutputFamily, DetailConfig } from 'src/app/metamodel/models';
+import { SearchQueryParams } from 'src/app/instance/store/models';
 import * as detailActions from 'src/app/instance/store/actions/detail.actions';
 import * as detailSelector from 'src/app/instance/store/selectors/detail.selector';
+import * as instanceSelector from 'src/app/metamodel/selectors/instance.selector';
 import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector';
 import * as searchSelector from 'src/app/instance/store/selectors/search.selector';
 import * as outputFamilySelector from 'src/app/metamodel/selectors/output-family.selector';
 import * as outputCategorySelector from 'src/app/metamodel/selectors/output-category.selector';
 import * as attributeSelector from 'src/app/metamodel/selectors/attribute.selector';
 import * as searchActions from 'src/app/instance/store/actions/search.actions';
+import * as detailConfigActions from 'src/app/metamodel/actions/detail-config.actions';
+import * as detailConfigSelector from 'src/app/metamodel/selectors/detail-config.selector';
 
 /**
  * @class
@@ -35,8 +39,12 @@ import * as searchActions from 'src/app/instance/store/actions/search.actions';
     templateUrl: 'detail.component.html'
 })
 export class DetailComponent implements OnInit, OnDestroy {
+    public instanceSelected: Observable<string>;
     public datasetSelected: Observable<string>;
     public pristine: Observable<boolean>;
+    public detailConfig: Observable<DetailConfig>;
+    public detailConfigIsLoading: Observable<boolean>;
+    public detailConfigIsLoaded: Observable<boolean>;
     public attributeList: Observable<Attribute[]>;
     public attributeListIsLoading: Observable<boolean>;
     public attributeListIsLoaded: Observable<boolean>;
@@ -49,15 +57,17 @@ export class DetailComponent implements OnInit, OnDestroy {
     public object: Observable<any>;
     public objectIsLoading: Observable<boolean>;
     public objectIsLoaded: Observable<boolean>;
-    public spectraCSV: Observable<string>;
-    public spectraIsLoading: Observable<boolean>;
-    public spectraIsLoaded: Observable<boolean>;
+    public queryParams: Observable<SearchQueryParams>;
 
     public attributeListIsLoadedSubscription: Subscription;
 
     constructor(private location: Location, private store: Store<{ }>) {
+        this.instanceSelected = store.select(instanceSelector.selectInstanceNameByRoute);
         this.datasetSelected = store.select(datasetSelector.selectDatasetNameByRoute);
         this.pristine = store.select(searchSelector.selectPristine);
+        this.detailConfig = store.select(detailConfigSelector.selectDetailConfig);
+        this.detailConfigIsLoading = store.select(detailConfigSelector.selectDetailConfigIsLoading);
+        this.detailConfigIsLoaded = store.select(detailConfigSelector.selectDetailConfigIsLoaded);
         this.attributeList = store.select(attributeSelector.selectAllAttributes);
         this.attributeListIsLoading = store.select(attributeSelector.selectAttributeListIsLoading);
         this.attributeListIsLoaded = store.select(attributeSelector.selectAttributeListIsLoaded);
@@ -70,9 +80,7 @@ export class DetailComponent implements OnInit, OnDestroy {
         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);
+        this.queryParams = this.store.select(searchSelector.selectQueryParams);
     }
 
     ngOnInit(): void {
@@ -85,27 +93,7 @@ export class DetailComponent implements OnInit, OnDestroy {
                 Promise.resolve(null).then(() => this.store.dispatch(detailActions.retrieveObject()));
             }
         });
-    }
-
-    /**
-     * Gets back to result page.
-     */
-    goBackToResult(): void {
-        this.location.back();
-    }
-
-    getDetailComponent(attributeList: Attribute[]) {
-        const attribute = attributeList.find(attribute => attribute.renderer === 'detail-link').renderer_config as DetailLinkRendererConfig;
-        return attribute.component;
-    }
-
-    /**
-     * Dispatches action to retrieve spectra file.
-     *
-     * @param  {string} spectraFile - The spectra file name.
-     */
-    getSpectraCSV(spectraFile: string): void {
-        this.store.dispatch(detailActions.retrieveSpectra({ filename: spectraFile }));
+        Promise.resolve(null).then(() => this.store.dispatch(detailConfigActions.loadDetailConfig()));
     }
 
     /**
diff --git a/client/src/app/instance/search/detail/detail-routing.module.ts b/client/src/app/instance/search/detail/detail-routing.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0b32f4090e5baf2dfedf5abf50f6e8a391be6add
--- /dev/null
+++ b/client/src/app/instance/search/detail/detail-routing.module.ts
@@ -0,0 +1,32 @@
+/**
+ * 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 { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { DetailComponent } from './containers/detail.component';
+
+const routes: Routes = [
+    { path: '', redirectTo: ':dname/:id', pathMatch: 'full' },
+    { path: ':dname/:id', component: DetailComponent }
+];
+
+/**
+ * @class
+ * @classdesc Search routing module.
+ */
+ @NgModule({
+    imports: [RouterModule.forChild(routes)],
+    exports: [RouterModule]
+})
+export class DetailRoutingModule { }
+
+export const routedComponents = [
+    DetailComponent
+];
diff --git a/client/src/app/instance/search/detail/detail.module.ts b/client/src/app/instance/search/detail/detail.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f272f91753733e577c6db6d053b75579258ccbda
--- /dev/null
+++ b/client/src/app/instance/search/detail/detail.module.ts
@@ -0,0 +1,38 @@
+/**
+ * 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 { NgModule } from '@angular/core';
+
+import { SharedModule } from 'src/app/shared/shared.module';
+import { DetailRoutingModule, routedComponents } from './detail-routing.module';
+import { dummiesComponents } from './components';
+import { hookParsers, dynamicComponents } from './dynamic-content';
+
+/**
+ * @class
+ * @classdesc Search module.
+ */
+@NgModule({
+    imports: [
+        SharedModule,
+        DetailRoutingModule
+    ],
+    declarations: [
+        routedComponents,
+        dummiesComponents,
+        dynamicComponents
+    ],
+    entryComponents: [
+        dynamicComponents
+    ],
+    providers: [
+        hookParsers
+    ]
+})
+export class DetailModule { }
diff --git a/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-object.component.html b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-object.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..5c1adc90c23c6bec1e06f2c4e4499b6319cea7e0
--- /dev/null
+++ b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-object.component.html
@@ -0,0 +1,41 @@
+<!-- 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 getOutputCategoryListByFamily(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 getAttributeListByOutputCategory(category.id)" class="row pb-2">
+                    <div class="col-5 font-weight-bold">{{ attribute.form_label }}</div>
+                    <div class="col">{{ object[attribute.label] }}</div>
+                </div>
+            </accordion-group>
+        </accordion>
+    </accordion-group>
+</accordion>
\ No newline at end of file
diff --git a/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-object.component.ts b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-object.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1c029c11508c6dbce4d80c0a89fb24e29e900430
--- /dev/null
+++ b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-object.component.ts
@@ -0,0 +1,36 @@
+/**
+ * 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';
+
+/**
+ * @class
+ * @classdesc Display object component.
+ */
+@Component({
+    selector: 'app-display-object',
+    templateUrl: 'display-object.component.html',
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class DisplayObjectComponent {
+    @Input() object: any;
+    @Input() attributeList: Attribute[];
+    @Input() outputFamilyList: OutputFamily[];
+    @Input() outputCategoryList: OutputCategory[];
+
+    getOutputCategoryListByFamily(idOutputFamily: number): OutputCategory[] {
+        return this.outputCategoryList.filter(outputCategory => outputCategory.id_output_family === idOutputFamily);
+    }
+
+    getAttributeListByOutputCategory(idOutputCategory: number): Attribute[] {
+        return this.attributeList.filter(attribute => attribute.id_detail_output_category === idOutputCategory);
+    }
+}
diff --git a/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-ra-dec.component.html b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-ra-dec.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..b779cc47211c89b6f36fd14b0fd435e532d3ec9d
--- /dev/null
+++ b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-ra-dec.component.html
@@ -0,0 +1,11 @@
+<table class="table mb-1" aria-describedby="Object coordinates">
+    <tr>
+        <th scope="col">Alpha</th>
+        <th scope="col">Delta</th>
+        <th scope="col" class="text-center" rowspan="2"><img src="assets/cesam_anis80.png" alt="CeSAM logo" /></th>
+    </tr>
+    <tr>
+        <td>{{ object[getAttributeRa().label] }}</td>
+        <td>{{ object[getAttributeDec().label] }}</td>
+    </tr>
+</table>
diff --git a/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-ra-dec.component.ts b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-ra-dec.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4715be9b5bc9e0fe6d765d52d4721117b1544da0
--- /dev/null
+++ b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-ra-dec.component.ts
@@ -0,0 +1,36 @@
+/**
+ * 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 } from 'src/app/metamodel/models';
+
+/**
+ * @class
+ * @classdesc Display object component.
+ */
+@Component({
+    selector: 'app-display-ra-dec',
+    templateUrl: 'display-ra-dec.component.html',
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class DisplayRaDecComponent {
+    @Input() object: any;
+    @Input() attributeList: Attribute[];
+    @Input() attributeRaId: number;
+    @Input() attributeDecId: number;
+
+    getAttributeRa() {
+        return this.attributeList.find(attribute => attribute.id === this.attributeRaId);
+    }
+
+    getAttributeDec() {
+        return this.attributeList.find(attribute => attribute.id === this.attributeDecId);
+    }
+}
diff --git a/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-spectra.component.html b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-spectra.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..f908a1e04a212c8fe72ca15abc8601591eabbf39
--- /dev/null
+++ b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-spectra.component.html
@@ -0,0 +1 @@
+<app-spectra-graph *ngIf="spectraCSV | async as data" [z]="getZ()" [spectraCSV]="data"></app-spectra-graph>
diff --git a/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-spectra.component.ts b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-spectra.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fcf7ffedf84bdb92861c0fe467e887250b31f964
--- /dev/null
+++ b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-spectra.component.ts
@@ -0,0 +1,47 @@
+/**
+ * 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 { HttpClient } from '@angular/common/http';
+
+import { Observable } from 'rxjs';
+
+import { Attribute } from 'src/app/metamodel/models';
+import { AppConfigService } from 'src/app/app-config.service';
+
+/**
+ * @class
+ * @classdesc Display spectra component.
+ */
+@Component({
+    selector: 'app-display-spectra',
+    templateUrl: 'display-spectra.component.html',
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class DisplaySpectraComponent {
+    @Input() object: any;
+    @Input() datasetName: string;
+    @Input() attributeList: Attribute[];
+    @Input() attributeSpectraId: number;
+    @Input() attributeZId: number;
+
+    spectraCSV: Observable<string>;
+
+    constructor(private http: HttpClient, private config: AppConfigService) { }
+
+    ngOnInit() {
+        const spectraFile = this.object[this.attributeList.find(attribute => attribute.id === this.attributeSpectraId).label];
+        this.spectraCSV = this.http.get(`${this.config.servicesUrl}/spectra-to-csv/${this.datasetName}?filename=${spectraFile}`, { responseType: 'text' });
+    }
+
+    getZ() {
+        const z = this.object[this.attributeList.find(attribute => attribute.id === this.attributeZId).label];
+        return +z;
+    }
+}
diff --git a/client/src/app/instance/search/detail/dynamic-content/dynamic-components/index.ts b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8c84be9b710bc658c127711e99eadc76678a664e
--- /dev/null
+++ b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/index.ts
@@ -0,0 +1,11 @@
+import { DisplayObjectComponent } from './display-object.component';
+import { DisplayRaDecComponent } from './display-ra-dec.component';
+import { DisplaySpectraComponent } from './display-spectra.component';
+import { SpectraGraphComponent } from './spectra-graph/spectra-graph.component';
+
+export const dynamicComponents = [
+    DisplayObjectComponent,
+    DisplayRaDecComponent,
+    DisplaySpectraComponent,
+    SpectraGraphComponent
+];
diff --git a/client/src/app/instance/search/components/detail/spectra/graph/point.ts b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/spectra-graph/point.ts
similarity index 100%
rename from client/src/app/instance/search/components/detail/spectra/graph/point.ts
rename to client/src/app/instance/search/detail/dynamic-content/dynamic-components/spectra-graph/point.ts
diff --git a/client/src/app/instance/search/components/detail/spectra/graph/rays.ts b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/spectra-graph/rays.ts
similarity index 100%
rename from client/src/app/instance/search/components/detail/spectra/graph/rays.ts
rename to client/src/app/instance/search/detail/dynamic-content/dynamic-components/spectra-graph/rays.ts
diff --git a/client/src/app/instance/search/components/detail/spectra/graph/spectra-graph.component.html b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/spectra-graph/spectra-graph.component.html
similarity index 100%
rename from client/src/app/instance/search/components/detail/spectra/graph/spectra-graph.component.html
rename to client/src/app/instance/search/detail/dynamic-content/dynamic-components/spectra-graph/spectra-graph.component.html
diff --git a/client/src/app/instance/search/components/detail/spectra/graph/spectra-graph.component.scss b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/spectra-graph/spectra-graph.component.scss
similarity index 100%
rename from client/src/app/instance/search/components/detail/spectra/graph/spectra-graph.component.scss
rename to client/src/app/instance/search/detail/dynamic-content/dynamic-components/spectra-graph/spectra-graph.component.scss
diff --git a/client/src/app/instance/search/components/detail/spectra/graph/spectra-graph.component.ts b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/spectra-graph/spectra-graph.component.ts
similarity index 100%
rename from client/src/app/instance/search/components/detail/spectra/graph/spectra-graph.component.ts
rename to client/src/app/instance/search/detail/dynamic-content/dynamic-components/spectra-graph/spectra-graph.component.ts
diff --git a/client/src/app/instance/search/components/detail/spectra/graph/spectra-type.ts b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/spectra-graph/spectra-type.ts
similarity index 100%
rename from client/src/app/instance/search/components/detail/spectra/graph/spectra-type.ts
rename to client/src/app/instance/search/detail/dynamic-content/dynamic-components/spectra-graph/spectra-type.ts
diff --git a/client/src/app/instance/search/detail/dynamic-content/index.ts b/client/src/app/instance/search/detail/dynamic-content/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a8a58f220117ec5118c9ca18d1f0e91af6ee7015
--- /dev/null
+++ b/client/src/app/instance/search/detail/dynamic-content/index.ts
@@ -0,0 +1,14 @@
+import { HookParserEntry } from 'ngx-dynamic-hooks';
+
+import { hookParsers } from './parsers';
+import { dynamicComponents } from './dynamic-components';
+
+export const componentParsers: Array<HookParserEntry> = [
+    ...hookParsers,
+    ...dynamicComponents.map(component => {
+        return { component };
+    })
+];
+
+export { hookParsers } from './parsers';
+export { dynamicComponents } from './dynamic-components';
diff --git a/client/src/app/instance/search/detail/dynamic-content/parsers/index.ts b/client/src/app/instance/search/detail/dynamic-content/parsers/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..552c5a60f42b85a8d485439c9086f2c80778140f
--- /dev/null
+++ b/client/src/app/instance/search/detail/dynamic-content/parsers/index.ts
@@ -0,0 +1,3 @@
+export const hookParsers = [
+    
+];
diff --git a/client/src/app/instance/search/pipes/sort-by-criteria-display.pipe.spec.ts b/client/src/app/instance/search/pipes/sort-by-criteria-display.pipe.spec.ts
index 4d06e2739ddb3405873b53bfc2313a6d78409dff..7cc6dc72b156e83c834f16e1fc0aa95f3e11816c 100644
--- a/client/src/app/instance/search/pipes/sort-by-criteria-display.pipe.spec.ts
+++ b/client/src/app/instance/search/pipes/sort-by-criteria-display.pipe.spec.ts
@@ -28,10 +28,7 @@ describe('[Instance][Search][Pipe] SortByCriteriaDisplayPipe', () => {
                 renderer_config: null,
                 order_by: true,
                 archive: false,
-                detail: true,
-                display_detail: 1,
-                renderer_detail: null,
-                renderer_detail_config: null,
+                detail_display: 1,
                 options: null,
                 vo_utype: null,
                 vo_ucd: null,
@@ -40,7 +37,8 @@ describe('[Instance][Search][Pipe] SortByCriteriaDisplayPipe', () => {
                 vo_datatype: null,
                 vo_size: null,
                 id_criteria_family: null,
-                id_output_category: null
+                id_output_category: null,
+                id_detail_output_category: null
             },
             {
                 id: 1,
@@ -64,10 +62,7 @@ describe('[Instance][Search][Pipe] SortByCriteriaDisplayPipe', () => {
                 renderer_config: null,
                 order_by: true,
                 archive: false,
-                detail: true,
-                display_detail: 2,
-                renderer_detail: null,
-                renderer_detail_config: null,
+                detail_display: 2,
                 options: null,
                 vo_utype: null,
                 vo_ucd: null,
@@ -76,7 +71,8 @@ describe('[Instance][Search][Pipe] SortByCriteriaDisplayPipe', () => {
                 vo_datatype: null,
                 vo_size: null,
                 id_criteria_family: null,
-                id_output_category: null
+                id_output_category: null,
+                id_detail_output_category: null
             }
         ];
         expect(pipe.transform(attributeList)[0].id).toBe(1);
diff --git a/client/src/app/instance/search/pipes/sort-by-detail-display.spec.ts b/client/src/app/instance/search/pipes/sort-by-detail-display.spec.ts
index f3cb99f44e266086165f6ae66b121bea401865f5..708cd1f38bb3423de88148397cb74dee1489dce4 100644
--- a/client/src/app/instance/search/pipes/sort-by-detail-display.spec.ts
+++ b/client/src/app/instance/search/pipes/sort-by-detail-display.spec.ts
@@ -28,10 +28,7 @@ describe('[Instance][Search][Pipe] SortByDetailDisplay', () => {
                 renderer_config: null,
                 order_by: true,
                 archive: false,
-                detail: true,
-                display_detail: 2,
-                renderer_detail: null,
-                renderer_detail_config: null,
+                detail_display: 2,
                 options: [
                     { label: 'Three', value: 'three', display: 3 },
                     { label: 'One', value: 'one', display: 1 },
@@ -44,7 +41,8 @@ describe('[Instance][Search][Pipe] SortByDetailDisplay', () => {
                 vo_datatype: null,
                 vo_size: null,
                 id_criteria_family: null,
-                id_output_category: null
+                id_output_category: null,
+                id_detail_output_category: null
             },
             {
                 id: 2,
@@ -68,10 +66,7 @@ describe('[Instance][Search][Pipe] SortByDetailDisplay', () => {
                 renderer_config: null,
                 order_by: true,
                 archive: false,
-                detail: true,
-                display_detail: 1,
-                renderer_detail: null,
-                renderer_detail_config: null,
+                detail_display: 1,
                 options: [
                     { label: 'Three', value: 'three', display: 3 },
                     { label: 'One', value: 'one', display: 1 },
@@ -85,6 +80,7 @@ describe('[Instance][Search][Pipe] SortByDetailDisplay', () => {
                 vo_size: null,
                 id_criteria_family: null,
                 id_output_category: null,
+                id_detail_output_category: null
             }
         ];
         expect(pipe.transform(attributeList)[0].id).toBe(2);
diff --git a/client/src/app/instance/search/pipes/sort-by-detail-display.ts b/client/src/app/instance/search/pipes/sort-by-detail-display.ts
index dd17a7ca46d072c91e36aa534811d4ae89a2ada4..2887500ac544ce036e7f9513a23b2ad085b7489c 100644
--- a/client/src/app/instance/search/pipes/sort-by-detail-display.ts
+++ b/client/src/app/instance/search/pipes/sort-by-detail-display.ts
@@ -14,6 +14,6 @@ import { Attribute } from 'src/app/metamodel/models';
 @Pipe({name: 'sortByDetailDisplay'})
 export class SortByDetailDisplay implements PipeTransform {
     transform(attributeList: Attribute[]): Attribute[] {
-        return [...attributeList].sort((a: Attribute, b: Attribute) => a.display_detail - b.display_detail);
+        return [...attributeList].sort((a: Attribute, b: Attribute) => a.detail_display - b.detail_display);
     }
 }
diff --git a/client/src/app/instance/search/pipes/sort-by-output-display.pipe.spec.ts b/client/src/app/instance/search/pipes/sort-by-output-display.pipe.spec.ts
index c760d9bd7f122bbf54fc65e2a470e396682668fe..f9b0dd9173e157d7988e27a504466b311161d1be 100644
--- a/client/src/app/instance/search/pipes/sort-by-output-display.pipe.spec.ts
+++ b/client/src/app/instance/search/pipes/sort-by-output-display.pipe.spec.ts
@@ -28,10 +28,7 @@ describe('[Instance][Search][Pipe] SortByOutputDisplayPipe', () => {
                 renderer_config: null,
                 order_by: true,
                 archive: false,
-                detail: true,
-                display_detail: 1,
-                renderer_detail: null,
-                renderer_detail_config: null,
+                detail_display: 1,
                 options: null,
                 vo_utype: null,
                 vo_ucd: null,
@@ -40,7 +37,8 @@ describe('[Instance][Search][Pipe] SortByOutputDisplayPipe', () => {
                 vo_datatype: null,
                 vo_size: null,
                 id_criteria_family: null,
-                id_output_category: null
+                id_output_category: null,
+                id_detail_output_category: null
             },
             {
                 id: 1,
@@ -64,10 +62,7 @@ describe('[Instance][Search][Pipe] SortByOutputDisplayPipe', () => {
                 renderer_config: null,
                 order_by: true,
                 archive: false,
-                detail: true,
-                display_detail: 2,
-                renderer_detail: null,
-                renderer_detail_config: null,
+                detail_display: 2,
                 options: null,
                 vo_utype: null,
                 vo_ucd: null,
@@ -76,7 +71,8 @@ describe('[Instance][Search][Pipe] SortByOutputDisplayPipe', () => {
                 vo_datatype: null,
                 vo_size: null,
                 id_criteria_family: null,
-                id_output_category: null
+                id_output_category: null,
+                id_detail_output_category: null
             }
         ]
         expect(pipe.transform(attributeList)[0].id).toBe(1);
diff --git a/client/src/app/instance/search/search-routing.module.ts b/client/src/app/instance/search/search-routing.module.ts
index ffc569397e828e64c8c3374a733e02e11f1d0f5e..cec8354915ef48ce8d73543afcf52a9c0326c2b0 100644
--- a/client/src/app/instance/search/search-routing.module.ts
+++ b/client/src/app/instance/search/search-routing.module.ts
@@ -15,11 +15,9 @@ import { DatasetComponent } from './containers/dataset.component';
 import { CriteriaComponent } from './containers/criteria.component';
 import { OutputComponent } from './containers/output.component';
 import { ResultComponent } from './containers/result.component';
-import { DetailComponent } from './containers/detail.component';
 import { SearchAuthGuard } from './search-auth.guard';
 
 const routes: Routes = [
-    { path: 'detail/:dname/:id', component: DetailComponent },
     {
         path: '', component: SearchComponent, children: [
             { path: '', redirectTo: 'dataset', pathMatch: 'full' },
@@ -29,7 +27,8 @@ const routes: Routes = [
             { path: 'output/:dname', canActivate: [SearchAuthGuard], component: OutputComponent },
             { path: 'result/:dname', canActivate: [SearchAuthGuard], component: ResultComponent }
         ]
-    }
+    },
+    { path: 'detail', loadChildren: () => import('./detail/detail.module').then(m => m.DetailModule) }
 ];
 
 /**
@@ -47,6 +46,5 @@ export const routedComponents = [
     DatasetComponent,
     CriteriaComponent,
     OutputComponent,
-    ResultComponent,
-    DetailComponent
+    ResultComponent
 ];
diff --git a/client/src/app/instance/search/search.module.ts b/client/src/app/instance/search/search.module.ts
index 2109154c13b7a32d7d1d041d86b59d1e95da69f4..e633fbb3bce0cf46b60004ea2d201630bda58b36 100644
--- a/client/src/app/instance/search/search.module.ts
+++ b/client/src/app/instance/search/search.module.ts
@@ -10,6 +10,7 @@
 import { NgModule } from '@angular/core';
 
 import { SharedModule } from 'src/app/shared/shared.module';
+// import { DynamicContentModule } from '../dynamic-content/dynamic-content.module';
 import { ConeSearchModule } from '../cone-search/cone-search.module';
 import { SearchRoutingModule, routedComponents } from './search-routing.module';
 import { dummiesComponents } from './components';
@@ -22,6 +23,7 @@ import { searchPipes } from './pipes';
 @NgModule({
     imports: [
         SharedModule,
+        //DynamicContentModule,
         ConeSearchModule,
         SearchRoutingModule
     ],
diff --git a/client/src/app/instance/store/effects/detail.effects.spec.ts b/client/src/app/instance/store/effects/detail.effects.spec.ts
index 641d716803bcb67f5e60ee0a84ad147ad9dcf2cb..6bedb172285c22e8c8ed3e0785bea087c838eeb4 100644
--- a/client/src/app/instance/store/effects/detail.effects.spec.ts
+++ b/client/src/app/instance/store/effects/detail.effects.spec.ts
@@ -94,10 +94,7 @@ describe('[Instance][Store] DetailEffects', () => {
                     renderer_config: null,
                     order_by: true,
                     archive: false,
-                    detail: true,
-                    display_detail: 1,
-                    renderer_detail: null,
-                    renderer_detail_config: null,
+                    detail_display: 1,
                     options: null,
                     vo_utype: null,
                     vo_ucd: null,
@@ -106,7 +103,8 @@ describe('[Instance][Store] DetailEffects', () => {
                     vo_datatype: null,
                     vo_size: null,
                     id_criteria_family: null,
-                    id_output_category: null
+                    id_output_category: null,
+                    id_detail_output_category: null
                 }]
             );
             mockDetailSelectorSelectIdByRoute = store.overrideSelector(
@@ -151,10 +149,7 @@ describe('[Instance][Store] DetailEffects', () => {
                     renderer_config: null,
                     order_by: true,
                     archive: false,
-                    detail: true,
-                    display_detail: 1,
-                    renderer_detail: null,
-                    renderer_detail_config: null,
+                    detail_display: 1,
                     options: null,
                     vo_utype: null,
                     vo_ucd: null,
@@ -163,7 +158,8 @@ describe('[Instance][Store] DetailEffects', () => {
                     vo_datatype: null,
                     vo_size: null,
                     id_criteria_family: null,
-                    id_output_category: null
+                    id_output_category: null,
+                    id_detail_output_category: null
                 }]
             );
             mockDetailSelectorSelectIdByRoute = store.overrideSelector(
diff --git a/client/src/app/instance/store/effects/detail.effects.ts b/client/src/app/instance/store/effects/detail.effects.ts
index b1f2aa5f2bfec3d2c09c3aa7055ee309655cafff..893cd264252961faa68ed71f7e39ca460e14fda3 100644
--- a/client/src/app/instance/store/effects/detail.effects.ts
+++ b/client/src/app/instance/store/effects/detail.effects.ts
@@ -27,7 +27,6 @@ import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector';
  */
 @Injectable()
 export class DetailEffects {
-
     /**
      * Calls actions to retrieve object.
      */
@@ -41,9 +40,8 @@ export class DetailEffects {
             ]),
             mergeMap(([, datasetName, attributeList, id]) => this.detailService.retrieveObject(
                 datasetName,
-                attributeList.find(attribute => attribute.order_by).id,
-                id,
-                attributeList.filter(attribute => attribute.detail).map(attribute => attribute.id)
+                attributeList.find(attribute => attribute.primary_key).id,
+                id
             ).pipe(
                 map(object => detailActions.retrieveObjectSuccess({ object: object[0] })),
                 catchError(() => of(detailActions.retrieveObjectFail()))
diff --git a/client/src/app/instance/store/effects/search.effects.spec.ts b/client/src/app/instance/store/effects/search.effects.spec.ts
index 9ec12b8ff4348f6720824c388e68b518be14a06b..436528864caa23d27230d2df7402fa349e3e2bbf 100644
--- a/client/src/app/instance/store/effects/search.effects.spec.ts
+++ b/client/src/app/instance/store/effects/search.effects.spec.ts
@@ -301,10 +301,7 @@ describe('[Instance][Store] SearchEffects', () => {
                         renderer_config: null,
                         order_by: true,
                         archive: false,
-                        detail: true,
-                        display_detail: 1,
-                        renderer_detail: null,
-                        renderer_detail_config: null,
+                        detail_display: 1,
                         options: null,
                         vo_utype: null,
                         vo_ucd: null,
@@ -313,7 +310,8 @@ describe('[Instance][Store] SearchEffects', () => {
                         vo_datatype: null,
                         vo_size: null,
                         id_criteria_family: 1,
-                        id_output_category: null
+                        id_output_category: null,
+                        id_detail_output_category: null
                     },
                     {
                         id: 2,
@@ -337,10 +335,7 @@ describe('[Instance][Store] SearchEffects', () => {
                         renderer_config: null,
                         order_by: true,
                         archive: false,
-                        detail: true,
-                        display_detail: 1,
-                        renderer_detail: null,
-                        renderer_detail_config: null,
+                        detail_display: 1,
                         options: null,
                         vo_utype: null,
                         vo_ucd: null,
@@ -349,7 +344,8 @@ describe('[Instance][Store] SearchEffects', () => {
                         vo_datatype: null,
                         vo_size: null,
                         id_criteria_family: 2,
-                        id_output_category: null
+                        id_output_category: null,
+                        id_detail_output_category: null
                     }
                 ]
             );
@@ -404,10 +400,7 @@ describe('[Instance][Store] SearchEffects', () => {
                         renderer_config: null,
                         order_by: true,
                         archive: false,
-                        detail: false,
-                        display_detail: 1,
-                        renderer_detail: null,
-                        renderer_detail_config: null,
+                        detail_display: 1,
                         options: null,
                         vo_utype: null,
                         vo_ucd: null,
@@ -416,7 +409,8 @@ describe('[Instance][Store] SearchEffects', () => {
                         vo_datatype: null,
                         vo_size: null,
                         id_criteria_family: 1,
-                        id_output_category: null
+                        id_output_category: null,
+                        id_detail_output_category: null
                     },
                     {
                         id: 2,
@@ -440,10 +434,7 @@ describe('[Instance][Store] SearchEffects', () => {
                         renderer_config: null,
                         order_by: true,
                         archive: false,
-                        detail: false,
-                        display_detail: 1,
-                        renderer_detail: null,
-                        renderer_detail_config: null,
+                        detail_display: 1,
                         options: null,
                         vo_utype: null,
                         vo_ucd: null,
@@ -452,7 +443,8 @@ describe('[Instance][Store] SearchEffects', () => {
                         vo_datatype: null,
                         vo_size: null,
                         id_criteria_family: 2,
-                        id_output_category: null
+                        id_output_category: null,
+                        id_detail_output_category: null
                     }
                 ]
             );
@@ -540,10 +532,7 @@ describe('[Instance][Store] SearchEffects', () => {
                         renderer_config: null,
                         order_by: true,
                         archive: false,
-                        detail: true,
-                        display_detail: 1,
-                        renderer_detail: null,
-                        renderer_detail_config: null,
+                        detail_display: 1,
                         options: null,
                         vo_utype: null,
                         vo_ucd: null,
@@ -552,7 +541,8 @@ describe('[Instance][Store] SearchEffects', () => {
                         vo_datatype: null,
                         vo_size: null,
                         id_criteria_family: 1,
-                        id_output_category: 1
+                        id_output_category: 1,
+                        id_detail_output_category: null
                     },
                     {
                         id: 2,
@@ -576,10 +566,7 @@ describe('[Instance][Store] SearchEffects', () => {
                         renderer_config: null,
                         order_by: true,
                         archive: false,
-                        detail: true,
-                        display_detail: 1,
-                        renderer_detail: null,
-                        renderer_detail_config: null,
+                        detail_display: 1,
                         options: null,
                         vo_utype: null,
                         vo_ucd: null,
@@ -588,7 +575,8 @@ describe('[Instance][Store] SearchEffects', () => {
                         vo_datatype: null,
                         vo_size: null,
                         id_criteria_family: 2,
-                        id_output_category: 1
+                        id_output_category: 1,
+                        id_detail_output_category: null
                     }
                 ]
             );
diff --git a/client/src/app/instance/store/models/criterion.model.spec.ts b/client/src/app/instance/store/models/criterion.model.spec.ts
index 8c69afbe19d7a42259a3f13394de8ba8483789a3..d678d277b5f8603aa9057b4810543e338ada5df8 100644
--- a/client/src/app/instance/store/models/criterion.model.spec.ts
+++ b/client/src/app/instance/store/models/criterion.model.spec.ts
@@ -57,10 +57,7 @@ describe('[Instance][Store] CriterionModel', () => {
             renderer_config: null,
             order_by: true,
             archive: false,
-            detail: true,
-            display_detail: 1,
-            renderer_detail: null,
-            renderer_detail_config: null,
+            detail_display: 1,
             options: null,
             vo_utype: null,
             vo_ucd: null,
@@ -69,7 +66,8 @@ describe('[Instance][Store] CriterionModel', () => {
             vo_datatype: null,
             vo_size: null,
             id_criteria_family: null,
-            id_output_category: null
+            id_output_category: null,
+            id_detail_output_category: null
         };
         let expected: Criterion = { id: 1, type: 'field', operator: 'neq', value: 'otherValue' } as FieldCriterion;
         expect(stringToCriterion(attribute, ['', 'neq', 'otherValue'])).toEqual(expected);
@@ -95,10 +93,7 @@ describe('[Instance][Store] CriterionModel', () => {
             renderer_config: null,
             order_by: true,
             archive: false,
-            detail: true,
-            display_detail: 1,
-            renderer_detail: null,
-            renderer_detail_config: null,
+            detail_display: 1,
             options: null,
             vo_utype: null,
             vo_ucd: null,
@@ -107,7 +102,8 @@ describe('[Instance][Store] CriterionModel', () => {
             vo_datatype: null,
             vo_size: null,
             id_criteria_family: null,
-            id_output_category: null
+            id_output_category: null,
+            id_detail_output_category: null
         };
         expected = { id: 1, type: 'field', operator: 'eq', value: 'value' } as FieldCriterion;
         expect(stringToCriterion(attribute)).toEqual(expected);
@@ -133,10 +129,7 @@ describe('[Instance][Store] CriterionModel', () => {
             renderer_config: null,
             order_by: true,
             archive: false,
-            detail: true,
-            display_detail: 1,
-            renderer_detail: null,
-            renderer_detail_config: null,
+            detail_display: 1,
             options: null,
             vo_utype: null,
             vo_ucd: null,
@@ -145,7 +138,8 @@ describe('[Instance][Store] CriterionModel', () => {
             vo_datatype: null,
             vo_size: null,
             id_criteria_family: null,
-            id_output_category: null
+            id_output_category: null,
+            id_detail_output_category: null
         };
         expected = { id: 1, type: 'list', values: ['one', 'two'] } as ListCriterion;
         expect(stringToCriterion(attribute, ['', '', 'one|two'])).toEqual(expected);
@@ -171,10 +165,7 @@ describe('[Instance][Store] CriterionModel', () => {
             renderer_config: null,
             order_by: true,
             archive: false,
-            detail: true,
-            display_detail: 1,
-            renderer_detail: null,
-            renderer_detail_config: null,
+            detail_display: 1,
             options: null,
             vo_utype: null,
             vo_ucd: null,
@@ -183,7 +174,8 @@ describe('[Instance][Store] CriterionModel', () => {
             vo_datatype: null,
             vo_size: null,
             id_criteria_family: null,
-            id_output_category: null
+            id_output_category: null,
+            id_detail_output_category: null
         };
         expected = { id: 1, type: 'list', values: ['valueA', 'valueB'] } as ListCriterion;
         expect(stringToCriterion(attribute)).toEqual(expected);
@@ -209,10 +201,7 @@ describe('[Instance][Store] CriterionModel', () => {
             renderer_config: null,
             order_by: true,
             archive: false,
-            detail: true,
-            display_detail: 1,
-            renderer_detail: null,
-            renderer_detail_config: null,
+            detail_display: 1,
             options: null,
             vo_utype: null,
             vo_ucd: null,
@@ -221,7 +210,8 @@ describe('[Instance][Store] CriterionModel', () => {
             vo_datatype: null,
             vo_size: null,
             id_criteria_family: null,
-            id_output_category: null
+            id_output_category: null,
+            id_detail_output_category: null
         };
         expected = { id: 1, type: 'between', min: 'valueA', max: 'valueB' } as BetweenCriterion;
         expect(stringToCriterion(attribute, ['', 'bw', 'valueA|valueB'])).toEqual(expected);
@@ -258,10 +248,7 @@ describe('[Instance][Store] CriterionModel', () => {
             renderer_config: null,
             order_by: true,
             archive: false,
-            detail: true,
-            display_detail: 1,
-            renderer_detail: null,
-            renderer_detail_config: null,
+            detail_display: 1,
             vo_utype: null,
             vo_ucd: null,
             vo_unit: null,
@@ -269,7 +256,8 @@ describe('[Instance][Store] CriterionModel', () => {
             vo_datatype: null,
             vo_size: null,
             id_criteria_family: null,
-            id_output_category: null
+            id_output_category: null,
+            id_detail_output_category: null
         };
         expected = { id: 1, type: 'multiple', options: [{ label: 'one', value: '1', display: 1 }, { label: 'three', value: '3', display: 3 }] } as SelectMultipleCriterion;
         expect(stringToCriterion(attribute, ['', '', '1|3'])).toEqual(expected);
@@ -300,10 +288,7 @@ describe('[Instance][Store] CriterionModel', () => {
             renderer_config: null,
             order_by: true,
             archive: false,
-            detail: true,
-            display_detail: 1,
-            renderer_detail: null,
-            renderer_detail_config: null,
+            detail_display: 1,
             vo_utype: null,
             vo_ucd: null,
             vo_unit: null,
@@ -311,7 +296,8 @@ describe('[Instance][Store] CriterionModel', () => {
             vo_datatype: null,
             vo_size: null,
             id_criteria_family: null,
-            id_output_category: null
+            id_output_category: null,
+            id_detail_output_category: null
         };
         expected = { id: 1, type: 'multiple', options: [{ label: 'one', value: '1', display: 1 }, { label: 'two', value: '2', display: 2 }] } as SelectMultipleCriterion;
         expect(stringToCriterion(attribute)).toEqual(expected);
@@ -338,10 +324,7 @@ describe('[Instance][Store] CriterionModel', () => {
             renderer_config: null,
             order_by: true,
             archive: false,
-            detail: true,
-            display_detail: 1,
-            renderer_detail: null,
-            renderer_detail_config: null,
+            detail_display: 1,
             vo_utype: null,
             vo_ucd: null,
             vo_unit: null,
@@ -349,7 +332,8 @@ describe('[Instance][Store] CriterionModel', () => {
             vo_datatype: null,
             vo_size: null,
             id_criteria_family: null,
-            id_output_category: null
+            id_output_category: null,
+            id_detail_output_category: null
         };
         expected = { id: 1, type: 'json', path: 'path', operator: 'op', value: 'value' } as JsonCriterion;
         expect(stringToCriterion(attribute, ['', '', 'path|op|value'])).toEqual(expected);
@@ -376,10 +360,7 @@ describe('[Instance][Store] CriterionModel', () => {
             renderer_config: null,
             order_by: true,
             archive: false,
-            detail: true,
-            display_detail: 1,
-            renderer_detail: null,
-            renderer_detail_config: null,
+            detail_display: 1,
             vo_utype: null,
             vo_ucd: null,
             vo_unit: null,
@@ -387,7 +368,8 @@ describe('[Instance][Store] CriterionModel', () => {
             vo_datatype: null,
             vo_size: null,
             id_criteria_family: null,
-            id_output_category: null
+            id_output_category: null,
+            id_detail_output_category: null
         };
         expect(stringToCriterion(attribute)).toEqual(expected);
         attribute = {
@@ -413,10 +395,7 @@ describe('[Instance][Store] CriterionModel', () => {
             renderer_config: null,
             order_by: true,
             archive: false,
-            detail: true,
-            display_detail: 1,
-            renderer_detail: null,
-            renderer_detail_config: null,
+            detail_display: 1,
             vo_utype: null,
             vo_ucd: null,
             vo_unit: null,
@@ -424,7 +403,8 @@ describe('[Instance][Store] CriterionModel', () => {
             vo_datatype: null,
             vo_size: null,
             id_criteria_family: null,
-            id_output_category: null
+            id_output_category: null,
+            id_detail_output_category: null
         };
         expect(stringToCriterion(attribute)).toBeNull();
     });
diff --git a/client/src/app/instance/store/services/detail.service.spec.ts b/client/src/app/instance/store/services/detail.service.spec.ts
index 5bac6d854bf16f91dee9e5fb58ce834ec094b12f..cd785472d58149784a713cde4e12f5f63a18dfdf 100644
--- a/client/src/app/instance/store/services/detail.service.spec.ts
+++ b/client/src/app/instance/store/services/detail.service.spec.ts
@@ -22,11 +22,11 @@ describe('[Instance][Store] DetailService', () => {
         inject([HttpTestingController, DetailService],(httpMock: HttpTestingController, detailService: DetailService) => {
             const mockResponse = ['myData'];
 
-            detailService.retrieveObject('myDataset', 1, 'myObject', [2,3]).subscribe((event: any[]) => {
+            detailService.retrieveObject('myDataset', 1, 'myObject').subscribe((event: any[]) => {
                 expect(event).toEqual(mockResponse);
             });
 
-            const mockRequest = httpMock.expectOne('http://testing.com/search/myDataset?c=1::eq::myObject&a=2;3');
+            const mockRequest = httpMock.expectOne('http://testing.com/search/myDataset?c=1::eq::myObject&a=all');
 
             expect(mockRequest.cancelled).toBeFalsy();
             expect(mockRequest.request.responseType).toEqual('json');
diff --git a/client/src/app/instance/store/services/detail.service.ts b/client/src/app/instance/store/services/detail.service.ts
index e9b644aa71907475302f8ffbc31187f4daa59ccd..eee2a6247e76bced3dff6964eccf3f89f4d84c40 100644
--- a/client/src/app/instance/store/services/detail.service.ts
+++ b/client/src/app/instance/store/services/detail.service.ts
@@ -32,8 +32,8 @@ export class DetailService {
      *
      * @return Observable<any[]>
      */
-    retrieveObject(dname: string, criterionId: number, objectSelected: string, outputList: number[]): Observable<any[]> {
-        const query = `${dname}?c=${criterionId}::eq::${objectSelected}&a=${outputList.join(';')}`;
+    retrieveObject(dname: string, criterionId: number, objectSelected: string): Observable<any[]> {
+        const query = `${dname}?c=${criterionId}::eq::${objectSelected}&a=all`;
         return this.http.get<any[]>(`${this.config.apiUrl}/search/${query}`);
     }
 
diff --git a/client/src/app/instance/dynamic-content/components/datatable.component.html b/client/src/app/instance/webpage/components/datatable.component.html
similarity index 100%
rename from client/src/app/instance/dynamic-content/components/datatable.component.html
rename to client/src/app/instance/webpage/components/datatable.component.html
diff --git a/client/src/app/instance/dynamic-content/components/datatable.component.ts b/client/src/app/instance/webpage/components/datatable.component.ts
similarity index 100%
rename from client/src/app/instance/dynamic-content/components/datatable.component.ts
rename to client/src/app/instance/webpage/components/datatable.component.ts
diff --git a/client/src/app/instance/webpage/components/index.ts b/client/src/app/instance/webpage/components/index.ts
index 28c122e3ead6c199771514aeb447141d7880c7a0..cdbd83740a4c940c54f8449fce573b7fd98019b1 100644
--- a/client/src/app/instance/webpage/components/index.ts
+++ b/client/src/app/instance/webpage/components/index.ts
@@ -1,5 +1,7 @@
 import { WebpageComponent } from './webpage-content.component';
+import { DatatableComponent } from './datatable.component';
 
 export const dummiesComponents = [
-    WebpageComponent
+    WebpageComponent,
+    DatatableComponent
 ];
diff --git a/client/src/app/instance/webpage/components/webpage-content.component.html b/client/src/app/instance/webpage/components/webpage-content.component.html
index 99c5e9a4f9b14ba3431047af7a8c746abf86c8b0..5fff195df3ec184143772fe509157449a25c0c0b 100644
--- a/client/src/app/instance/webpage/components/webpage-content.component.html
+++ b/client/src/app/instance/webpage/components/webpage-content.component.html
@@ -1 +1 @@
-<ngx-dynamic-hooks [content]="webpage.content"></ngx-dynamic-hooks>
\ No newline at end of file
+<ngx-dynamic-hooks [content]="webpage.content" [parsers]="getParsers()"></ngx-dynamic-hooks>
\ No newline at end of file
diff --git a/client/src/app/instance/webpage/components/webpage-content.component.ts b/client/src/app/instance/webpage/components/webpage-content.component.ts
index a8bfd9bbc62020da36e27f1b93e00eb397f52045..7559b9c51420858fc938a183b3b481c867ed4f29 100644
--- a/client/src/app/instance/webpage/components/webpage-content.component.ts
+++ b/client/src/app/instance/webpage/components/webpage-content.component.ts
@@ -7,9 +7,11 @@
  * file that was distributed with this source code.
  */
 
-import { Component, Input } from '@angular/core';
+import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
 
 import { Webpage } from 'src/app/metamodel/models';
+import { globalParsers } from 'src/app/shared/dynamic-content';
+import { componentParsers } from '../dynamic-content';
 
 /**
  * @class
@@ -17,8 +19,16 @@ import { Webpage } from 'src/app/metamodel/models';
  */
 @Component({
     selector: 'app-webpage-content',
-    templateUrl: 'webpage-content.component.html'
+    templateUrl: 'webpage-content.component.html',
+    changeDetection: ChangeDetectionStrategy.OnPush
 })
 export class WebpageComponent {
     @Input() webpage: Webpage;
+
+    getParsers() {
+        return [
+            ...globalParsers,
+            ...componentParsers
+        ];
+    }
 }
diff --git a/client/src/app/instance/dynamic-content/dynamic-components/dataset-sample.component.html b/client/src/app/instance/webpage/dynamic-content/dynamic-components/dataset-sample.component.html
similarity index 100%
rename from client/src/app/instance/dynamic-content/dynamic-components/dataset-sample.component.html
rename to client/src/app/instance/webpage/dynamic-content/dynamic-components/dataset-sample.component.html
diff --git a/client/src/app/instance/dynamic-content/dynamic-components/dataset-sample.component.ts b/client/src/app/instance/webpage/dynamic-content/dynamic-components/dataset-sample.component.ts
similarity index 100%
rename from client/src/app/instance/dynamic-content/dynamic-components/dataset-sample.component.ts
rename to client/src/app/instance/webpage/dynamic-content/dynamic-components/dataset-sample.component.ts
diff --git a/client/src/app/instance/webpage/dynamic-content/dynamic-components/index.ts b/client/src/app/instance/webpage/dynamic-content/dynamic-components/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..36671afce11b25c1cb064a8d3851b24b431cf5d4
--- /dev/null
+++ b/client/src/app/instance/webpage/dynamic-content/dynamic-components/index.ts
@@ -0,0 +1,5 @@
+import { DatasetSampleComponent } from './dataset-sample.component';
+
+export const dynamicComponents = [
+    DatasetSampleComponent
+];
diff --git a/client/src/app/instance/webpage/dynamic-content/index.ts b/client/src/app/instance/webpage/dynamic-content/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a8a58f220117ec5118c9ca18d1f0e91af6ee7015
--- /dev/null
+++ b/client/src/app/instance/webpage/dynamic-content/index.ts
@@ -0,0 +1,14 @@
+import { HookParserEntry } from 'ngx-dynamic-hooks';
+
+import { hookParsers } from './parsers';
+import { dynamicComponents } from './dynamic-components';
+
+export const componentParsers: Array<HookParserEntry> = [
+    ...hookParsers,
+    ...dynamicComponents.map(component => {
+        return { component };
+    })
+];
+
+export { hookParsers } from './parsers';
+export { dynamicComponents } from './dynamic-components';
diff --git a/client/src/app/instance/webpage/dynamic-content/parsers/index.ts b/client/src/app/instance/webpage/dynamic-content/parsers/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..552c5a60f42b85a8d485439c9086f2c80778140f
--- /dev/null
+++ b/client/src/app/instance/webpage/dynamic-content/parsers/index.ts
@@ -0,0 +1,3 @@
+export const hookParsers = [
+    
+];
diff --git a/client/src/app/instance/webpage/webpage.module.ts b/client/src/app/instance/webpage/webpage.module.ts
index 5bec1e05d2e46632e45e0dc24d0b376d975504ee..86a7baca4e2077dd36eda7b4a4eba995a96a4b8f 100644
--- a/client/src/app/instance/webpage/webpage.module.ts
+++ b/client/src/app/instance/webpage/webpage.module.ts
@@ -10,9 +10,9 @@
 import { NgModule } from '@angular/core';
 
 import { SharedModule } from 'src/app/shared/shared.module';
-import { DynamicContentModule } from '../dynamic-content/dynamic-content.module';
 import { WebpageRoutingModule, routedComponents } from './webpage-routing.module';
 import { dummiesComponents } from './components';
+import { hookParsers, dynamicComponents } from './dynamic-content';
 
 /**
  * @class
@@ -21,12 +21,18 @@ import { dummiesComponents } from './components';
 @NgModule({
     imports: [
         SharedModule,
-        DynamicContentModule,
         WebpageRoutingModule,
     ],
     declarations: [
         routedComponents,
-        dummiesComponents
+        dummiesComponents,
+        dynamicComponents
+    ],
+    entryComponents: [
+        dynamicComponents
+    ],
+    providers: [
+        hookParsers
     ]
 })
 export class WebpageModule { }
diff --git a/client/src/app/metamodel/actions/detail-config.actions.ts b/client/src/app/metamodel/actions/detail-config.actions.ts
new file mode 100644
index 0000000000000000000000000000000000000000..685a2fdbae81f1997172e75a7f9b2ce840dc8a10
--- /dev/null
+++ b/client/src/app/metamodel/actions/detail-config.actions.ts
@@ -0,0 +1,22 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { createAction, props } from '@ngrx/store';
+ 
+import { DetailConfig } from '../models';
+
+export const loadDetailConfig = createAction('[Metamodel] Load Detail Config');
+export const loadDetailConfigSuccess = createAction('[Metamodel] Load Detail Config Success', props<{ detailConfig: DetailConfig }>());
+export const loadDetailConfigFail = createAction('[Metamodel] Load Detail Config Fail');
+export const addDetailConfig = createAction('[Metamodel] Add Detail Config', props<{ detailConfig: DetailConfig }>());
+export const addDetailConfigSuccess = createAction('[Metamodel] Add Detail Config Success', props<{ detailConfig: DetailConfig }>());
+export const addDetailConfigFail = createAction('[Metamodel] Add Detail Config Fail');
+export const editDetailConfig = createAction('[Metamodel] Edit Detail Config', props<{ detailConfig: DetailConfig }>());
+export const editDetailConfigSuccess = createAction('[Metamodel] Edit Detail Config Success', props<{ detailConfig: DetailConfig }>());
+export const editDetailConfigFail = createAction('[Metamodel] Edit Detail Config Fail');
diff --git a/client/src/app/metamodel/effects/detail-config.effects.ts b/client/src/app/metamodel/effects/detail-config.effects.ts
new file mode 100644
index 0000000000000000000000000000000000000000..497015a88b8d6c0831ba40302bcb8fa2b2a45ba6
--- /dev/null
+++ b/client/src/app/metamodel/effects/detail-config.effects.ts
@@ -0,0 +1,122 @@
+/**
+ * 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 * as detailConfigActions from '../actions/detail-config.actions';
+import { DetailConfigService } from '../services/detail-config.service';
+import * as datasetSelector from '../selectors/dataset.selector';
+
+/**
+ * @class
+ * @classdesc Detail effects.
+ */
+@Injectable()
+export class DetailConfigEffects {
+    /**
+     * Calls action to retrieve detail configuration
+     */
+    loadDetailsConfig$ = createEffect((): any =>
+        this.actions$.pipe(
+            ofType(detailConfigActions.loadDetailConfig),
+            concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)),
+            mergeMap(([, datasetName]) => this.detailConfigService.retrieveDetailConfig(datasetName)
+                .pipe(
+                    map(detailConfig => detailConfigActions.loadDetailConfigSuccess({ detailConfig })),
+                    catchError(() => of(detailConfigActions.loadDetailConfigFail()))
+                )
+            )
+        )
+    );
+
+    /**
+     * Calls action to add an detail.
+     */
+    addDetailConfig$ = createEffect((): any =>
+        this.actions$.pipe(
+            ofType(detailConfigActions.addDetailConfig),
+            concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)),
+            mergeMap(([action, datasetName]) => this.detailConfigService.addDetailConfig(datasetName, action.detailConfig)
+                .pipe(
+                    map(detailConfig => detailConfigActions.addDetailConfigSuccess({ detailConfig })),
+                    catchError(() => of(detailConfigActions.addDetailConfigFail()))
+                )
+            )
+        )
+    );
+
+    /**
+     * Displays add detail configuration success notification.
+     */
+    addDetailConfigSuccess$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(detailConfigActions.addDetailConfigSuccess),
+            tap(() => this.toastr.success('Detail config successfully added', 'The new detail config was added into the database'))
+        ), { dispatch: false }
+    );
+
+    /**
+     * Displays add detail configuration error notification.
+     */
+    addDetailConfigFail$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(detailConfigActions.addDetailConfigFail),
+            tap(() => this.toastr.error('Failure to add detail config', 'The new detail config could not be added into the database'))
+        ), { dispatch: false }
+    );
+
+    /**
+     * Calls action to modify an detail configuration
+     */
+    editDetailConfig$ = createEffect((): any =>
+        this.actions$.pipe(
+            ofType(detailConfigActions.editDetailConfig),
+            concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)),
+            mergeMap(([action, datasetName]) => this.detailConfigService.editDetailConfig(datasetName, action.detailConfig)
+                .pipe(
+                    map(detailConfig => detailConfigActions.editDetailConfigSuccess({ detailConfig })),
+                    catchError(() => of(detailConfigActions.editDetailConfigFail()))
+                )
+            )
+        )
+    );
+
+    /**
+     * Displays edit detail configuration success notification.
+     */
+    editDetailConfigSuccess$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(detailConfigActions.editDetailConfigSuccess),
+            tap(() => this.toastr.success('Detail configuration successfully edited', 'The existing detail configuration has been edited into the database'))
+        ), { dispatch: false }
+    );
+
+    /**
+     * Displays edit detail configuration error notification.
+     */
+    editDetailConfigFail$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(detailConfigActions.editDetailConfigFail),
+            tap(() => this.toastr.error('Failure to edit detail configuration', 'The existing detail configuration could not be edited into the database'))
+        ), { dispatch: false }
+    );
+
+    constructor(
+        private actions$: Actions,
+        private detailConfigService: DetailConfigService,
+        private toastr: ToastrService,
+        private store: Store<{ }>
+    ) {}
+}
diff --git a/client/src/app/metamodel/effects/index.ts b/client/src/app/metamodel/effects/index.ts
index 9f2e2fc1b4e1f176e4ee57b683a368506214a3ac..4975891556f58baac43642c3fcc91a363cd8c36c 100644
--- a/client/src/app/metamodel/effects/index.ts
+++ b/client/src/app/metamodel/effects/index.ts
@@ -20,6 +20,7 @@ import { OutputFamilyEffects } from './output-family.effects';
 import { ImageEffects } from './image.effects';
 import { FileEffects } from './file.effects';
 import { ConeSearchConfigEffects } from './cone-search-config.effects'
+import { DetailConfigEffects } from './detail-config.effects';
 import { WebpageFamilyEffects } from './webpage-family.effects';
 import { WebpageEffects } from './webpage.effects';
 
@@ -37,6 +38,7 @@ export const metamodelEffects = [
     ImageEffects,
     FileEffects,
     ConeSearchConfigEffects,
+    DetailConfigEffects,
     WebpageFamilyEffects,
     WebpageEffects
 ];
diff --git a/client/src/app/metamodel/metamodel.reducer.ts b/client/src/app/metamodel/metamodel.reducer.ts
index 017bb208bf52fad13a7d7d9ffef312fd6bdd2e3b..c77c467c0e4915591d57616135193498a2c5d424 100644
--- a/client/src/app/metamodel/metamodel.reducer.ts
+++ b/client/src/app/metamodel/metamodel.reducer.ts
@@ -23,6 +23,7 @@ import * as outputFamily from './reducers/output-family.reducer';
 import * as image from './reducers/image.reducer';
 import * as file from './reducers/file.reducer';
 import * as coneSearchConfig from './reducers/cone-search-config.reducer';
+import * as detailConfig from './reducers/detail-config.reducer';
 import * as webpageFamily from './reducers/webpage-family.reducer';
 import * as webpage from './reducers/webpage.reducer';
 
@@ -45,6 +46,7 @@ export interface State {
     image: image.State;
     file: file.State;
     coneSearchConfig: coneSearchConfig.State;
+    detailConfig: detailConfig.State;
     webpageFamily: webpageFamily.State;
     webpage: webpage.State;
 }
@@ -63,6 +65,7 @@ const reducers = {
     image: image.imageReducer,
     file: file.fileReducer,
     coneSearchConfig: coneSearchConfig.coneSearchConfigReducer,
+    detailConfig: detailConfig.detailConfigReducer,
     webpageFamily: webpageFamily.webpageFamilyReducer,
     webpage: webpage.webpageReducer
 };
diff --git a/client/src/app/metamodel/models/attribute.model.ts b/client/src/app/metamodel/models/attribute.model.ts
index ec8a1f3dad0c7150f3d8bfbe37c864d8d5b29474..ab96827eca36153c04b2b853ea1f47c5b6ccd7e5 100644
--- a/client/src/app/metamodel/models/attribute.model.ts
+++ b/client/src/app/metamodel/models/attribute.model.ts
@@ -9,7 +9,6 @@
 
 import { Option } from './option.model';
 import { RendererConfig } from './renderers';
-import { DetailRendererConfig } from './detail-renderers';
 
 /**
  * Interface for attribute.
@@ -38,10 +37,7 @@ export interface Attribute {
     renderer_config: RendererConfig;
     order_by: boolean;
     archive: boolean;
-    detail: boolean;
-    display_detail: number;
-    renderer_detail: string;
-    renderer_detail_config: DetailRendererConfig;
+    detail_display: number;
     options: Option[];
     vo_utype: string;
     vo_ucd: string;
@@ -51,4 +47,5 @@ export interface Attribute {
     vo_size: number;
     id_criteria_family: number;
     id_output_category: number;
+    id_detail_output_category: number;
 }
diff --git a/client/src/app/metamodel/models/detail-renderers/detail-renderer-config.model.ts b/client/src/app/metamodel/models/detail-config.model.ts
similarity index 62%
rename from client/src/app/metamodel/models/detail-renderers/detail-renderer-config.model.ts
rename to client/src/app/metamodel/models/detail-config.model.ts
index 2eca974a42fef44681b51552cb6a0a69c8327e54..52f3e6559af5db34257bf1fd7bc6a997715e0b59 100644
--- a/client/src/app/metamodel/models/detail-renderers/detail-renderer-config.model.ts
+++ b/client/src/app/metamodel/models/detail-config.model.ts
@@ -8,10 +8,12 @@
  */
 
 /**
- * Interface for renderer config.
+ * Interface for detail config.
  *
- * @interface DetailRendererConfig
+ * @interface DetailConfig
  */
-export interface DetailRendererConfig {
-    id: 'detail-renderer-config';
-}
+export interface DetailConfig {
+    id: number;
+    enabled: boolean;
+    content: string;
+}
\ No newline at end of file
diff --git a/client/src/app/metamodel/models/detail-renderers/index.ts b/client/src/app/metamodel/models/detail-renderers/index.ts
deleted file mode 100644
index 0ffe3fbd3e6b5d98f4e5f95ad5416cd21cd15d14..0000000000000000000000000000000000000000
--- a/client/src/app/metamodel/models/detail-renderers/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from './detail-renderer-config.model';
-export * from './spectra-graph-renderer-config.model';
diff --git a/client/src/app/metamodel/models/detail-renderers/spectra-graph-renderer-config.model.ts b/client/src/app/metamodel/models/detail-renderers/spectra-graph-renderer-config.model.ts
deleted file mode 100644
index 2390fd9a29fa19929c55fc1b447d3982c532021b..0000000000000000000000000000000000000000
--- a/client/src/app/metamodel/models/detail-renderers/spectra-graph-renderer-config.model.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-/**
- * 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 { DetailRendererConfig } from './detail-renderer-config.model';
-
-/**
- * Interface for sprectra graph renderer config.
- *
- * @interface SpectraGraphRendererConfig
- * @extends DetailRendererConfig
- */
-export interface SpectraGraphRendererConfig extends DetailRendererConfig {
-    z: number;
-}
diff --git a/client/src/app/metamodel/models/index.ts b/client/src/app/metamodel/models/index.ts
index 976dea69f42d52915527e4df612fd2a076f406e0..12e9e400a19a77764cc1c28aad386786e3b0fec9 100644
--- a/client/src/app/metamodel/models/index.ts
+++ b/client/src/app/metamodel/models/index.ts
@@ -20,8 +20,8 @@ export * from './output-category.model';
 export * from './output-family.model';
 export * from './image.model';
 export * from './renderers';
-export * from './detail-renderers';
 export * from './cone-search-config.model';
+export * from './detail-config.model';
 export * from './file.model';
 export * from './webpage.model';
 export * from './webpage-family';
diff --git a/client/src/app/metamodel/reducers/detail-config.reducer.ts b/client/src/app/metamodel/reducers/detail-config.reducer.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9abac1dc8a8dd82534ac10ea8bfa3146310171c8
--- /dev/null
+++ b/client/src/app/metamodel/reducers/detail-config.reducer.ts
@@ -0,0 +1,72 @@
+/**
+ * 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 { DetailConfig } from '../models';
+import * as detailActions from '../actions/detail-config.actions';
+
+/**
+ * Interface for detail state.
+ *
+ * @interface State
+ */
+export interface State {
+    detailConfig: DetailConfig;
+    detailConfigIsLoading: boolean;
+    detailConfigIsLoaded: boolean;
+}
+
+export const initialState: State = {
+    detailConfig: null,
+    detailConfigIsLoading: false,
+    detailConfigIsLoaded: false
+};
+
+export const detailConfigReducer = createReducer(
+    initialState,
+    on(detailActions.loadDetailConfig, (state) => {
+        return {
+            ...state,
+            detailConfig: null,
+            detailConfigIsLoading: true,
+            detailConfigIsLoaded: false
+        }
+    }),
+    on(detailActions.loadDetailConfigSuccess, (state, { detailConfig }) => {
+        return {
+            ...state,
+            detailConfig,
+            detailConfigIsLoading: false,
+            detailConfigIsLoaded: true
+        }
+    }),
+    on(detailActions.loadDetailConfigFail, (state) => {
+        return {
+            ...state,
+            detailConfigIsLoading: false
+        }
+    }),
+    on(detailActions.addDetailConfigSuccess, (state, { detailConfig }) => {
+        return {
+            ...state,
+            detailConfig
+        }
+    }),
+    on(detailActions.editDetailConfigSuccess, (state, { detailConfig }) => {
+        return {
+            ...state,
+            detailConfig
+        }
+    })
+);
+
+export const selectDetailConfig = (state: State) => state.detailConfig;
+export const selectDetailConfigIsLoading = (state: State) => state.detailConfigIsLoading;
+export const selectDetailConfigIsLoaded = (state: State) => state.detailConfigIsLoaded;
diff --git a/client/src/app/metamodel/selectors/detail-config.selector.ts b/client/src/app/metamodel/selectors/detail-config.selector.ts
new file mode 100644
index 0000000000000000000000000000000000000000..211496fe7267636c933d74c8db5e23aee62dd68d
--- /dev/null
+++ b/client/src/app/metamodel/selectors/detail-config.selector.ts
@@ -0,0 +1,33 @@
+/**
+ * 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 '../metamodel.reducer';
+import * as fromDetailConfig from '../reducers/detail-config.reducer';
+
+export const selectDetailConfigState = createSelector(
+    reducer.getMetamodelState,
+    (state: reducer.State) => state.detailConfig
+);
+
+export const selectDetailConfig = createSelector(
+    selectDetailConfigState,
+    fromDetailConfig.selectDetailConfig
+);
+
+export const selectDetailConfigIsLoading = createSelector(
+    selectDetailConfigState,
+    fromDetailConfig.selectDetailConfigIsLoading
+);
+
+export const selectDetailConfigIsLoaded = createSelector(
+    selectDetailConfigState,
+    fromDetailConfig.selectDetailConfigIsLoaded
+);
diff --git a/client/src/app/metamodel/services/detail-config.service.ts b/client/src/app/metamodel/services/detail-config.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3a2b08063945d47503888c5b629bbc34f10c4d0c
--- /dev/null
+++ b/client/src/app/metamodel/services/detail-config.service.ts
@@ -0,0 +1,57 @@
+/**
+ * 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 { DetailConfig } from '../models';
+import { AppConfigService } from 'src/app/app-config.service';
+
+/**
+ * @class
+ * @classdesc detail configuration service.
+ */
+@Injectable()
+export class DetailConfigService {
+    constructor(private http: HttpClient, private config: AppConfigService) { }
+
+    /**
+     * Retrieves detail configuration
+     *
+     * @return Observable<DetailConfig>
+     */
+    retrieveDetailConfig(datasetName: string): Observable<DetailConfig> {
+        return this.http.get<DetailConfig>(`${this.config.apiUrl}/dataset/${datasetName}/detail-config`);
+    }
+
+    /**
+     * Adds a new detail configuration for the given dataset.
+     *
+     * @param  {string} datasetName - The dataset name.
+     * @param  {Detail} newDetailConfig - The detail configuration.
+     *
+     * @return Observable<DetailConfig>
+     */
+    addDetailConfig(datasetName: string, newDetailConfig: DetailConfig): Observable<DetailConfig> {
+        return this.http.post<DetailConfig>(`${this.config.apiUrl}/dataset/${datasetName}/detail-config`, newDetailConfig);
+    }
+
+    /**
+     * Modifies detail configuration.
+     *
+     * @param  {Detail} detailConfig - The detail cofiguration.
+     *
+     * @return Observable<DetailConfig>
+     */
+    editDetailConfig(datasetName: string, detailConfig: DetailConfig): Observable<DetailConfig> {
+        return this.http.put<DetailConfig>(`${this.config.apiUrl}/dataset/${datasetName}/detail-config`, detailConfig);
+    }
+}
diff --git a/client/src/app/metamodel/services/index.ts b/client/src/app/metamodel/services/index.ts
index 1c670871ca257a1f0da700f2ce2f45baa6e8fcd7..b2588fe9038f588cb611dd7c7c69997c19a3b2e7 100644
--- a/client/src/app/metamodel/services/index.ts
+++ b/client/src/app/metamodel/services/index.ts
@@ -20,6 +20,7 @@ import { OutputFamilyService } from './output-family.service';
 import { ImageService } from './image.service';
 import { FileService } from './file.service';
 import { ConeSearchConfigService } from './cone-search-config.service';
+import { DetailConfigService } from './detail-config.service';
 import { WebpageFamilyService } from './webpage-family.service';
 import { WebpageService } from './webpage.service';
 
@@ -37,6 +38,7 @@ export const metamodelServices = [
     ImageService,
     FileService,
     ConeSearchConfigService,
+    DetailConfigService,
     WebpageFamilyService,
     WebpageService
 ];
diff --git a/client/src/app/instance/dynamic-content/dynamic-components/dynamic-router-link.component.html b/client/src/app/shared/dynamic-content/dynamic-components/dynamic-router-link.component.html
similarity index 100%
rename from client/src/app/instance/dynamic-content/dynamic-components/dynamic-router-link.component.html
rename to client/src/app/shared/dynamic-content/dynamic-components/dynamic-router-link.component.html
diff --git a/client/src/app/instance/dynamic-content/dynamic-components/dynamic-router-link.component.scss b/client/src/app/shared/dynamic-content/dynamic-components/dynamic-router-link.component.scss
similarity index 100%
rename from client/src/app/instance/dynamic-content/dynamic-components/dynamic-router-link.component.scss
rename to client/src/app/shared/dynamic-content/dynamic-components/dynamic-router-link.component.scss
diff --git a/client/src/app/instance/dynamic-content/dynamic-components/dynamic-router-link.component.ts b/client/src/app/shared/dynamic-content/dynamic-components/dynamic-router-link.component.ts
similarity index 100%
rename from client/src/app/instance/dynamic-content/dynamic-components/dynamic-router-link.component.ts
rename to client/src/app/shared/dynamic-content/dynamic-components/dynamic-router-link.component.ts
diff --git a/client/src/app/shared/dynamic-content/dynamic-components/index.ts b/client/src/app/shared/dynamic-content/dynamic-components/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..376e8a427ccae25fc2bd6090b24f59785feb858a
--- /dev/null
+++ b/client/src/app/shared/dynamic-content/dynamic-components/index.ts
@@ -0,0 +1,5 @@
+import { DynamicRouterLinkComponent } from './dynamic-router-link.component';
+
+export const dynamicComponents = [
+    DynamicRouterLinkComponent
+];
diff --git a/client/src/app/shared/dynamic-content/index.ts b/client/src/app/shared/dynamic-content/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..98122e3a81ef2561c55476df774861dfda839d67
--- /dev/null
+++ b/client/src/app/shared/dynamic-content/index.ts
@@ -0,0 +1,14 @@
+import { HookParserEntry } from 'ngx-dynamic-hooks';
+
+import { hookParsers } from './parsers';
+import { dynamicComponents } from './dynamic-components';
+
+export const globalParsers: Array<HookParserEntry> = [
+    ...hookParsers,
+    ...dynamicComponents.map(component => {
+        return { component };
+    })
+];
+
+export { hookParsers } from './parsers';
+export { dynamicComponents } from './dynamic-components';
diff --git a/client/src/app/instance/dynamic-content/parsers/dynamic-router-link-parser.ts b/client/src/app/shared/dynamic-content/parsers/dynamic-router-link-parser.ts
similarity index 99%
rename from client/src/app/instance/dynamic-content/parsers/dynamic-router-link-parser.ts
rename to client/src/app/shared/dynamic-content/parsers/dynamic-router-link-parser.ts
index cb543c79b8a96b1d49c935f44f88045eef4ee3d9..01b5fa6eaeea8bd6c29a5e2bdf326d822472159d 100644
--- a/client/src/app/instance/dynamic-content/parsers/dynamic-router-link-parser.ts
+++ b/client/src/app/shared/dynamic-content/parsers/dynamic-router-link-parser.ts
@@ -21,7 +21,7 @@ export class DynamicRouterLinkParser implements HookParser {
         this.linkClosingTagRegex = new RegExp('<\\/a>',  'gim');
         this.hrefAttrRegex = new RegExp(hrefAttr, 'im');
         this.classAttrRegex = new RegExp('\\s+class\=\\"([^\\"]*?)\\"', 'im');
-        this.targetAttrRegex = new RegExp('\\s+target\=\\"([^\\"]*?)\\"', 'im')
+        this.targetAttrRegex = new RegExp('\\s+target\=\\"([^\\"]*?)\\"', 'im');
     }
 
     public findHooks(content: string, context: any): Array<HookPosition> {
diff --git a/client/src/app/instance/dynamic-content/parsers/index.ts b/client/src/app/shared/dynamic-content/parsers/index.ts
similarity index 100%
rename from client/src/app/instance/dynamic-content/parsers/index.ts
rename to client/src/app/shared/dynamic-content/parsers/index.ts
diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts
index 5455dacaf03013492277fba8166c698db0219f76..750a89c0234ed6dca33bf93335087f5b2c6bc379 100644
--- a/client/src/app/shared/shared.module.ts
+++ b/client/src/app/shared/shared.module.ts
@@ -23,9 +23,11 @@ import { PaginationModule } from 'ngx-bootstrap/pagination';
 import { ProgressbarModule } from 'ngx-bootstrap/progressbar';
 import { NgSelectModule } from '@ng-select/ng-select';
 import { NgxJsonViewerModule } from 'ngx-json-viewer';
+import { DynamicHooksModule } from 'ngx-dynamic-hooks';
 
 import { sharedComponents } from './components';
 import { sharedPipes } from './pipes';
+import { hookParsers, dynamicComponents } from './dynamic-content';
 
 /**
  * @class
@@ -34,7 +36,14 @@ import { sharedPipes } from './pipes';
 @NgModule({
     declarations: [
         sharedComponents,
-        sharedPipes
+        sharedPipes,
+        dynamicComponents
+    ],
+    entryComponents: [
+        dynamicComponents
+    ],
+    providers: [
+        hookParsers
     ],
     imports: [
         CommonModule,
@@ -51,7 +60,8 @@ import { sharedPipes } from './pipes';
         PaginationModule.forRoot(),
         ProgressbarModule.forRoot(),
         NgSelectModule,
-        NgxJsonViewerModule
+        NgxJsonViewerModule,
+        DynamicHooksModule.forRoot({}),
     ],
     exports: [
         CommonModule,
@@ -68,8 +78,10 @@ import { sharedPipes } from './pipes';
         ProgressbarModule,
         NgSelectModule,
         NgxJsonViewerModule,
+        DynamicHooksModule,
         sharedComponents,
-        sharedPipes
+        sharedPipes,
+        dynamicComponents
     ]
 })
 export class SharedModule { }
diff --git a/client/src/test-data.ts b/client/src/test-data.ts
index 40464d8233914649c8fddb66f59687f6158ef2cd..c1a031a9b1e8709956003aa010f42067241b4510 100644
--- a/client/src/test-data.ts
+++ b/client/src/test-data.ts
@@ -248,10 +248,7 @@ export const ATTRIBUTE_LIST: Attribute[] = [
         renderer_config: null,
         order_by: true,
         archive: false,
-        detail: true,
-        display_detail: 2,
-        renderer_detail: null,
-        renderer_detail_config: null,
+        detail_display: 2,
         options: [
             { label: 'Three', value: 'three', display: 3 },
             { label: 'One', value: 'one', display: 1 },
@@ -264,7 +261,8 @@ export const ATTRIBUTE_LIST: Attribute[] = [
         vo_datatype: null,
         vo_size: null,
         id_criteria_family: null,
-        id_output_category: 2
+        id_output_category: 2,
+        id_detail_output_category: null
     },
     {
         id: 2,
@@ -288,10 +286,7 @@ export const ATTRIBUTE_LIST: Attribute[] = [
         renderer_config: null,
         order_by: true,
         archive: false,
-        detail: true,
-        display_detail: 2,
-        renderer_detail: null,
-        renderer_detail_config: null,
+        detail_display: 2,
         options: [
             { label: 'Three', value: 'three', display: 3 },
             { label: 'One', value: 'one', display: 1 },
@@ -304,7 +299,8 @@ export const ATTRIBUTE_LIST: Attribute[] = [
         vo_datatype: null,
         vo_size: null,
         id_criteria_family: 1,
-        id_output_category: 1
+        id_output_category: 1,
+        id_detail_output_category: null
     },
     {
         id: 3,
@@ -328,10 +324,7 @@ export const ATTRIBUTE_LIST: Attribute[] = [
         renderer_config: null,
         order_by: true,
         archive: false,
-        detail: false,
-        display_detail: 3,
-        renderer_detail: null,
-        renderer_detail_config: null,
+        detail_display: 3,
         options: [
             { label: 'Three', value: 'three', display: 3 },
             { label: 'One', value: 'one', display: 1 },
@@ -344,7 +337,8 @@ export const ATTRIBUTE_LIST: Attribute[] = [
         vo_datatype: null,
         vo_size: null,
         id_criteria_family: null,
-        id_output_category: null
+        id_output_category: null,
+        id_detail_output_category: null
     },
     {
         id: 4,
@@ -368,10 +362,7 @@ export const ATTRIBUTE_LIST: Attribute[] = [
         renderer_config: null,
         order_by: true,
         archive: false,
-        detail: false,
-        display_detail: 4,
-        renderer_detail: 'spectra_graph',
-        renderer_detail_config: null,
+        detail_display: 4,
         options: [
             { label: 'Three', value: 'three', display: 3 },
             { label: 'One', value: 'one', display: 1 },
@@ -385,6 +376,7 @@ export const ATTRIBUTE_LIST: Attribute[] = [
         vo_size: null,
         id_criteria_family: null,
         id_output_category: 1,
+        id_detail_output_category: null
     }
 ];
 
@@ -410,10 +402,7 @@ export const ATTRIBUTE: Attribute = {
     renderer_config: null,
     order_by: true,
     archive: false,
-    detail: true,
-    display_detail: 2,
-    renderer_detail: null,
-    renderer_detail_config: null,
+    detail_display: 2,
     options: [
         { label: 'Three', value: 'three', display: 3 },
         { label: 'One', value: 'one', display: 1 },
@@ -426,7 +415,8 @@ export const ATTRIBUTE: Attribute = {
     vo_datatype: null,
     vo_size: null,
     id_criteria_family: null,
-    id_output_category: 2
+    id_output_category: 2,
+    id_detail_output_category: null
 };
 
 export const COLUMN_LIST: Column[] = [
diff --git a/conf-dev/create-db.sh b/conf-dev/create-db.sh
index 85345caa90455d1001ccb6a8443c2682d4593b2b..0136c54f99c67ca202c72a8dc41a406ea75494a8 100644
--- a/conf-dev/create-db.sh
+++ b/conf-dev/create-db.sh
@@ -27,36 +27,39 @@ curl -d '{"label":"Default","display":10,"opened":true}' --header 'Content-Type:
 curl -d '{"label":"Default","display":10,"opened":true}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/output-family
 curl -d '{"label":"Default","display":10}' --header 'Content-Type: application/json' -X POST http://localhost/output-family/1/output-category
 
-curl -d '{"id":1,"name":"num","label":"num","form_label":"num","description":null,"primary_key":true,"output_display":10,"criteria_display":10,"search_type":"field","type":"decimal","operator":"eq","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":"detail-link","renderer_config":{"display":"text","blank":true},"display_detail":10,"selected":true,"order_by":true,"archive":false,"detail":true,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":1,"id_output_category":1}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
-curl -d '{"id":2,"name":"alpha","label":"alpha","form_label":"alpha","description":null,"primary_key":false,"output_display":20,"criteria_display":20,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":20,"selected":true,"order_by":true,"archive":false,"detail":true,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":1}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
-curl -d '{"id":3,"name":"delta","label":"delta","form_label":"delta","description":null,"primary_key":false,"output_display":30,"criteria_display":30,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":30,"selected":true,"order_by":true,"archive":false,"detail":true,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":1}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
-curl -d '{"id":4,"name":"selmag","label":"selmag","form_label":"selmag","description":null,"primary_key":false,"output_display":40,"criteria_display":40,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":40,"selected":true,"order_by":true,"archive":false,"detail":true,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":1}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
-curl -d '{"id":5,"name":"errselmag","label":"errselmag","form_label":"errselmag","description":null,"primary_key":false,"output_display":50,"criteria_display":50,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":50,"selected":true,"order_by":true,"archive":false,"detail":true,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":1}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
-curl -d '{"id":6,"name":"pointing","label":"pointing","form_label":"pointing","description":null,"primary_key":false,"output_display":60,"criteria_display":60,"search_type":null,"type":"string","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":60,"selected":true,"order_by":true,"archive":false,"detail":true,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":1}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
-curl -d '{"id":7,"name":"quadrant","label":"quadrant","form_label":"quadrant","description":null,"primary_key":false,"output_display":70,"criteria_display":70,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":70,"selected":true,"order_by":true,"archive":false,"detail":true,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":1}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
-curl -d '{"id":8,"name":"zspec","label":"zspec","form_label":"zspec","description":null,"primary_key":false,"output_display":80,"criteria_display":80,"search_type":"between","type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":80,"selected":true,"order_by":true,"archive":false,"detail":true,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":1,"id_output_category":1}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
-curl -d '{"id":9,"name":"zflg","label":"zflg","form_label":"zflg","description":null,"primary_key":false,"output_display":90,"criteria_display":90,"search_type":"datalist","type":"decimal","operator":"in","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":90,"selected":true,"order_by":true,"archive":false,"detail":true,"renderer_detail":null,"renderer_detail_config":null,"options":[{"label":"1,2,3,4,9","value":"1,2,3,4,9","display":10},{"label":"2,3,4,9","value":"2,3,4,9","display":20},{"label":"3,4","value":"3,4","display":30},{"label":"1,2,3,4,9,21,22,23,24,29","value":"1,2,3,4,9,21,22,23,24,29","display":40},{"label":"2,3,4,9,22,23,24,29","value":"2,3,4,9,22,23,24,29","display":50}],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":1,"id_output_category":1}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
-curl -d '{"id":10,"name":"norm","label":"norm","form_label":"norm","description":null,"primary_key":false,"output_display":100,"criteria_display":100,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":100,"selected":true,"order_by":true,"archive":false,"detail":true,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":1}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
-curl -d '{"id":11,"name":"epoch","label":"epoch","form_label":"epoch","description":null,"primary_key":false,"output_display":110,"criteria_display":110,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":110,"selected":true,"order_by":true,"archive":false,"detail":true,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
-curl -d '{"id":12,"name":"photomask","label":"photomask","form_label":"photomask","description":null,"primary_key":false,"output_display":120,"criteria_display":120,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":120,"selected":true,"order_by":true,"archive":false,"detail":true,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
-curl -d '{"id":56,"name":"spec1d","label":"spec1d","form_label":"spec1d","description":null,"primary_key":false,"output_display":560,"criteria_display":560,"search_type":null,"type":"text","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":"download","renderer_config":{"display":"icon-button","text":"DOWNLOAD","icon":"fas fa-download"},"display_detail":560,"selected":true,"order_by":false,"archive":false,"detail":true,"renderer_detail":"spectra_graph","renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":1}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
-curl -d '{"id":57,"name":"spec1dnoise","label":"spec1dnoise","form_label":"spec1dnoise","description":null,"primary_key":false,"output_display":570,"criteria_display":570,"search_type":null,"type":"text","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":"download","renderer_config":{"display":"icon-button","text":"DOWNLOAD","icon":"fas fa-download"},"display_detail":570,"selected":true,"order_by":false,"archive":false,"detail":true,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":1}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
-curl -d '{"id":58,"name":"spec1dsky","label":"spec1dsky","form_label":"spec1dsky","description":null,"primary_key":false,"output_display":580,"criteria_display":580,"search_type":null,"type":"text","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":"download","renderer_config":{"display":"icon-button","text":"DOWNLOAD","icon":"fas fa-download"},"display_detail":580,"selected":true,"order_by":false,"archive":false,"detail":true,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":1}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
+curl -d '{"id":1,"name":"num","label":"num","form_label":"num","description":null,"primary_key":true,"output_display":10,"criteria_display":10,"search_type":"field","type":"decimal","operator":"eq","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":"detail-link","renderer_config":{"display":"text"},"detail_display":10,"selected":true,"order_by":true,"archive":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":1,"id_output_category":1,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
+curl -d '{"id":2,"name":"alpha","label":"alpha","form_label":"alpha","description":null,"primary_key":false,"output_display":20,"criteria_display":20,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":20,"selected":true,"order_by":true,"archive":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":1,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
+curl -d '{"id":3,"name":"delta","label":"delta","form_label":"delta","description":null,"primary_key":false,"output_display":30,"criteria_display":30,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":30,"selected":true,"order_by":true,"archive":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":1,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
+curl -d '{"id":4,"name":"selmag","label":"selmag","form_label":"selmag","description":null,"primary_key":false,"output_display":40,"criteria_display":40,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":40,"selected":true,"order_by":true,"archive":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":1,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
+curl -d '{"id":5,"name":"errselmag","label":"errselmag","form_label":"errselmag","description":null,"primary_key":false,"output_display":50,"criteria_display":50,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":50,"selected":true,"order_by":true,"archive":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":1,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
+curl -d '{"id":6,"name":"pointing","label":"pointing","form_label":"pointing","description":null,"primary_key":false,"output_display":60,"criteria_display":60,"search_type":null,"type":"string","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":60,"selected":true,"order_by":true,"archive":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":1,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
+curl -d '{"id":7,"name":"quadrant","label":"quadrant","form_label":"quadrant","description":null,"primary_key":false,"output_display":70,"criteria_display":70,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":70,"selected":true,"order_by":true,"archive":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":1,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
+curl -d '{"id":8,"name":"zspec","label":"zspec","form_label":"zspec","description":null,"primary_key":false,"output_display":80,"criteria_display":80,"search_type":"between","type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":80,"selected":true,"order_by":true,"archive":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":1,"id_output_category":1,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
+curl -d '{"id":9,"name":"zflg","label":"zflg","form_label":"zflg","description":null,"primary_key":false,"output_display":90,"criteria_display":90,"search_type":"datalist","type":"decimal","operator":"in","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":90,"selected":true,"order_by":true,"archive":false,"options":[{"label":"1,2,3,4,9","value":"1,2,3,4,9","display":10},{"label":"2,3,4,9","value":"2,3,4,9","display":20},{"label":"3,4","value":"3,4","display":30},{"label":"1,2,3,4,9,21,22,23,24,29","value":"1,2,3,4,9,21,22,23,24,29","display":40},{"label":"2,3,4,9,22,23,24,29","value":"2,3,4,9,22,23,24,29","display":50}],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":1,"id_output_category":1,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
+curl -d '{"id":10,"name":"norm","label":"norm","form_label":"norm","description":null,"primary_key":false,"output_display":100,"criteria_display":100,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":100,"selected":true,"order_by":true,"archive":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":1,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
+curl -d '{"id":11,"name":"epoch","label":"epoch","form_label":"epoch","description":null,"primary_key":false,"output_display":110,"criteria_display":110,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":110,"selected":true,"order_by":true,"archive":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":null,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
+curl -d '{"id":12,"name":"photomask","label":"photomask","form_label":"photomask","description":null,"primary_key":false,"output_display":120,"criteria_display":120,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":120,"selected":true,"order_by":true,"archive":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":null,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
+curl -d '{"id":56,"name":"spec1d","label":"spec1d","form_label":"spec1d","description":null,"primary_key":false,"output_display":560,"criteria_display":560,"search_type":null,"type":"text","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":"download","renderer_config":{"display":"icon-button","text":"DOWNLOAD","icon":"fas fa-download"},"detail_display":560,"selected":true,"order_by":false,"archive":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":1,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
+curl -d '{"id":57,"name":"spec1dnoise","label":"spec1dnoise","form_label":"spec1dnoise","description":null,"primary_key":false,"output_display":570,"criteria_display":570,"search_type":null,"type":"text","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":"download","renderer_config":{"display":"icon-button","text":"DOWNLOAD","icon":"fas fa-download"},"detail_display":570,"selected":true,"order_by":false,"archive":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":1,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
+curl -d '{"id":58,"name":"spec1dsky","label":"spec1dsky","form_label":"spec1dsky","description":null,"primary_key":false,"output_display":580,"criteria_display":580,"search_type":null,"type":"text","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":"download","renderer_config":{"display":"icon-button","text":"DOWNLOAD","icon":"fas fa-download"},"detail_display":580,"selected":true,"order_by":false,"archive":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":1,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/attribute
+
+# Add vipers_dr2_w1 detail config
+curl -d '{"enabled":true,"content":"<div class=\"row\">\n    <div class=\"col col-md-8 col-sm-12\">\n        <app-display-spectra\n            [object]=\"context.object\"\n            [datasetName]=\"context.datasetName\"\n            [attributeList]=\"context.attributeList\"\n            [attributeSpectraId]=\"56\"\n            [attributeZId]=\"8\">\n        </app-display-spectra>\n    </div>\n    <div class=\"col col-md-4 col-sm-12\">\n        <app-display-ra-dec\n            [object]=\"context.object\"\n            [attributeList]=\"context.attributeList\"\n            [attributeRaId]=\"2\"\n            [attributeDecId]=\"3\">\n        </app-display-ra-dec>\n        <app-display-object \n            [object]=\"context.object\"\n            [attributeList]=\"context.attributeList\"\n            [outputFamilyList]=\"context.outputFamilyList\"\n            [outputCategoryList]=\"context.outputCategoryList\">\n        </app-display-object>\n    </div>\n</div>"}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vipers_dr2_w1/detail-config
 
 # Add sp_cards attributes
 curl -d '{"label":"Card","display":10,"opened":true}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/sp_cards/criteria-family
 curl -d '{"label":"Default","display":10,"opened":true}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/sp_cards/output-family
 curl -d '{"label":"Default","display":10}' --header 'Content-Type: application/json' -X POST http://localhost/output-family/2/output-category
 
-curl -d '{"id":1,"name":"acronym","label":"acronym","form_label":"Product Acronym","description":null,"primary_key":true,"output_display":10,"criteria_display":10,"search_type":"select","type":"string","operator":"eq","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":[],"display_detail":10,"selected":true,"order_by":true,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":[{"label":"LAMTEST","value":"LAMTEST","display":10},{"label":"LAMTEST_DC1","value":"LAMTEST_DC1","display":20},{"label":"MXT-EVT-CAL","value":"MXT-EVT-CAL","display":30},{"label":"OBLC_ECL","value":"OBLC_ECL","display":40}],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":2,"id_output_category":2}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/sp_cards/attribute
-curl -d '{"id":2,"name":"sp_id","label":"sp_id","form_label":"SP IAP ID","description":null,"primary_key":false,"output_display":20,"criteria_display":20,"search_type":null,"type":"integer","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":20,"selected":true,"order_by":true,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":2}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/sp_cards/attribute
-curl -d '{"id":3,"name":"version","label":"version","form_label":"Version","description":null,"primary_key":false,"output_display":30,"criteria_display":30,"search_type":null,"type":"float","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":30,"selected":true,"order_by":true,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":2}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/sp_cards/attribute
-curl -d '{"id":4,"name":"program","label":"program","form_label":"Program","description":null,"primary_key":false,"output_display":40,"criteria_display":40,"search_type":"select","type":"string","operator":"eq","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":40,"selected":true,"order_by":true,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":[{"label":"CP","value":"CP","display":10},{"label":"GP","value":"GP","display":20}],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":2,"id_output_category":2}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/sp_cards/attribute
-curl -d '{"id":5,"name":"instrument","label":"instrument","form_label":"Instrument","description":null,"primary_key":false,"output_display":50,"criteria_display":50,"search_type":"select","type":"string","operator":"eq","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":50,"selected":true,"order_by":true,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":[{"label":"ALL","value":"ALL","display":10},{"label":"MXT","value":"MXT","display":20}],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":2,"id_output_category":2}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/sp_cards/attribute
-curl -d '{"id":6,"name":"type","label":"type","form_label":"Type","description":null,"primary_key":false,"output_display":60,"criteria_display":60,"search_type":null,"type":"string","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":60,"selected":true,"order_by":true,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":2}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/sp_cards/attribute
-curl -d '{"id":7,"name":"json_schema","label":"json_schema","form_label":"JSON Schema (original)","description":null,"primary_key":false,"output_display":70,"criteria_display":70,"search_type":null,"type":"json","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":"json","renderer_config":[],"display_detail":70,"selected":true,"order_by":true,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":2}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/sp_cards/attribute
-curl -d '{"id":8,"name":"search_kw","label":"search_kw","form_label":"KWs searchable","description":null,"primary_key":false,"output_display":80,"criteria_display":80,"search_type":null,"type":"json","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":"json","renderer_config":[],"display_detail":80,"selected":true,"order_by":true,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":2}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/sp_cards/attribute
-curl -d '{"id":9,"name":"json_schema_uploaded","label":"json_schema_uploaded","form_label":"json_schema_uploaded","description":null,"primary_key":false,"output_display":90,"criteria_display":90,"search_type":null,"type":"json","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":90,"selected":true,"order_by":true,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/sp_cards/attribute
+curl -d '{"id":1,"name":"acronym","label":"acronym","form_label":"Product Acronym","description":null,"primary_key":true,"output_display":10,"criteria_display":10,"search_type":"select","type":"string","operator":"eq","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":[],"detail_display":10,"selected":true,"order_by":true,"archive":false,"detail":false,"options":[{"label":"LAMTEST","value":"LAMTEST","display":10},{"label":"LAMTEST_DC1","value":"LAMTEST_DC1","display":20},{"label":"MXT-EVT-CAL","value":"MXT-EVT-CAL","display":30},{"label":"OBLC_ECL","value":"OBLC_ECL","display":40}],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":2,"id_output_category":2,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/sp_cards/attribute
+curl -d '{"id":2,"name":"sp_id","label":"sp_id","form_label":"SP IAP ID","description":null,"primary_key":false,"output_display":20,"criteria_display":20,"search_type":null,"type":"integer","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":20,"selected":true,"order_by":true,"archive":false,"detail":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":2,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/sp_cards/attribute
+curl -d '{"id":3,"name":"version","label":"version","form_label":"Version","description":null,"primary_key":false,"output_display":30,"criteria_display":30,"search_type":null,"type":"float","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":30,"selected":true,"order_by":true,"archive":false,"detail":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":2,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/sp_cards/attribute
+curl -d '{"id":4,"name":"program","label":"program","form_label":"Program","description":null,"primary_key":false,"output_display":40,"criteria_display":40,"search_type":"select","type":"string","operator":"eq","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":40,"selected":true,"order_by":true,"archive":false,"detail":false,"options":[{"label":"CP","value":"CP","display":10},{"label":"GP","value":"GP","display":20}],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":2,"id_output_category":2,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/sp_cards/attribute
+curl -d '{"id":5,"name":"instrument","label":"instrument","form_label":"Instrument","description":null,"primary_key":false,"output_display":50,"criteria_display":50,"search_type":"select","type":"string","operator":"eq","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":50,"selected":true,"order_by":true,"archive":false,"detail":false,"options":[{"label":"ALL","value":"ALL","display":10},{"label":"MXT","value":"MXT","display":20}],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":2,"id_output_category":2,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/sp_cards/attribute
+curl -d '{"id":6,"name":"type","label":"type","form_label":"Type","description":null,"primary_key":false,"output_display":60,"criteria_display":60,"search_type":null,"type":"string","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":60,"selected":true,"order_by":true,"archive":false,"detail":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":2,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/sp_cards/attribute
+curl -d '{"id":7,"name":"json_schema","label":"json_schema","form_label":"JSON Schema (original)","description":null,"primary_key":false,"output_display":70,"criteria_display":70,"search_type":null,"type":"json","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":"json","renderer_config":[],"detail_display":70,"selected":true,"order_by":true,"archive":false,"detail":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":2,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/sp_cards/attribute
+curl -d '{"id":8,"name":"search_kw","label":"search_kw","form_label":"KWs searchable","description":null,"primary_key":false,"output_display":80,"criteria_display":80,"search_type":null,"type":"json","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":"json","renderer_config":[],"detail_display":80,"selected":true,"order_by":true,"archive":false,"detail":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":2,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/sp_cards/attribute
+curl -d '{"id":9,"name":"json_schema_uploaded","label":"json_schema_uploaded","form_label":"json_schema_uploaded","description":null,"primary_key":false,"output_display":90,"criteria_display":90,"search_type":null,"type":"json","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":90,"selected":true,"order_by":true,"archive":false,"detail":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":null,"id_detail_output_category":null,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/sp_cards/attribute
 
 # Add observations attributes
 curl -d '{"label":"Default","display":10,"opened":true}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/observations/criteria-family
@@ -64,28 +67,31 @@ curl -d '{"label":"Default parameters","display":10,"opened":true}' --header 'Co
 curl -d '{"label":"Astrometry","display":10}' --header 'Content-Type: application/json' -X POST http://localhost/output-family/3/output-category
 curl -d '{"label":"Observation","display":10}' --header 'Content-Type: application/json' -X POST http://localhost/output-family/3/output-category
 
-curl -d '{"id":1,"name":"id","label":"id","form_label":"id","description":"Observation Ident","primary_key":true,"output_display":10,"criteria_display":10,"search_type":null,"type":"integer","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":10,"selected":true,"order_by":true,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":3}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/observations/attribute
-curl -d '{"id":2,"name":"ra","label":"ra","form_label":"Alpha J2000","description":"Alpha J2000","primary_key":false,"output_display":20,"criteria_display":20,"search_type":null,"type":"float","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":20,"selected":true,"order_by":true,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":3}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/observations/attribute
-curl -d '{"id":3,"name":"dec","label":"dec","form_label":"Delta J2000","description":"Delta J2000","primary_key":false,"output_display":30,"criteria_display":30,"search_type":null,"type":"float","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":30,"selected":true,"order_by":true,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":3}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/observations/attribute
-curl -d '{"id":4,"name":"date_time","label":"date_time","form_label":"Date Time","description":"Date Time","primary_key":false,"output_display":40,"criteria_display":40,"search_type":"between-date","type":"datetime","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":40,"selected":true,"order_by":true,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":1,"id_output_category":4}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/observations/attribute
-curl -d '{"id":5,"name":"exposure_time","label":"exposure_time","form_label":"Exposure Time","description":"Exposure Time","primary_key":false,"output_display":50,"criteria_display":50,"search_type":"between","type":"integer","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":50,"selected":true,"order_by":true,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":1,"id_output_category":4}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/observations/attribute
-curl -d '{"id":6,"name":"filter","label":"filter","form_label":"Filter","description":"Filter","primary_key":false,"output_display":60,"criteria_display":60,"search_type":"select","type":"string","operator":"eq","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":60,"selected":true,"order_by":true,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":[{"label":"Halpha","value":"Halpha","display":10},{"label":"SDSS g","value":"SDSS g","display":20}],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":1,"id_output_category":4}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/observations/attribute
-curl -d '{"id":7,"name":"object_name","label":"object_name","form_label":"Object name","description":"Object name","primary_key":false,"output_display":70,"criteria_display":70,"search_type":"field","type":"string","operator":"lk","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":"link","renderer_config":{"href":"http:\/\/cdsportal.u-strasbg.fr\/?target=$value","display":"text","text":"$value","icon":"fas fa-link","blank":true},"display_detail":70,"selected":true,"order_by":true,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":1,"id_output_category":4}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/observations/attribute
-curl -d '{"id":8,"name":"fits_file","label":"fits_file","form_label":"fits_file","description":null,"primary_key":false,"output_display":80,"criteria_display":80,"search_type":null,"type":"string","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":"download","renderer_config":{"display":"icon-button","text":"DOWNLOAD","icon":"fas fa-download"},"display_detail":80,"selected":true,"order_by":false,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":4}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/observations/attribute
-curl -d '{"id":9,"name":"fits_png","label":"fits_png","form_label":"fits_png","description":null,"primary_key":false,"output_display":90,"criteria_display":90,"search_type":null,"type":"string","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":"image","renderer_config":{"type":"fits","display":"modal","width":"50","height":"50"},"display_detail":90,"selected":true,"order_by":false,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":4}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/observations/attribute
+curl -d '{"id":1,"name":"id","label":"id","form_label":"id","description":"Observation Ident","primary_key":true,"output_display":10,"criteria_display":10,"search_type":null,"type":"integer","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":10,"selected":true,"order_by":true,"archive":false,"detail":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":3,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/observations/attribute
+curl -d '{"id":2,"name":"ra","label":"ra","form_label":"Alpha J2000","description":"Alpha J2000","primary_key":false,"output_display":20,"criteria_display":20,"search_type":null,"type":"float","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":20,"selected":true,"order_by":true,"archive":false,"detail":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":3,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/observations/attribute
+curl -d '{"id":3,"name":"dec","label":"dec","form_label":"Delta J2000","description":"Delta J2000","primary_key":false,"output_display":30,"criteria_display":30,"search_type":null,"type":"float","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":30,"selected":true,"order_by":true,"archive":false,"detail":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":3,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/observations/attribute
+curl -d '{"id":4,"name":"date_time","label":"date_time","form_label":"Date Time","description":"Date Time","primary_key":false,"output_display":40,"criteria_display":40,"search_type":"between-date","type":"datetime","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":40,"selected":true,"order_by":true,"archive":false,"detail":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":1,"id_output_category":4,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/observations/attribute
+curl -d '{"id":5,"name":"exposure_time","label":"exposure_time","form_label":"Exposure Time","description":"Exposure Time","primary_key":false,"output_display":50,"criteria_display":50,"search_type":"between","type":"integer","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":50,"selected":true,"order_by":true,"archive":false,"detail":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":1,"id_output_category":4,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/observations/attribute
+curl -d '{"id":6,"name":"filter","label":"filter","form_label":"Filter","description":"Filter","primary_key":false,"output_display":60,"criteria_display":60,"search_type":"select","type":"string","operator":"eq","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":60,"selected":true,"order_by":true,"archive":false,"detail":false,"options":[{"label":"Halpha","value":"Halpha","display":10},{"label":"SDSS g","value":"SDSS g","display":20}],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":1,"id_output_category":4,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/observations/attribute
+curl -d '{"id":7,"name":"object_name","label":"object_name","form_label":"Object name","description":"Object name","primary_key":false,"output_display":70,"criteria_display":70,"search_type":"field","type":"string","operator":"lk","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":"link","renderer_config":{"href":"http:\/\/cdsportal.u-strasbg.fr\/?target=$value","display":"text","text":"$value","icon":"fas fa-link","blank":true},"detail_display":70,"selected":true,"order_by":true,"archive":false,"detail":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":1,"id_output_category":4,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/observations/attribute
+curl -d '{"id":8,"name":"fits_file","label":"fits_file","form_label":"fits_file","description":null,"primary_key":false,"output_display":80,"criteria_display":80,"search_type":null,"type":"string","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":"download","renderer_config":{"display":"icon-button","text":"DOWNLOAD","icon":"fas fa-download"},"detail_display":80,"selected":true,"order_by":false,"archive":false,"detail":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":4,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/observations/attribute
+curl -d '{"id":9,"name":"fits_png","label":"fits_png","form_label":"fits_png","description":null,"primary_key":false,"output_display":90,"criteria_display":90,"search_type":null,"type":"string","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":"image","renderer_config":{"type":"fits","display":"modal","width":"50","height":"50"},"detail_display":90,"selected":true,"order_by":false,"archive":false,"detail":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":4,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/observations/attribute
+
+# Add observations detail config
+curl -d '{"enabled":true,"content":"<div class=\"row justify-content-center\">\n    <div class=\"col col-lg-10 col-xl-8 mt-4\">\n        <div class=\"row\">\n            <div class=\"col-12\">\n                <app-display-ra-dec\n                    [object]=\"context.object\"\n                    [attributeList]=\"context.attributeList\"\n                    [attributeRaId]=\"2\"\n                    [attributeDecId]=\"3\">\n                </app-display-ra-dec>\n            </div>\n        </div>\n\n        <app-display-object \n            [object]=\"context.object\"\n            [attributeList]=\"context.attributeList\"\n            [outputFamilyList]=\"context.outputFamilyList\"\n            [outputCategoryList]=\"context.outputCategoryList\">\n        </app-display-object>\n    </div>\n</div>"}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/observations/detail-config
 
 # Add vvds_f02_udeep attributes
 curl -d '{"label":"Default","display":10,"opened":true}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vvds_f02_udeep/criteria-family
 curl -d '{"label":"Default","display":10,"opened":true}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vvds_f02_udeep/output-family
 curl -d '{"label":"Default","display":10}' --header 'Content-Type: application/json' -X POST http://localhost/output-family/4/output-category
 
-curl -d '{"id":1,"name":"vvds_ident","label":"vvds_ident","form_label":"vvds_ident","description":null,"primary_key":true,"output_display":10,"criteria_display":10,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":10,"selected":true,"order_by":true,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":5}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vvds_f02_udeep/attribute
-curl -d '{"id":2,"name":"id_iau","label":"id_iau","form_label":"id_iau","description":null,"primary_key":false,"output_display":20,"criteria_display":20,"search_type":null,"type":"string","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":20,"selected":true,"order_by":true,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":5}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vvds_f02_udeep/attribute
-curl -d '{"id":3,"name":"alpha","label":"alpha","form_label":"alpha","description":null,"primary_key":false,"output_display":30,"criteria_display":30,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":30,"selected":true,"order_by":true,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":5}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vvds_f02_udeep/attribute
-curl -d '{"id":4,"name":"delta","label":"delta","form_label":"delta","description":null,"primary_key":false,"output_display":40,"criteria_display":40,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":40,"selected":true,"order_by":true,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":5}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vvds_f02_udeep/attribute
-curl -d '{"id":5,"name":"z_spec","label":"z_spec","form_label":"z_spec","description":null,"primary_key":false,"output_display":50,"criteria_display":50,"search_type":"between","type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":50,"selected":true,"order_by":true,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":4,"id_output_category":5}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vvds_f02_udeep/attribute
-curl -d '{"id":6,"name":"zflag","label":"zflag","form_label":"zflag","description":null,"primary_key":false,"output_display":60,"criteria_display":60,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":60,"selected":true,"order_by":true,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":5}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vvds_f02_udeep/attribute
-curl -d '{"id":7,"name":"sel_mag","label":"sel_mag","form_label":"sel_mag","description":null,"primary_key":false,"output_display":70,"criteria_display":70,"search_type":"between","type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"display_detail":70,"selected":true,"order_by":true,"archive":false,"detail":false,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":4,"id_output_category":5}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vvds_f02_udeep/attribute
+curl -d '{"id":1,"name":"vvds_ident","label":"vvds_ident","form_label":"vvds_ident","description":null,"primary_key":true,"output_display":10,"criteria_display":10,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":10,"selected":true,"order_by":true,"archive":false,"detail":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":5,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vvds_f02_udeep/attribute
+curl -d '{"id":2,"name":"id_iau","label":"id_iau","form_label":"id_iau","description":null,"primary_key":false,"output_display":20,"criteria_display":20,"search_type":null,"type":"string","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":20,"selected":true,"order_by":true,"archive":false,"detail":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":5,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vvds_f02_udeep/attribute
+curl -d '{"id":3,"name":"alpha","label":"alpha","form_label":"alpha","description":null,"primary_key":false,"output_display":30,"criteria_display":30,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":30,"selected":true,"order_by":true,"archive":false,"detail":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":5,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vvds_f02_udeep/attribute
+curl -d '{"id":4,"name":"delta","label":"delta","form_label":"delta","description":null,"primary_key":false,"output_display":40,"criteria_display":40,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":40,"selected":true,"order_by":true,"archive":false,"detail":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":5,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vvds_f02_udeep/attribute
+curl -d '{"id":5,"name":"z_spec","label":"z_spec","form_label":"z_spec","description":null,"primary_key":false,"output_display":50,"criteria_display":50,"search_type":"between","type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":50,"selected":true,"order_by":true,"archive":false,"detail":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":4,"id_output_category":5,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vvds_f02_udeep/attribute
+curl -d '{"id":6,"name":"zflag","label":"zflag","form_label":"zflag","description":null,"primary_key":false,"output_display":60,"criteria_display":60,"search_type":null,"type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":60,"selected":true,"order_by":true,"archive":false,"detail":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":5,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vvds_f02_udeep/attribute
+curl -d '{"id":7,"name":"sel_mag","label":"sel_mag","form_label":"sel_mag","description":null,"primary_key":false,"output_display":70,"criteria_display":70,"search_type":"between","type":"decimal","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"detail_display":70,"selected":true,"order_by":true,"archive":false,"detail":false,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":4,"id_output_category":5,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/vvds_f02_udeep/attribute
 
 # Add products attributes
 curl -d '{"label":"Obs","display":10,"opened":true}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/criteria-family
@@ -94,22 +100,22 @@ curl -d '{"label":"Default","display":10,"opened":true}' --header 'Content-Type:
 curl -d '{"label":"Default","display":10}' --header 'Content-Type: application/json' -X POST http://localhost/output-family/5/output-category
 curl -d '{"label":"Additionnal","display":20}' --header 'Content-Type: application/json' -X POST http://localhost/output-family/5/output-category
 
-curl -d '{"id":1,"name":"product_id","label":"product_id","form_label":"Product ID","description":null,"primary_key":true,"output_display":10,"criteria_display":10,"search_type":"field","type":"integer","operator":"eq","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":[],"selected":true,"order_by":true,"archive":false,"detail":false,"display_detail":10,"renderer_detail":null,"renderer_detail_config":null,"options":[],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":5,"id_output_category":6}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
-curl -d '{"id":2,"name":"obs_id","label":"obs_id","form_label":"Observation ID","description":null,"primary_key":false,"output_display":20,"criteria_display":20,"search_type":"field","type":"bigint","operator":"eq","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":true,"order_by":true,"archive":false,"detail":false,"display_detail":20,"renderer_detail":null,"renderer_detail_config":null,"options":[],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":5,"id_output_category":6}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
-curl -d '{"id":3,"name":"acronym","label":"acronym","form_label":"Product Acronym","description":null,"primary_key":false,"output_display":30,"criteria_display":30,"search_type":"select","type":"string","operator":"eq","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":true,"order_by":true,"archive":false,"detail":false,"display_detail":30,"renderer_detail":null,"renderer_detail_config":null,"options":[{"label":"ECL-DET-UBC","value":"ECL-DET-UBC","display":10},{"label":"OBLC_ECL","value":"OBLC_ECL","display":20}],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":6,"id_output_category":6}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
-curl -d '{"id":4,"name":"url","label":"url","form_label":"Product URL","description":null,"primary_key":false,"output_display":40,"criteria_display":40,"search_type":null,"type":"text","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":"link","renderer_config":{"href":"$value","display":"text","text":"$value","icon":"fas fa-link","blank":true},"selected":true,"order_by":true,"archive":false,"detail":false,"display_detail":40,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":6}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
-curl -d '{"id":5,"name":"criteria","label":"criteria","form_label":"Product criteria (based on KWs searchables)","description":null,"primary_key":false,"output_display":50,"criteria_display":50,"search_type":"svom_json_kw","type":"json","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":"json","renderer_config":[],"selected":false,"order_by":true,"archive":false,"detail":false,"display_detail":50,"renderer_detail":null,"renderer_detail_config":null,"options":[],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":6,"id_output_category":7}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
-curl -d '{"id":6,"name":"added_at","label":"added_at","form_label":"Added at","description":null,"primary_key":false,"output_display":60,"criteria_display":60,"search_type":"between-date","type":"datetime","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":true,"order_by":true,"archive":false,"detail":false,"display_detail":60,"renderer_detail":null,"renderer_detail_config":null,"options":[],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":5,"id_output_category":6}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
-curl -d '{"id":7,"name":"product_version","label":"product_version","form_label":"Version","description":null,"primary_key":false,"output_display":70,"criteria_display":70,"search_type":null,"type":"integer","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":true,"order_by":true,"archive":false,"detail":false,"display_detail":70,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":6}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
-curl -d '{"id":8,"name":"program","label":"program","form_label":"Program","description":null,"primary_key":false,"output_display":80,"criteria_display":80,"search_type":"select","type":"string","operator":"eq","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":false,"order_by":true,"archive":false,"detail":false,"display_detail":80,"renderer_detail":null,"renderer_detail_config":null,"options":[{"label":"CoreProgram","value":"CoreProgram","display":10},{"label":"GeneralProgram","value":"GeneralProgram","display":20}],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":6,"id_output_category":7}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
-curl -d '{"id":9,"name":"instrument","label":"instrument","form_label":"Instrument","description":null,"primary_key":false,"output_display":90,"criteria_display":90,"search_type":"select","type":"string","operator":"eq","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":false,"order_by":true,"archive":false,"detail":false,"display_detail":90,"renderer_detail":null,"renderer_detail_config":null,"options":[{"label":"ECL","value":"ECL","display":10}],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":6,"id_output_category":7}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
-curl -d '{"id":10,"name":"type","label":"type","form_label":"Type","description":null,"primary_key":false,"output_display":100,"criteria_display":100,"search_type":null,"type":"string","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":false,"order_by":true,"archive":false,"detail":false,"display_detail":100,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":7}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
-curl -d '{"id":11,"name":"upload_filename","label":"upload_filename","form_label":"Filename","description":null,"primary_key":false,"output_display":110,"criteria_display":110,"search_type":null,"type":"string","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":true,"order_by":true,"archive":false,"detail":false,"display_detail":110,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":6}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
-curl -d '{"id":12,"name":"burst_id","label":"burst_id","form_label":"Burst ID","description":null,"primary_key":false,"output_display":120,"criteria_display":120,"search_type":"field","type":"string","operator":"eq","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":true,"order_by":true,"archive":false,"detail":false,"display_detail":120,"renderer_detail":null,"renderer_detail_config":null,"options":[],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":5,"id_output_category":6}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
-curl -d '{"id":13,"name":"pipeline_version","label":"pipeline_version","form_label":"Pipeline version","description":null,"primary_key":false,"output_display":130,"criteria_display":130,"search_type":null,"type":"float","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":false,"order_by":true,"archive":false,"detail":false,"display_detail":130,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":7}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
-curl -d '{"id":14,"name":"schema_version","label":"schema_version","form_label":"Schema version","description":null,"primary_key":false,"output_display":140,"criteria_display":140,"search_type":null,"type":"float","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":false,"order_by":true,"archive":false,"detail":false,"display_detail":140,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":7}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
-curl -d '{"id":15,"name":"src_id","label":"src_id","form_label":"SRC ID","description":null,"primary_key":false,"output_display":150,"criteria_display":150,"search_type":null,"type":"integer","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":false,"order_by":true,"archive":false,"detail":false,"display_detail":150,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":7}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
+curl -d '{"id":1,"name":"product_id","label":"product_id","form_label":"Product ID","description":null,"primary_key":true,"output_display":10,"criteria_display":10,"search_type":"field","type":"integer","operator":"eq","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":[],"selected":true,"order_by":true,"archive":false,"detail":false,"detail_display":10,"options":[],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":5,"id_output_category":6,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
+curl -d '{"id":2,"name":"obs_id","label":"obs_id","form_label":"Observation ID","description":null,"primary_key":false,"output_display":20,"criteria_display":20,"search_type":"field","type":"bigint","operator":"eq","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":true,"order_by":true,"archive":false,"detail":false,"detail_display":20,"options":[],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":5,"id_output_category":6,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
+curl -d '{"id":3,"name":"acronym","label":"acronym","form_label":"Product Acronym","description":null,"primary_key":false,"output_display":30,"criteria_display":30,"search_type":"select","type":"string","operator":"eq","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":true,"order_by":true,"archive":false,"detail":false,"detail_display":30,"options":[{"label":"ECL-DET-UBC","value":"ECL-DET-UBC","display":10},{"label":"OBLC_ECL","value":"OBLC_ECL","display":20}],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":6,"id_output_category":6,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
+curl -d '{"id":4,"name":"url","label":"url","form_label":"Product URL","description":null,"primary_key":false,"output_display":40,"criteria_display":40,"search_type":null,"type":"text","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":"link","renderer_config":{"href":"$value","display":"text","text":"$value","icon":"fas fa-link","blank":true},"selected":true,"order_by":true,"archive":false,"detail":false,"detail_display":40,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":6,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
+curl -d '{"id":5,"name":"criteria","label":"criteria","form_label":"Product criteria (based on KWs searchables)","description":null,"primary_key":false,"output_display":50,"criteria_display":50,"search_type":"svom_json_kw","type":"json","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":"json","renderer_config":[],"selected":false,"order_by":true,"archive":false,"detail":false,"detail_display":50,"options":[],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":6,"id_output_category":7,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
+curl -d '{"id":6,"name":"added_at","label":"added_at","form_label":"Added at","description":null,"primary_key":false,"output_display":60,"criteria_display":60,"search_type":"between-date","type":"datetime","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":true,"order_by":true,"archive":false,"detail":false,"detail_display":60,"options":[],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":5,"id_output_category":6,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
+curl -d '{"id":7,"name":"product_version","label":"product_version","form_label":"Version","description":null,"primary_key":false,"output_display":70,"criteria_display":70,"search_type":null,"type":"integer","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":true,"order_by":true,"archive":false,"detail":false,"detail_display":70,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":6,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
+curl -d '{"id":8,"name":"program","label":"program","form_label":"Program","description":null,"primary_key":false,"output_display":80,"criteria_display":80,"search_type":"select","type":"string","operator":"eq","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":false,"order_by":true,"archive":false,"detail":false,"detail_display":80,"options":[{"label":"CoreProgram","value":"CoreProgram","display":10},{"label":"GeneralProgram","value":"GeneralProgram","display":20}],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":6,"id_output_category":7,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
+curl -d '{"id":9,"name":"instrument","label":"instrument","form_label":"Instrument","description":null,"primary_key":false,"output_display":90,"criteria_display":90,"search_type":"select","type":"string","operator":"eq","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":false,"order_by":true,"archive":false,"detail":false,"detail_display":90,"options":[{"label":"ECL","value":"ECL","display":10}],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":6,"id_output_category":7,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
+curl -d '{"id":10,"name":"type","label":"type","form_label":"Type","description":null,"primary_key":false,"output_display":100,"criteria_display":100,"search_type":null,"type":"string","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":false,"order_by":true,"archive":false,"detail":false,"detail_display":100,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":7,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
+curl -d '{"id":11,"name":"upload_filename","label":"upload_filename","form_label":"Filename","description":null,"primary_key":false,"output_display":110,"criteria_display":110,"search_type":null,"type":"string","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":true,"order_by":true,"archive":false,"detail":false,"detail_display":110,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":6,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
+curl -d '{"id":12,"name":"burst_id","label":"burst_id","form_label":"Burst ID","description":null,"primary_key":false,"output_display":120,"criteria_display":120,"search_type":"field","type":"string","operator":"eq","dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":true,"order_by":true,"archive":false,"detail":false,"detail_display":120,"options":[],"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":5,"id_output_category":6,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
+curl -d '{"id":13,"name":"pipeline_version","label":"pipeline_version","form_label":"Pipeline version","description":null,"primary_key":false,"output_display":130,"criteria_display":130,"search_type":null,"type":"float","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":false,"order_by":true,"archive":false,"detail":false,"detail_display":130,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":7,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
+curl -d '{"id":14,"name":"schema_version","label":"schema_version","form_label":"Schema version","description":null,"primary_key":false,"output_display":140,"criteria_display":140,"search_type":null,"type":"float","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":false,"order_by":true,"archive":false,"detail":false,"detail_display":140,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":7,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
+curl -d '{"id":15,"name":"src_id","label":"src_id","form_label":"SRC ID","description":null,"primary_key":false,"output_display":150,"criteria_display":150,"search_type":null,"type":"integer","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":false,"order_by":true,"archive":false,"detail":false,"detail_display":150,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":7,"id_detail_output_category":null}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
 
 # Add webpages
 curl -d '{"label":"Default","icon":null,"display":10}' --header 'Content-Type: application/json' -X POST http://localhost/instance/default/webpage-family
-curl -d '{"label":"Home","icon":"fas fa-home","display":10,"title":"Home","content":"<div class=\"row align-items-center jumbotron\"><div class=\"col-6 col-md-4 order-md-2 mx-auto text-center\"><img class=\"img-fluid mb-3 mb-md-0\" src=\"http://localhost:8080/instance/default/file-explorer/home_component_logo.png\" alt=\"Instance logo\"></div><div class=\"col-md-8 order-md-1 text-justify pr-md-5\"><h2 class=\"mb-3\">Welcome to the ANIS default instance</h2><p class=\"lead\">This service provides several sub-services to interact with the database.<br>Here is a brief presentation of these sub-services:</p><ul class=\"lead\"><li><a href=\"https://drf-gitlab.cea.fr/svom/sdb/api-import/-/wikis/home\">/import/</a> =&gt; This sub-service allows you to import data L0, L1 or SR3/SR4</li><li>/export-rest =&gt; =&gt; This sub-service allows you to request and export data from the database in json format with a specific URL. To build this URL you could <a class=\"btn btn-warning\" href=\"../../search\">Go to search form</a></li></ul></div></div>"}' --header 'Content-Type: application/json' -X POST http://localhost/webpage-family/1/webpage
+curl -d '{"label":"Home","icon":"fas fa-home","display":10,"title":"Home","content":"<div class=\"row align-items-center jumbotron\">\n    <div class=\"col-6 col-md-4 order-md-2 mx-auto text-center\">\n        <img class=\"img-fluid mb-3 mb-md-0\" src=\"http://localhost:8080/instance/default/file-explorer/home_component_logo.png\" alt=\"Instance logo\">\n   </div>\n   <div class=\"col-md-8 order-md-1 text-justify pr-md-5\">\n       <h2 class=\"mb-3\">Welcome to the ANIS default instance</h2>\n       <p class=\"lead\">\n           This service provides several sub-services to interact with the database.<br>\n           Here is a brief presentation of these sub-services:\n       </p>\n       <ul class=\"lead\">\n           <li>\n               <a href=\"https://drf-gitlab.cea.fr/svom/sdb/api-import/-/wikis/home\">\n                   /import/\n               </a> =&gt; This sub-service allows you to import data L0, L1 or SR3/SR4\n           </li>\n           <li>\n               /export-rest =&gt; =&gt; This sub-service allows you to request and export data \n               from the database in json format with a specific URL. To build this URL you could \n               <a class=\"btn btn-warning\" href=\"../../search\">\n                   Go to search form\n               </a>\n           </li>\n       </ul>\n   </div>\n</div>\n<app-dataset-sample\n    [datasetName]=\"'\''observations'\''\"\n    [sortingColumn]=\"1\"\n    [sortingDirection]=\"'\''d'\''\"\n    [nbItems]=\"5\">\n</app-dataset-sample>"}' --header 'Content-Type: application/json' -X POST http://localhost/webpage-family/1/webpage
diff --git a/server/app/dependencies.php b/server/app/dependencies.php
index 102f8df534db10df4d9be0832cc6825ba9449115..598cb47368ce171d294e421bb30fb2e56dac53d3 100644
--- a/server/app/dependencies.php
+++ b/server/app/dependencies.php
@@ -225,6 +225,10 @@ $container->set('App\Action\ConeSearchConfigAction', function (ContainerInterfac
     return new App\Action\ConeSearchConfigAction($c->get('em'));
 });
 
+$container->set('App\Action\DetailConfigAction', function (ContainerInterface $c) {
+    return new App\Action\DetailConfigAction($c->get('em'));
+});
+
 $container->set('App\Action\SearchAction', function (ContainerInterface $c) {
     $anisQueryBuilder = (new App\Search\Query\AnisQueryBuilder())
         ->addQueryPart(new App\Search\Query\From())
diff --git a/server/app/routes.php b/server/app/routes.php
index 6ad263ef5e6d50fe512d28a1bc4a4b2d12eb97db..73887c502d2cb9eadbc2853ede6d9586a8be09c0 100644
--- a/server/app/routes.php
+++ b/server/app/routes.php
@@ -75,6 +75,7 @@ $app->group('', function (RouteCollectorProxy $group) {
     $group->map([OPTIONS, GET, POST], '/dataset/{name}/file', App\Action\FileListAction::class);
     $group->map([OPTIONS, GET, PUT, DELETE], '/file/{id}', App\Action\FileAction::class);
     $group->map([OPTIONS, GET, POST, PUT], '/dataset/{name}/cone-search-config', App\Action\ConeSearchConfigAction::class);
+    $group->map([OPTIONS, GET, POST, PUT], '/dataset/{name}/detail-config', App\Action\DetailConfigAction::class);
 })->add(new App\Middleware\RouteGuardMiddleware(
     boolval($container->get(SETTINGS)['token']['enabled']), 
     array(POST, PUT, DELETE), 
diff --git a/server/doctrine-proxy/__CG__AppEntityAttribute.php b/server/doctrine-proxy/__CG__AppEntityAttribute.php
index 0d1ec8c3f3191d037c0be588092fcc82d35c7c35..e9734fb01579e1ab7e3a2421e77fc5e46fd5ea72 100644
--- a/server/doctrine-proxy/__CG__AppEntityAttribute.php
+++ b/server/doctrine-proxy/__CG__AppEntityAttribute.php
@@ -67,10 +67,10 @@ class Attribute extends \App\Entity\Attribute implements \Doctrine\ORM\Proxy\Pro
     public function __sleep()
     {
         if ($this->__isInitialized__) {
-            return ['__isInitialized__', 'id', 'dataset', 'name', 'label', 'formLabel', 'description', 'primaryKey', 'type', 'searchType', 'operator', 'dynamicOperator', 'min', 'max', 'options', 'placeholderMin', 'placeholderMax', 'criteriaDisplay', 'outputDisplay', 'selected', 'renderer', 'rendererConfig', 'orderBy', 'archive', 'detail', 'displayDetail', 'rendererDetail', 'rendererDetailConfig', 'voUtype', 'voUcd', 'voUnit', 'voDescription', 'voDatatype', 'voSize', 'criteriaFamily', 'outputCategory'];
+            return ['__isInitialized__', 'id', 'dataset', 'name', 'label', 'formLabel', 'description', 'primaryKey', 'type', 'searchType', 'operator', 'dynamicOperator', 'min', 'max', 'options', 'placeholderMin', 'placeholderMax', 'criteriaDisplay', 'outputDisplay', 'selected', 'renderer', 'rendererConfig', 'orderBy', 'archive', 'detailDisplay', 'voUtype', 'voUcd', 'voUnit', 'voDescription', 'voDatatype', 'voSize', 'criteriaFamily', 'outputCategory', 'detailOutputCategory'];
         }
 
-        return ['__isInitialized__', 'id', 'dataset', 'name', 'label', 'formLabel', 'description', 'primaryKey', 'type', 'searchType', 'operator', 'dynamicOperator', 'min', 'max', 'options', 'placeholderMin', 'placeholderMax', 'criteriaDisplay', 'outputDisplay', 'selected', 'renderer', 'rendererConfig', 'orderBy', 'archive', 'detail', 'displayDetail', 'rendererDetail', 'rendererDetailConfig', 'voUtype', 'voUcd', 'voUnit', 'voDescription', 'voDatatype', 'voSize', 'criteriaFamily', 'outputCategory'];
+        return ['__isInitialized__', 'id', 'dataset', 'name', 'label', 'formLabel', 'description', 'primaryKey', 'type', 'searchType', 'operator', 'dynamicOperator', 'min', 'max', 'options', 'placeholderMin', 'placeholderMax', 'criteriaDisplay', 'outputDisplay', 'selected', 'renderer', 'rendererConfig', 'orderBy', 'archive', 'detailDisplay', 'voUtype', 'voUcd', 'voUnit', 'voDescription', 'voDatatype', 'voSize', 'criteriaFamily', 'outputCategory', 'detailOutputCategory'];
     }
 
     /**
@@ -679,23 +679,23 @@ class Attribute extends \App\Entity\Attribute implements \Doctrine\ORM\Proxy\Pro
     /**
      * {@inheritDoc}
      */
-    public function getDisplayDetail()
+    public function getDetailDisplay()
     {
 
-        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getDisplayDetail', []);
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getDetailDisplay', []);
 
-        return parent::getDisplayDetail();
+        return parent::getDetailDisplay();
     }
 
     /**
      * {@inheritDoc}
      */
-    public function setDisplayDetail($displayDetail)
+    public function setDetailDisplay($detailDisplay)
     {
 
-        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setDisplayDetail', [$displayDetail]);
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setDetailDisplay', [$detailDisplay]);
 
-        return parent::setDisplayDetail($displayDetail);
+        return parent::setDetailDisplay($detailDisplay);
     }
 
     /**
@@ -833,111 +833,67 @@ class Attribute extends \App\Entity\Attribute implements \Doctrine\ORM\Proxy\Pro
     /**
      * {@inheritDoc}
      */
-    public function getDetail()
-    {
-
-        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getDetail', []);
-
-        return parent::getDetail();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public function setDetail($detail)
-    {
-
-        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setDetail', [$detail]);
-
-        return parent::setDetail($detail);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public function getRendererDetail()
-    {
-
-        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getRendererDetail', []);
-
-        return parent::getRendererDetail();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public function setRendererDetail($rendererDetail)
-    {
-
-        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setRendererDetail', [$rendererDetail]);
-
-        return parent::setRendererDetail($rendererDetail);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public function getRendererDetailConfig()
+    public function getCriteriaFamily()
     {
 
-        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getRendererDetailConfig', []);
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getCriteriaFamily', []);
 
-        return parent::getRendererDetailConfig();
+        return parent::getCriteriaFamily();
     }
 
     /**
      * {@inheritDoc}
      */
-    public function setRendererDetailConfig($rendererDetailConfig)
+    public function setCriteriaFamily($criteriaFamily)
     {
 
-        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setRendererDetailConfig', [$rendererDetailConfig]);
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setCriteriaFamily', [$criteriaFamily]);
 
-        return parent::setRendererDetailConfig($rendererDetailConfig);
+        return parent::setCriteriaFamily($criteriaFamily);
     }
 
     /**
      * {@inheritDoc}
      */
-    public function getCriteriaFamily()
+    public function getOutputCategory()
     {
 
-        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getCriteriaFamily', []);
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getOutputCategory', []);
 
-        return parent::getCriteriaFamily();
+        return parent::getOutputCategory();
     }
 
     /**
      * {@inheritDoc}
      */
-    public function setCriteriaFamily($criteriaFamily)
+    public function setOutputCategory($outputCategory)
     {
 
-        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setCriteriaFamily', [$criteriaFamily]);
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setOutputCategory', [$outputCategory]);
 
-        return parent::setCriteriaFamily($criteriaFamily);
+        return parent::setOutputCategory($outputCategory);
     }
 
     /**
      * {@inheritDoc}
      */
-    public function getOutputCategory()
+    public function getDetailOutputCategory()
     {
 
-        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getOutputCategory', []);
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getDetailOutputCategory', []);
 
-        return parent::getOutputCategory();
+        return parent::getDetailOutputCategory();
     }
 
     /**
      * {@inheritDoc}
      */
-    public function setOutputCategory($outputCategory)
+    public function setDetailOutputCategory($detailOutputCategory)
     {
 
-        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setOutputCategory', [$outputCategory]);
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setDetailOutputCategory', [$detailOutputCategory]);
 
-        return parent::setOutputCategory($outputCategory);
+        return parent::setDetailOutputCategory($detailOutputCategory);
     }
 
     /**
diff --git a/server/doctrine-proxy/__CG__AppEntityDataset.php b/server/doctrine-proxy/__CG__AppEntityDataset.php
index 2d0da202673c348e3d21db33c676cf70dcea3e08..cd70caa3d43ccacd978ec065e1107a21206eddf1 100644
--- a/server/doctrine-proxy/__CG__AppEntityDataset.php
+++ b/server/doctrine-proxy/__CG__AppEntityDataset.php
@@ -67,10 +67,10 @@ class Dataset extends \App\Entity\Dataset implements \Doctrine\ORM\Proxy\Proxy
     public function __sleep()
     {
         if ($this->__isInitialized__) {
-            return ['__isInitialized__', 'name', 'tableRef', 'label', 'description', 'display', 'dataPath', 'public', 'downloadJson', 'downloadCsv', 'downloadAscii', 'downloadVo', 'serverLinkEnabled', 'datatableEnabled', 'datatableSelectableRows', '' . "\0" . 'App\\Entity\\Dataset' . "\0" . 'coneSearchConfig', 'database', 'datasetFamily', 'attributes'];
+            return ['__isInitialized__', 'name', 'tableRef', 'label', 'description', 'display', 'dataPath', 'public', 'downloadJson', 'downloadCsv', 'downloadAscii', 'downloadVo', 'serverLinkEnabled', 'datatableEnabled', 'datatableSelectableRows', '' . "\0" . 'App\\Entity\\Dataset' . "\0" . 'coneSearchConfig', '' . "\0" . 'App\\Entity\\Dataset' . "\0" . 'detailConfig', 'database', 'datasetFamily', 'attributes'];
         }
 
-        return ['__isInitialized__', 'name', 'tableRef', 'label', 'description', 'display', 'dataPath', 'public', 'downloadJson', 'downloadCsv', 'downloadAscii', 'downloadVo', 'serverLinkEnabled', 'datatableEnabled', 'datatableSelectableRows', '' . "\0" . 'App\\Entity\\Dataset' . "\0" . 'coneSearchConfig', 'database', 'datasetFamily', 'attributes'];
+        return ['__isInitialized__', 'name', 'tableRef', 'label', 'description', 'display', 'dataPath', 'public', 'downloadJson', 'downloadCsv', 'downloadAscii', 'downloadVo', 'serverLinkEnabled', 'datatableEnabled', 'datatableSelectableRows', '' . "\0" . 'App\\Entity\\Dataset' . "\0" . 'coneSearchConfig', '' . "\0" . 'App\\Entity\\Dataset' . "\0" . 'detailConfig', 'database', 'datasetFamily', 'attributes'];
     }
 
     /**
@@ -500,6 +500,28 @@ class Dataset extends \App\Entity\Dataset implements \Doctrine\ORM\Proxy\Proxy
         return parent::setConeSearchConfig($coneSearchConfig);
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    public function getDetailConfig()
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getDetailConfig', []);
+
+        return parent::getDetailConfig();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function setDetailConfig($detailConfig)
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setDetailConfig', [$detailConfig]);
+
+        return parent::setDetailConfig($detailConfig);
+    }
+
     /**
      * {@inheritDoc}
      */
diff --git a/server/doctrine-proxy/__CG__AppEntityDetailConfig.php b/server/doctrine-proxy/__CG__AppEntityDetailConfig.php
new file mode 100644
index 0000000000000000000000000000000000000000..45fb9beb72f23dc4407d16a627f62218b360833b
--- /dev/null
+++ b/server/doctrine-proxy/__CG__AppEntityDetailConfig.php
@@ -0,0 +1,250 @@
+<?php
+
+namespace DoctrineProxies\__CG__\App\Entity;
+
+
+/**
+ * DO NOT EDIT THIS FILE - IT WAS CREATED BY DOCTRINE'S PROXY GENERATOR
+ */
+class DetailConfig extends \App\Entity\DetailConfig implements \Doctrine\ORM\Proxy\Proxy
+{
+    /**
+     * @var \Closure the callback responsible for loading properties in the proxy object. This callback is called with
+     *      three parameters, being respectively the proxy object to be initialized, the method that triggered the
+     *      initialization process and an array of ordered parameters that were passed to that method.
+     *
+     * @see \Doctrine\Common\Proxy\Proxy::__setInitializer
+     */
+    public $__initializer__;
+
+    /**
+     * @var \Closure the callback responsible of loading properties that need to be copied in the cloned object
+     *
+     * @see \Doctrine\Common\Proxy\Proxy::__setCloner
+     */
+    public $__cloner__;
+
+    /**
+     * @var boolean flag indicating if this object was already initialized
+     *
+     * @see \Doctrine\Persistence\Proxy::__isInitialized
+     */
+    public $__isInitialized__ = false;
+
+    /**
+     * @var array<string, null> properties to be lazy loaded, indexed by property name
+     */
+    public static $lazyPropertiesNames = array (
+);
+
+    /**
+     * @var array<string, mixed> default values of properties to be lazy loaded, with keys being the property names
+     *
+     * @see \Doctrine\Common\Proxy\Proxy::__getLazyProperties
+     */
+    public static $lazyPropertiesDefaults = array (
+);
+
+
+
+    public function __construct(?\Closure $initializer = null, ?\Closure $cloner = null)
+    {
+
+        $this->__initializer__ = $initializer;
+        $this->__cloner__      = $cloner;
+    }
+
+
+
+
+
+
+
+    /**
+     * 
+     * @return array
+     */
+    public function __sleep()
+    {
+        if ($this->__isInitialized__) {
+            return ['__isInitialized__', 'id', 'enabled', 'content'];
+        }
+
+        return ['__isInitialized__', 'id', 'enabled', 'content'];
+    }
+
+    /**
+     * 
+     */
+    public function __wakeup()
+    {
+        if ( ! $this->__isInitialized__) {
+            $this->__initializer__ = function (DetailConfig $proxy) {
+                $proxy->__setInitializer(null);
+                $proxy->__setCloner(null);
+
+                $existingProperties = get_object_vars($proxy);
+
+                foreach ($proxy::$lazyPropertiesDefaults as $property => $defaultValue) {
+                    if ( ! array_key_exists($property, $existingProperties)) {
+                        $proxy->$property = $defaultValue;
+                    }
+                }
+            };
+
+        }
+    }
+
+    /**
+     * 
+     */
+    public function __clone()
+    {
+        $this->__cloner__ && $this->__cloner__->__invoke($this, '__clone', []);
+    }
+
+    /**
+     * Forces initialization of the proxy
+     */
+    public function __load()
+    {
+        $this->__initializer__ && $this->__initializer__->__invoke($this, '__load', []);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @internal generated method: use only when explicitly handling proxy specific loading logic
+     */
+    public function __isInitialized()
+    {
+        return $this->__isInitialized__;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @internal generated method: use only when explicitly handling proxy specific loading logic
+     */
+    public function __setInitialized($initialized)
+    {
+        $this->__isInitialized__ = $initialized;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @internal generated method: use only when explicitly handling proxy specific loading logic
+     */
+    public function __setInitializer(\Closure $initializer = null)
+    {
+        $this->__initializer__ = $initializer;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @internal generated method: use only when explicitly handling proxy specific loading logic
+     */
+    public function __getInitializer()
+    {
+        return $this->__initializer__;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @internal generated method: use only when explicitly handling proxy specific loading logic
+     */
+    public function __setCloner(\Closure $cloner = null)
+    {
+        $this->__cloner__ = $cloner;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @internal generated method: use only when explicitly handling proxy specific cloning logic
+     */
+    public function __getCloner()
+    {
+        return $this->__cloner__;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @internal generated method: use only when explicitly handling proxy specific loading logic
+     * @deprecated no longer in use - generated code now relies on internal components rather than generated public API
+     * @static
+     */
+    public function __getLazyProperties()
+    {
+        return self::$lazyPropertiesDefaults;
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    public function getId()
+    {
+        if ($this->__isInitialized__ === false) {
+            return (int)  parent::getId();
+        }
+
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getId', []);
+
+        return parent::getId();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getEnabled()
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getEnabled', []);
+
+        return parent::getEnabled();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function setEnabled($enabled)
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setEnabled', [$enabled]);
+
+        return parent::setEnabled($enabled);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getContent()
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getContent', []);
+
+        return parent::getContent();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function setContent($content)
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setContent', [$content]);
+
+        return parent::setContent($content);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function jsonSerialize(): array
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'jsonSerialize', []);
+
+        return parent::jsonSerialize();
+    }
+
+}
diff --git a/server/src/Action/AttributeAction.php b/server/src/Action/AttributeAction.php
index 75b4cd761646ba7a83a483988647a28d87a76477..4ceb4096c1a7eaf5ba2c3f9d653a3805b0ae9e9e 100644
--- a/server/src/Action/AttributeAction.php
+++ b/server/src/Action/AttributeAction.php
@@ -85,10 +85,7 @@ final class AttributeAction extends AbstractAction
                 'renderer_config',
                 'order_by',
                 'archive',
-                'detail',
-                'display_detail',
-                'renderer_detail',
-                'renderer_detail_config',
+                'detail_display',
                 'vo_utype',
                 'vo_ucd',
                 'vo_unit',
@@ -96,7 +93,8 @@ final class AttributeAction extends AbstractAction
                 'vo_datatype',
                 'vo_size',
                 'id_criteria_family',
-                'id_output_category'
+                'id_output_category',
+                'id_detail_output_category'
             );
             foreach ($fields as $a) {
                 if (!array_key_exists($a, $parsedBody)) {
@@ -152,10 +150,7 @@ final class AttributeAction extends AbstractAction
         $attribute->setRendererConfig($parsedBody['renderer_config']);
         $attribute->setOrderBy($parsedBody['order_by']);
         $attribute->setArchive($parsedBody['archive']);
-        $attribute->setDetail($parsedBody['detail']);
-        $attribute->setDisplayDetail($parsedBody['display_detail']);
-        $attribute->setRendererDetail($parsedBody['renderer_detail']);
-        $attribute->setRendererDetailConfig($parsedBody['renderer_detail_config']);
+        $attribute->setDetailDisplay($parsedBody['detail_display']);
         $attribute->setVoUtype($parsedBody['vo_utype']);
         $attribute->setVoUcd($parsedBody['vo_ucd']);
         $attribute->setVoUnit($parsedBody['vo_unit']);
@@ -180,6 +175,15 @@ final class AttributeAction extends AbstractAction
             );
         }
         $attribute->setOutputCategory($outputCategory);
+        if (is_null($parsedBody['id_detail_output_category'])) {
+            $detailOutputCategory = null;
+        } else {
+            $detailOutputCategory = $this->em->find(
+                'App\Entity\OutputCategory',
+                $parsedBody['id_detail_output_category']
+            );
+        }
+        $attribute->setDetailOutputCategory($detailOutputCategory);
         $this->em->flush();
     }
 }
diff --git a/server/src/Action/AttributeListAction.php b/server/src/Action/AttributeListAction.php
index b98cae3d873a8401d2341ff816b0c93641b92cd4..e1496b10d00f994be42136fd2cdaaa719a39c80a 100644
--- a/server/src/Action/AttributeListAction.php
+++ b/server/src/Action/AttributeListAction.php
@@ -87,10 +87,7 @@ final class AttributeListAction extends AbstractAction
                 'renderer_config',
                 'order_by',
                 'archive',
-                'detail',
-                'display_detail',
-                'renderer_detail',
-                'renderer_detail_config',
+                'detail_display',
                 'vo_utype',
                 'vo_ucd',
                 'vo_unit',
@@ -98,7 +95,8 @@ final class AttributeListAction extends AbstractAction
                 'vo_datatype',
                 'vo_size',
                 'id_criteria_family',
-                'id_output_category'
+                'id_output_category',
+                'id_detail_output_category'
             );
             foreach ($fields as $a) {
                 if (!array_key_exists($a, $parsedBody)) {
@@ -150,10 +148,7 @@ final class AttributeListAction extends AbstractAction
         $attribute->setRendererConfig($parsedBody['renderer_config']);
         $attribute->setOrderBy($parsedBody['order_by']);
         $attribute->setArchive($parsedBody['archive']);
-        $attribute->setDetail($parsedBody['detail']);
-        $attribute->setDisplayDetail($parsedBody['display_detail']);
-        $attribute->setRendererDetail($parsedBody['renderer_detail']);
-        $attribute->setRendererDetailConfig($parsedBody['renderer_detail_config']);
+        $attribute->setDetailDisplay($parsedBody['detail_display']);
         $attribute->setVoUtype($parsedBody['vo_utype']);
         $attribute->setVoUcd($parsedBody['vo_ucd']);
         $attribute->setVoUnit($parsedBody['vo_unit']);
@@ -178,6 +173,15 @@ final class AttributeListAction extends AbstractAction
             );
         }
         $attribute->setOutputCategory($outputCategory);
+        if (is_null($parsedBody['id_detail_output_category'])) {
+            $detailOutputCategory = null;
+        } else {
+            $detailOutputCategory = $this->em->find(
+                'App\Entity\OutputCategory',
+                $parsedBody['id_detail_output_category']
+            );
+        }
+        $attribute->setDetailOutputCategory($detailOutputCategory);
 
         $this->em->persist($attribute);
         $this->em->flush();
diff --git a/server/src/Action/DetailConfigAction.php b/server/src/Action/DetailConfigAction.php
new file mode 100644
index 0000000000000000000000000000000000000000..38877b680c223c67f67ce097096cb457c3a2027f
--- /dev/null
+++ b/server/src/Action/DetailConfigAction.php
@@ -0,0 +1,154 @@
+<?php
+
+/*
+ * This file is part of Anis Server.
+ *
+ * (c) 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.
+ */
+declare(strict_types=1);
+
+namespace App\Action;
+
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use Slim\Exception\HttpBadRequestException;
+use Slim\Exception\HttpNotFoundException;
+use App\Entity\DetailConfig;
+use App\Entity\Dataset;
+
+/**
+ * @author François Agneray <francois.agneray@lam.fr>
+ * @package App\Action
+ */
+final class DetailConfigAction extends AbstractAction
+{
+    /**
+     * `GET` Returns the detail configuration found
+     * `PUT` Full update the detail configuration and returns the new version
+     * `DELETE` Delete the detail configuration and return a confirmation message
+     *
+     * @param  ServerRequestInterface $request  PSR-7 This object represents the HTTP request
+     * @param  ResponseInterface      $response PSR-7 This object represents the HTTP response
+     * @param  string[]               $args     This table contains information transmitted in the URL (see routes.php)
+     *
+     * @return ResponseInterface
+     */
+    public function __invoke(
+        ServerRequestInterface $request,
+        ResponseInterface $response,
+        array $args
+    ): ResponseInterface {
+        if ($request->getMethod() === OPTIONS) {
+            return $response->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, OPTIONS');
+        }
+
+        $dataset = $this->em->find('App\Entity\Dataset', $args['name']);
+
+        // Returns HTTP 404 if the dataset is not found
+        if (is_null($dataset)) {
+            throw new HttpNotFoundException(
+                $request,
+                'Dataset with name ' . $args['name'] . ' is not found'
+            );
+        }
+
+        // Search the correct detail configuration with primary key
+        if ($dataset->getDetailConfig()) {
+            $detailConfig = $this->em->find(
+                'App\Entity\DetailConfig',
+                $dataset->getDetailConfig()->getId()
+            );
+        } else {
+            $detailConfig = null;
+        }
+
+        if ($request->getMethod() === GET) {
+            $payload = json_encode($detailConfig);
+        }
+
+        if ($request->getMethod() === POST) {
+            $parsedBody = $request->getParsedBody();
+            $this->checkParsedBody($parsedBody, $request);
+            $detailConfig = $this->postDetailConfig($parsedBody, $dataset);
+            $payload = json_encode($detailConfig);
+            $response = $response->withStatus(201);
+        }
+
+        if ($request->getMethod() === PUT) {
+            // Returns HTTP 404 if the detail configuation is not found
+            if (is_null($detailConfig)) {
+                throw new HttpNotFoundException(
+                    $request,
+                    'Detail config is not found'
+                );
+            }
+
+            $parsedBody = $request->getParsedBody();
+            $this->checkParsedBody($parsedBody, $request);
+
+            $this->editDetailConfig($detailConfig, $parsedBody);
+            $payload = json_encode($detailConfig);
+        }
+
+        $response->getBody()->write($payload);
+        return $response;
+    }
+
+    /**
+     * @param array                  $parsedBody  Contains the values ​​of the new detail configuration
+     * @param ServerRequestInterface $request     PSR-7 This object represents the HTTP request
+     */
+    private function checkParsedBody($parsedBody, $request)
+    {
+        $fields = array(
+            'enabled',
+            'content'
+        );
+
+        // To work this actions needs information
+        foreach ($fields as $a) {
+            if (!array_key_exists($a, $parsedBody)) {
+                throw new HttpBadRequestException(
+                    $request,
+                    'Param ' . $a . ' needed to add or edit detail configuration'
+                );
+            }
+        }
+    }
+
+    /**
+     * @param array   $parsedBody Contains the values ​​of the new detail configuration sent by the user
+     * @param Dataset $dataset    Dataset for adding the detail configuration
+     *
+     * @return DetailConfig
+     */
+    private function postDetailConfig(array $parsedBody, Dataset $dataset): DetailConfig
+    {
+        $detailConfig = new DetailConfig();
+        $detailConfig->setEnabled($parsedBody['enabled']);
+        $detailConfig->setContent($parsedBody['content']);
+
+        $dataset->setDetailConfig($detailConfig);
+
+        $this->em->persist($dataset);
+        $this->em->flush();
+
+        return $detailConfig;
+    }
+
+    /**
+     * Update detail configuration object with setters
+     *
+     * @param DetailConfig $detailConfig The detail configuration to update
+     * @param string[]     $parsedBody   Contains the new values ​​of the detail sent by the user
+     */
+    private function editDetailConfig(detailConfig $detailConfig, array $parsedBody): void
+    {
+        $detailConfig->setEnabled($parsedBody['enabled']);
+        $detailConfig->setContent($parsedBody['content']);
+        $this->em->flush();
+    }
+}
diff --git a/server/src/Entity/Attribute.php b/server/src/Entity/Attribute.php
index ffe7b3ba5ea6912bdc535427bd10c9ef6594693a..82f8aeb5d389af1631b9c39ae6d0b92cd429e087 100644
--- a/server/src/Entity/Attribute.php
+++ b/server/src/Entity/Attribute.php
@@ -185,33 +185,12 @@ class Attribute implements \JsonSerializable
      */
     protected $archive;
 
-    /**
-     * @var bool
-     *
-     * @Column(type="boolean", name="detail", nullable=false, options={"default":false})
-     */
-    protected $detail;
-
     /**
      * @var int
      *
-     * @Column(type="integer", name="display_detail", nullable=true)
-     */
-    protected $displayDetail;
-
-    /**
-     * @var string
-     *
-     * @Column(type="string", name="renderer_detail", nullable=true)
+     * @Column(type="integer", name="detail_display", nullable=true)
      */
-    protected $rendererDetail;
-
-    /**
-     * @var string
-     *
-     * @Column(type="json", name="renderer_detail_config", nullable=true)
-     */
-    protected $rendererDetailConfig;
+    protected $detailDisplay;
 
     /**
      * @var string
@@ -271,6 +250,14 @@ class Attribute implements \JsonSerializable
      */
     protected $outputCategory;
 
+    /**
+     * @var OutputCategory
+     *
+     * @ManyToOne(targetEntity="OutputCategory")
+     * @JoinColumn(name="detail_output_category", referencedColumnName="id", nullable=true, onDelete="SET NULL")
+     */
+    protected $detailOutputCategory;
+
     public function __construct(int $id, Dataset $dataset)
     {
         $this->id = $id;
@@ -502,14 +489,14 @@ class Attribute implements \JsonSerializable
         $this->archive = $archive;
     }
 
-    public function getDisplayDetail()
+    public function getDetailDisplay()
     {
-        return $this->displayDetail;
+        return $this->detailDisplay;
     }
 
-    public function setDisplayDetail($displayDetail)
+    public function setDetailDisplay($detailDisplay)
     {
-        $this->displayDetail = $displayDetail;
+        $this->detailDisplay = $detailDisplay;
     }
 
     public function getVoUtype()
@@ -572,36 +559,6 @@ class Attribute implements \JsonSerializable
         $this->voSize = $voSize;
     }
 
-    public function getDetail()
-    {
-        return $this->detail;
-    }
-
-    public function setDetail($detail)
-    {
-        $this->detail = $detail;
-    }
-
-    public function getRendererDetail()
-    {
-        return $this->rendererDetail;
-    }
-
-    public function setRendererDetail($rendererDetail)
-    {
-        $this->rendererDetail = $rendererDetail;
-    }
-
-    public function getRendererDetailConfig()
-    {
-        return $this->rendererDetailConfig;
-    }
-
-    public function setRendererDetailConfig($rendererDetailConfig)
-    {
-        $this->rendererDetailConfig = $rendererDetailConfig;
-    }
-
     public function getCriteriaFamily()
     {
         return $this->criteriaFamily;
@@ -622,6 +579,16 @@ class Attribute implements \JsonSerializable
         $this->outputCategory = $outputCategory;
     }
 
+    public function getDetailOutputCategory()
+    {
+        return $this->detailOutputCategory;
+    }
+
+    public function setDetailOutputCategory($detailOutputCategory)
+    {
+        $this->detailOutputCategory = $detailOutputCategory;
+    }
+
     public function jsonSerialize(): array
     {
         return [
@@ -647,10 +614,7 @@ class Attribute implements \JsonSerializable
             'renderer_config' => $this->getRendererConfig(),
             'order_by' => $this->getOrderBy(),
             'archive' => $this->getArchive(),
-            'detail' => $this->getDetail(),
-            'display_detail' => $this->getDisplayDetail(),
-            'renderer_detail' => $this->getRendererDetail(),
-            'renderer_detail_config' => $this->getRendererDetailConfig(),
+            'detail_display' => $this->getDetailDisplay(),
             'vo_utype' => $this->getVoUtype(),
             'vo_ucd' => $this->getVoUcd(),
             'vo_unit' => $this->getVoUnit(),
@@ -658,7 +622,8 @@ class Attribute implements \JsonSerializable
             'vo_datatype' => $this->getVoDatatype(),
             'vo_size' => $this->getVoSize(),
             'id_criteria_family' => is_null($this->getCriteriaFamily()) ? null : $this->getCriteriaFamily()->getId(),
-            'id_output_category' => is_null($this->getOutputCategory()) ? null : $this->getOutputCategory()->getId()
+            'id_output_category' => is_null($this->getOutputCategory()) ? null : $this->getOutputCategory()->getId(),
+            'id_detail_output_category' => is_null($this->getDetailOutputCategory()) ? null : $this->getDetailOutputCategory()->getId()
         ];
     }
 }
diff --git a/server/src/Entity/Dataset.php b/server/src/Entity/Dataset.php
index 5fb2e20b3891e9c501c01a7e994301ebf858464b..9edf396e1ea072970550094dffbb6e78f2c68d4f 100644
--- a/server/src/Entity/Dataset.php
+++ b/server/src/Entity/Dataset.php
@@ -130,6 +130,14 @@ class Dataset implements \JsonSerializable
      */
     private $coneSearchConfig;
 
+    /**
+     * @var DetailConfig
+     *
+     * @OneToOne(targetEntity="DetailConfig", cascade={"persist", "remove"})
+     * @JoinColumn(name="detail_config_id", referencedColumnName="id", nullable=true)
+     */
+    private $detailConfig;
+
     /**
      * @var Database
      *
@@ -304,6 +312,16 @@ class Dataset implements \JsonSerializable
         $this->coneSearchConfig = $coneSearchConfig;
     }
 
+    public function getDetailConfig()
+    {
+        return $this->detailConfig;
+    }
+
+    public function setDetailConfig($detailConfig)
+    {
+        $this->detailConfig = $detailConfig;
+    }
+
     public function setDatabase($database)
     {
         $this->database = $database;
@@ -353,6 +371,8 @@ class Dataset implements \JsonSerializable
             'datatable_selectable_rows' => $this->getDatatableSelectableRows(),
             'cone_search_config_id' => is_null($this->getConeSearchConfig())
                 ? null : $this->getConeSearchConfig()->getId(),
+            'detail_config_id' => is_null($this->getDetailConfig())
+                ? null : $this->getDetailConfig()->getId(),
             'id_database' => $this->getDatabase()->getId(),
             'id_dataset_family' => $this->getDatasetFamily()->getId(),
             'full_data_path' => $this->getFullDataPath()
diff --git a/server/src/Entity/DetailConfig.php b/server/src/Entity/DetailConfig.php
new file mode 100644
index 0000000000000000000000000000000000000000..768e3ee18eacf7223cf5dd07373bed8c8e8300ef
--- /dev/null
+++ b/server/src/Entity/DetailConfig.php
@@ -0,0 +1,80 @@
+<?php
+
+/*
+ * This file is part of Anis Server.
+ *
+ * (c) 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.
+ */
+declare(strict_types=1);
+
+namespace App\Entity;
+
+/**
+ * @author François Agneray <francois.agneray@lam.fr>
+ * @package App\Entity
+ *
+ * @Entity
+ * @Table(name="detail")
+ */
+class DetailConfig implements \JsonSerializable
+{
+    /**
+     * @var int
+     *
+     * @Id
+     * @Column(type="integer", nullable=false)
+     * @GeneratedValue
+     */
+    protected $id;
+
+    /**
+     * @var bool
+     *
+     * @Column(type="boolean", name="enabled", nullable=false)
+     */
+    protected $enabled;
+
+    /**
+     * @var string
+     *
+     * @Column(type="text", name="content", nullable=false)
+     */
+    protected $content;
+
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    public function getEnabled()
+    {
+        return $this->enabled;
+    }
+
+    public function setEnabled($enabled)
+    {
+        $this->enabled = $enabled;
+    }
+
+    public function getContent()
+    {
+        return $this->content;
+    }
+
+    public function setContent($content)
+    {
+        $this->content = $content;
+    }
+
+    public function jsonSerialize(): array
+    {
+        return [
+            'id' => $this->getId(),
+            'enabled' => $this->getEnabled(),
+            'content' => $this->getContent()
+        ];
+    }
+}
diff --git a/server/tests/Action/AttributeActionTest.php b/server/tests/Action/AttributeActionTest.php
index a9de89c31cf8c8e8b32dd69a95e9244f09fa8d1e..0617eb9516e2ebe7017146203d99d8a29b93a1be 100644
--- a/server/tests/Action/AttributeActionTest.php
+++ b/server/tests/Action/AttributeActionTest.php
@@ -152,9 +152,7 @@ final class AttributeActionTest extends TestCase
             'order_by' => false,
             'archive' => false,
             'detail' => false,
-            'display_detail' => 10,
-            'renderer_detail' => null,
-            'renderer_detail_config' => null,
+            'detail_display' => 10,
             'vo_utype' => null,
             'vo_ucd' => null,
             'vo_unit' => null,
@@ -162,7 +160,8 @@ final class AttributeActionTest extends TestCase
             'vo_datatype' => null,
             'vo_size' => 0,
             'id_criteria_family' => 1,
-            'id_output_category' => 1
+            'id_output_category' => 1,
+            'id_detail_output_category' => null
         );
     }
 
diff --git a/server/tests/Action/AttributeListActionTest.php b/server/tests/Action/AttributeListActionTest.php
index 1dcce595519470ea4db572440e833d4430da674c..85e3368ace16c9107f8272c884c4b2e73ff43a63 100644
--- a/server/tests/Action/AttributeListActionTest.php
+++ b/server/tests/Action/AttributeListActionTest.php
@@ -104,9 +104,7 @@ final class AttributeListActionTest extends TestCase
             'order_by' => false,
             'archive' => false,
             'detail' => false,
-            'display_detail' => 10,
-            'renderer_detail' => null,
-            'renderer_detail_config' => null,
+            'detail_display' => 10,
             'vo_utype' => null,
             'vo_ucd' => null,
             'vo_unit' => null,
@@ -114,7 +112,8 @@ final class AttributeListActionTest extends TestCase
             'vo_datatype' => null,
             'vo_size' => 0,
             'id_criteria_family' => null,
-            'id_output_category' => null
+            'id_output_category' => null,
+            'id_detail_output_category' => null
         );
         $this->entityManager->expects($this->once())->method('persist');