diff --git a/ivette/src/ivette/display.tsx b/ivette/src/ivette/display.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..e5b3cb3744e0e634ac33646c8436309f40145000
--- /dev/null
+++ b/ivette/src/ivette/display.tsx
@@ -0,0 +1,85 @@
+/* ************************************************************************ */
+/*                                                                          */
+/*   This file is part of Frama-C.                                          */
+/*                                                                          */
+/*   Copyright (C) 2007-2023                                                */
+/*     CEA (Commissariat à l'énergie atomique et aux énergies               */
+/*          alternatives)                                                   */
+/*                                                                          */
+/*   you can redistribute it and/or modify it under the terms of the GNU    */
+/*   Lesser General Public License as published by the Free Software        */
+/*   Foundation, version 2.1.                                               */
+/*                                                                          */
+/*   It is distributed in the hope that it will be useful,                  */
+/*   but WITHOUT ANY WARRANTY; without even the implied warranty of         */
+/*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
+/*   GNU Lesser General Public License for more details.                    */
+/*                                                                          */
+/*   See the GNU Lesser General Public License version 2.1                  */
+/*   for more details (enclosed in the file licenses/LGPLv2.1).             */
+/*                                                                          */
+/* ************************************************************************ */
+
+/* --------------------------------------------------------------------------*/
+/* --- Display Interaction                                                ---*/
+/* --------------------------------------------------------------------------*/
+
+/**
+   @packageDocumentation
+   @module ivette/display
+ */
+
+import React from 'react';
+import { VIEW, COMPONENT } from 'ivette';
+import * as State from './state';
+import * as Laboratory from './laboratory';
+
+export interface ItemProps {
+  id: string;
+  selected?: boolean;
+}
+
+/**
+   A sidebar item for controlling an Ivette view.
+ */
+export function ViemItem(props: ItemProps): JSX.Element | null {
+  const { id, selected=false } = props;
+  const view = State.useElement(VIEW,id);
+  const state = Laboratory.useState();
+  if (!view) return null;
+  const status = Laboratory.getViewStatus(state, id);
+  return (
+    <Laboratory.ViewItem
+      view={view}
+      selected={selected}
+      {...status}
+    />
+  );
+}
+
+/**
+   A sidebar item for controlling an Ivette component.
+ */
+export function ComponentItem(props: ItemProps): JSX.Element | null {
+  const { id, selected=false } = props;
+  const comp = State.useElement(COMPONENT,id);
+  const state = Laboratory.useState();
+  if (!comp) return null;
+  const status = Laboratory.getComponentStatus(state, id);
+  return (
+    <Laboratory.ComponentItem
+      comp={comp}
+      selected={selected}
+      {...status}
+    />
+  );
+}
+
+/**
+   A bundle of sidebar items for controlling an Ivette group of components.
+ */
+export function GroupSection(props: ItemProps): JSX.Element | null {
+  return <>{props.id}</>;
+}
+
+/* --------------------------------------------------------------------------*/
diff --git a/ivette/src/renderer/Laboratory.tsx b/ivette/src/ivette/laboratory.tsx
similarity index 96%
rename from ivette/src/renderer/Laboratory.tsx
rename to ivette/src/ivette/laboratory.tsx
index b14654ec66cce24318cdb1dfc0ca563f7492ff58..9515e8c2561b5d83e774d5927728d95e2a430abf 100644
--- a/ivette/src/renderer/Laboratory.tsx
+++ b/ivette/src/ivette/laboratory.tsx
@@ -620,6 +620,52 @@ Settings.onWindowSettings(() => {
   }
 });
 
+/* -------------------------------------------------------------------------- */
+/* --- Exported API                                                       --- */
+/* -------------------------------------------------------------------------- */
+
+export function useState(): LabViewState
+{
+  const [state] = States.useGlobalState(LAB);
+  return state;
+}
+
+export interface ViewStatus {
+  favorite: boolean;
+  displayed: boolean;
+  layout: Layout;
+}
+
+export function getViewStatus(
+  state: LabViewState,
+  viewId: viewId
+): ViewStatus
+{
+  const tab = state.tabs.get(viewId);
+  const favorite = tab ? tab.custom === 0 : false;
+  const displayed = tab ? tab.key === state.tabKey : false;
+  const layout = displayed ? state.stack[0] : tab?.stack[0];
+  return { favorite, displayed, layout: layout ?? defaultLayout };
+}
+
+export interface ComponentStatus {
+  active: boolean;
+  docked: boolean;
+  position: Ivette.LayoutPosition | undefined;
+}
+
+export function getComponentStatus(
+  state: LabViewState,
+  compId: compId
+): ComponentStatus
+{
+  const layout = state.stack[0] ?? defaultLayout;
+  const position= getLayoutPosition(layout, compId);
+  const active = state.panels.has(compId);
+  const docked = state.docked.has(compId);
+  return { position, active, docked };
+}
+
 /* -------------------------------------------------------------------------- */
 /* --- Layout Menu State                                                  --- */
 /* -------------------------------------------------------------------------- */
@@ -873,7 +919,7 @@ interface ViewItemProps {
   layout: Layout | undefined;
 }
 
-function ViewItem(props: ViewItemProps): JSX.Element {
+export function ViewItem(props: ViewItemProps): JSX.Element {
   const { view, favorite, displayed, selected, layout } = props;
   const { id, label: vname, title: vtitle } = view;
 
@@ -960,7 +1006,7 @@ interface ComponentItemProps {
   docked: boolean;
 }
 
-function ComponentItem(props: ComponentItemProps): JSX.Element {
+export function ComponentItem(props: ComponentItemProps): JSX.Element {
   const { comp, position, selected, active, docked } = props;
   const { id, label, title = label } = comp;
   const icon =
diff --git a/ivette/src/renderer/Application.tsx b/ivette/src/renderer/Application.tsx
index 7a6c3d7b47af58642e433c8b50abebcc0735370f..b5348cb9f56144783d8d2841bd4f61a64443aba4 100644
--- a/ivette/src/renderer/Application.tsx
+++ b/ivette/src/renderer/Application.tsx
@@ -33,10 +33,10 @@ import { LSplit } from 'dome/layout/splitters';
 import * as Toolbar from 'dome/frame/toolbars';
 import * as Sidebar from './Sidebar';
 import * as Controller from './Controller';
-import * as Lab from './Laboratory';
+import { TOOLBAR, STATUSBAR } from 'ivette';
 import * as State from 'ivette/state';
 import * as Search from 'ivette/search';
-import { TOOLBAR, STATUSBAR } from 'ivette';
+import * as Laboratory from 'ivette/laboratory';
 import * as IvettePrefs from 'ivette/prefs';
 import './loader';
 import './sandbox';
@@ -67,7 +67,7 @@ export default function Application(): JSX.Element {
         <Controller.Control />
         <>{ToolBar}</>
         <Toolbar.Filler />
-        <Lab.Tabs />
+        <Laboratory.Tabs />
         <Toolbar.Filler />
         <IvettePrefs.ThemeSwitchTool />
         <IvettePrefs.FontTools />
@@ -81,13 +81,13 @@ export default function Application(): JSX.Element {
       </Toolbar.ToolBar>
       <LSplit settings="frama-c.sidebar.split" unfold={sidebar}>
         <Sidebar.Panel />
-        <Lab.LabView />
+        <Laboratory.LabView />
       </LSplit>
       <Toolbar.ToolBar className="statusbar">
         <Controller.Status />
         <>{StatusBar}</>
         <Toolbar.Filler />
-        <Lab.Dock />
+        <Laboratory.Dock />
       </Toolbar.ToolBar>
     </Vfill>
   );