diff --git a/docs/source/gui_handles.md b/docs/source/gui_handles.md
index 9cd30e41..e79fff60 100644
--- a/docs/source/gui_handles.md
+++ b/docs/source/gui_handles.md
@@ -14,6 +14,8 @@
.. autoclass:: viser.GuiMarkdownHandle()
+.. autoclass:: viser.GuiHtmlHandle()
+
.. autoclass:: viser.GuiPlotlyHandle()
.. autoclass:: viser.GuiTabGroupHandle()
diff --git a/src/viser/__init__.py b/src/viser/__init__.py
index e7c48ae6..7ce5a1ad 100644
--- a/src/viser/__init__.py
+++ b/src/viser/__init__.py
@@ -5,6 +5,7 @@
from ._gui_handles import GuiDropdownHandle as GuiDropdownHandle
from ._gui_handles import GuiEvent as GuiEvent
from ._gui_handles import GuiFolderHandle as GuiFolderHandle
+from ._gui_handles import GuiHtmlHandle as GuiHtmlHandle
from ._gui_handles import GuiImageHandle as GuiImageHandle
from ._gui_handles import GuiInputHandle as GuiInputHandle
from ._gui_handles import GuiMarkdownHandle as GuiMarkdownHandle
diff --git a/src/viser/_gui_api.py b/src/viser/_gui_api.py
index e29e9848..261b7679 100644
--- a/src/viser/_gui_api.py
+++ b/src/viser/_gui_api.py
@@ -41,6 +41,7 @@
GuiDropdownHandle,
GuiEvent,
GuiFolderHandle,
+ GuiHtmlHandle,
GuiImageHandle,
GuiMarkdownHandle,
GuiModalHandle,
@@ -631,6 +632,44 @@ def add_markdown(
handle.content = content
return handle
+ def add_html(
+ self,
+ content: str,
+ order: float | None = None,
+ visible: bool = True,
+ ) -> GuiHtmlHandle:
+ """Add HTML to the GUI.
+
+ Args:
+ content: HTML content to display.
+ order: Optional ordering, smallest values will be displayed first.
+ visible: Whether the component is visible.
+
+ Returns:
+ A handle that can be used to interact with the GUI element.
+ """
+ message = _messages.GuiHtmlMessage(
+ uuid=_make_uuid(),
+ container_uuid=self._get_container_uuid(),
+ props=_messages.GuiHtmlProps(
+ order=_apply_default_order(order),
+ content=content,
+ visible=visible,
+ ),
+ )
+ self._websock_interface.queue_message(message)
+
+ handle = GuiHtmlHandle(
+ _GuiHandleState(
+ message.uuid,
+ self,
+ None,
+ props=message.props,
+ parent_container_id=message.container_uuid,
+ ),
+ )
+ return handle
+
def add_image(
self,
image: np.ndarray,
diff --git a/src/viser/_gui_handles.py b/src/viser/_gui_handles.py
index 7276b92b..1efad94f 100644
--- a/src/viser/_gui_handles.py
+++ b/src/viser/_gui_handles.py
@@ -38,6 +38,7 @@
GuiCloseModalMessage,
GuiDropdownProps,
GuiFolderProps,
+ GuiHtmlProps,
GuiImageProps,
GuiMarkdownProps,
GuiMultiSliderProps,
@@ -797,6 +798,10 @@ def content(self, content: str) -> None:
self._markdown = _parse_markdown(content, self._image_root)
+class GuiHtmlHandle(_GuiHandle[None], GuiHtmlProps):
+ """Handling for updating and removing HTML elements."""
+
+
class GuiPlotlyHandle(_GuiHandle[None], GuiPlotlyProps):
"""Handle for updating and removing Plotly figures."""
diff --git a/src/viser/_messages.py b/src/viser/_messages.py
index 991c1e48..db5a6872 100644
--- a/src/viser/_messages.py
+++ b/src/viser/_messages.py
@@ -866,6 +866,22 @@ class GuiMarkdownMessage(_CreateGuiComponentMessage):
props: GuiMarkdownProps
+@dataclasses.dataclass
+class GuiHtmlProps:
+ order: float
+ """Order value for arranging GUI elements. Synchronized automatically when assigned."""
+ content: str
+ """HTML content to be displayed. Synchronized automatically when assigned."""
+ visible: bool
+ """Visibility state of the markdown element. Synchronized automatically when assigned."""
+
+
+@dataclasses.dataclass
+class GuiHtmlMessage(_CreateGuiComponentMessage):
+ container_uuid: str
+ props: GuiHtmlProps
+
+
@dataclasses.dataclass
class GuiProgressBarProps:
order: float
diff --git a/src/viser/client/src/ControlPanel/Generated.tsx b/src/viser/client/src/ControlPanel/Generated.tsx
index c1fdc67f..3540ddb8 100644
--- a/src/viser/client/src/ControlPanel/Generated.tsx
+++ b/src/viser/client/src/ControlPanel/Generated.tsx
@@ -23,6 +23,7 @@ import MultiSliderComponent from "../components/MultiSlider";
import UploadButtonComponent from "../components/UploadButton";
import ProgressBarComponent from "../components/ProgressBar";
import ImageComponent from "../components/Image";
+import HtmlComponent from "../components/Html";
/** Root of generated inputs. */
export default function GeneratedGuiContainer({
@@ -101,6 +102,8 @@ function GeneratedInput(props: { guiUuid: string }) {
return