# ui.scene | NiceGUI

[Button: icon:menu]

[](/)

[Installation](/#installation)

[Features](/#features)

[Demos](/#demos)

[Documentation](/documentation)

[Examples](/examples)

[Why?](/#why)

[Button]

[Button]

[](https://github.com/zauberzeug/nicegui/)

[Button: icon:more_vert]

# ui.*scene*

## 3D Scene

Display a 3D scene using `three.js <https://threejs.org/>`_.
Currently NiceGUI supports boxes, spheres, cylinders/cones, extrusions, straight lines, curves and textured meshes.
Objects can be translated, rotated and displayed with different color, opacity or as wireframes.
They can also be grouped to apply joint movements.

:width: width of the canvas
:height: height of the canvas
:grid: whether to display a grid (boolean or tuple of ``size`` and ``divisions`` for `Three.js' GridHelper <https://threejs.org/docs/#api/en/helpers/GridHelper>`_, default: 100x100)
:camera: camera definition, either instance of ``ui.scene.perspective_camera`` (default) or ``ui.scene.orthographic_camera``
:on_click: callback to execute when a 3D object is clicked (use ``click_events`` to specify which events to subscribe to)
:click_events: list of JavaScript click events to subscribe to (default: ``['click', 'dblclick']``)
:on_drag_start: callback to execute when a 3D object is dragged
:on_drag_end: callback to execute when a 3D object is dropped
:drag_constraints: comma-separated JavaScript expression for constraining positions of dragged objects (e.g. ``'x = 0, z = y / 2'``)
:background_color: background color of the scene (default: "#eee")
:control_type: type of controls to use for navigating the scene, one of "orbit", "trackball", "map" (default: "orbit", *added in version 3.9.0*)
:fps: target frame rate for the scene in frames per second (default: 20, *added in version 3.2.0*)
:show_stats: whether to show performance stats (default: ``False``, *added in version 3.2.0*)

main.py

[Button]

````python
from nicegui import ui

with ui.scene().classes('w-full h-64') as scene:
    scene.axes_helper()
    scene.sphere().material('#4488ff').move(2, 2)
    scene.cylinder(1, 0.5, 2, 20).material('#ff8800', opacity=0.5).move(-2, 1)
    scene.extrusion([[0, 0], [0, 1], [1, 0.5]], 0.1).material('#ff8888').move(2, -1)

    with scene.group().move(z=2):
        scene.box().move(x=2)
        scene.box().move(y=2).rotate(0.25, 0.5, 0.75)
        scene.box(wireframe=True).material('#888888').move(x=2, y=2)

    scene.line([-4, 0, 0], [-4, 2, 0]).material('#ff0000')
    scene.curve([-4, 0, 0], [-4, -1, 0], [-3, -1, 0], [-3, 0, 0]).material('#008800')

    logo = 'https://avatars.githubusercontent.com/u/2843826'
    scene.texture(logo, [[[0.5, 2, 0], [2.5, 2, 0]],
                         [[0.5, 0, 0], [2.5, 0, 0]]]).move(1, -3)

    teapot = 'https://upload.wikimedia.org/wikipedia/commons/9/93/Utah_teapot_(solid).stl'
    scene.stl(teapot).scale(0.2).move(-3, 4)

    avocado = 'https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/main/Models/Avocado/glTF-Binary/Avocado.glb'
    scene.gltf(avocado).scale(40).move(-2, -3, 0.5)

    scene.text('2D', 'background: rgba(0, 0, 0, 0.2); border-radius: 5px; padding: 5px').move(z=2)
    scene.text3d('3D', 'background: rgba(0, 0, 0, 0.2); border-radius: 5px; padding: 5px').move(y=-2).scale(.05)

ui.run()
````

## Handling Click Events

You can use the `on_click` argument to `ui.scene` to handle click events.
The callback receives a `SceneClickEventArguments` object with the following attributes:

- `click_type`: the type of click ("click" or "dblclick").
- `button`: the button that was clicked (1, 2, or 3).
- `alt`, `ctrl`, `meta`, `shift`: whether the alt, ctrl, meta, or shift key was pressed.
- `hits`: a list of `SceneClickEventHit` objects, sorted by distance from the camera.

The `SceneClickEventHit` object has the following attributes:

- `object_id`: the id of the object that was clicked.
- `object_name`: the name of the object that was clicked.
- `x`, `y`, `z`: the x, y and z coordinates of the click.

main.py

[Button]

````python
from nicegui import events, ui

def handle_click(e: events.SceneClickEventArguments):
    hit = e.hits[0]
    name = hit.object_name or hit.object_id
    ui.notify(f'You clicked on the {name} at ({hit.x:.2f}, {hit.y:.2f}, {hit.z:.2f})')

with ui.scene(width=285, height=220, on_click=handle_click) as scene:
    scene.sphere().move(x=-1, z=1).with_name('sphere')
    scene.box().move(x=1, z=1).with_name('box')

ui.run()
````

## Context menu for 3D objects

This demo shows how to create a context menu for 3D objects.
By setting the `click_events` argument to `['contextmenu']`, the `handle_click` function will be called on right-click.
It clears the context menu and adds items based on the object that was clicked.

main.py

[Button]

````python
from nicegui import events, ui

def handle_click(e: events.SceneClickEventArguments) -> None:
    name = next((hit.object_name for hit in e.hits if hit.object_name), None)
    with context_menu.clear():
        if name == 'sphere':
            ui.item('SPHERE').classes('font-bold')
            ui.menu_item('inspect')
            ui.menu_item('open')
        if name == 'box':
            ui.item('BOX').classes('font-bold')
            ui.menu_item('rotate')
            ui.menu_item('move')

with ui.element():
    context_menu = ui.context_menu()
    with ui.scene(width=285, height=220, on_click=handle_click,
                  click_events=['contextmenu']) as scene:
        scene.sphere().move(x=-1, z=1).with_name('sphere')
        scene.box().move(x=1, z=1).with_name('box')

ui.run()
````

## Draggable objects

You can make objects draggable using the `.draggable` method.
There is an optional `on_drag_start` and `on_drag_end` argument to `ui.scene` to handle drag events.
The callbacks receive a `SceneDragEventArguments` object with the following attributes:

- `type`: the type of drag event ("dragstart" or "dragend").
- `object_id`: the id of the object that was dragged.
- `object_name`: the name of the object that was dragged.
- `x`, `y`, `z`: the x, y and z coordinates of the dragged object.

You can also use the `drag_constraints` argument to set comma-separated JavaScript expressions
for constraining positions of dragged objects.

main.py

[Button]

````python
from nicegui import events, ui

def handle_drag(e: events.SceneDragEventArguments):
    ui.notify(f'You dropped the sphere at ({e.x:.2f}, {e.y:.2f}, {e.z:.2f})')

with ui.scene(width=285, height=220,
              drag_constraints='z = 1', on_drag_end=handle_drag) as scene:
    sphere = scene.sphere().move(z=1).draggable()

ui.switch('draggable sphere',
          value=sphere.draggable_,
          on_change=lambda e: sphere.draggable(e.value))

ui.run()
````

## Subscribe to the drag event

By default, a draggable object is only updated when the drag ends to avoid performance issues.
But you can explicitly subscribe to the "drag" event to get immediate updates.
In this demo we update the position and size of a box based on the positions of two draggable spheres.

main.py

[Button]

````python
from nicegui import events, ui

with ui.scene(width=285, drag_constraints='z=0') as scene:
    box = scene.box(1, 1, 0.2).move(0, 0).material('Orange')
    sphere1 = scene.sphere(0.2).move(0.5, -0.5).material('SteelBlue').draggable()
    sphere2 = scene.sphere(0.2).move(-0.5, 0.5).material('SteelBlue').draggable()

def handle_drag(e: events.GenericEventArguments) -> None:
    x1 = sphere1.x if e.args['object_id'] == sphere2.id else e.args['x']
    y1 = sphere1.y if e.args['object_id'] == sphere2.id else e.args['y']
    x2 = sphere2.x if e.args['object_id'] == sphere1.id else e.args['x']
    y2 = sphere2.y if e.args['object_id'] == sphere1.id else e.args['y']
    box.move((x1 + x2) / 2, (y1 + y2) / 2).scale(x2 - x1, y2 - y1, 1)
scene.on('drag', handle_drag)

ui.run()
````

## Rendering point clouds

You can render point clouds using the `point_cloud` method.
The `points` argument is a list of point coordinates, and the `colors` argument is a list of RGB colors (0..1).
You can update the cloud using its `set_points()` method.

main.py

[Button]

````python
import numpy as np
from nicegui import ui

def generate_data(frequency: float = 1.0):
    x, y = np.meshgrid(np.linspace(-3, 3), np.linspace(-3, 3))
    z = np.sin(x * frequency) * np.cos(y * frequency) + 1
    points = np.dstack([x, y, z]).reshape(-1, 3)
    colors = points / [6, 6, 2] + [0.5, 0.5, 0]
    return points, colors

with ui.scene().classes('w-full h-64') as scene:
    points, colors = generate_data()
    point_cloud = scene.point_cloud(points, colors, point_size=0.1)

ui.slider(min=0.1, max=3, step=0.1, value=1) \
    .on_value_change(lambda e: point_cloud.set_points(*generate_data(e.value)))

ui.run()
````

## Wait for Initialization

You can wait for the scene to be initialized with the `initialized` method.
This demo animates a camera movement after the scene has been fully loaded.

main.py

[Button]

````python
from nicegui import ui

@ui.page('/')
async def page():
    with ui.scene(width=285, height=220) as scene:
        scene.sphere()
        await scene.initialized()
        scene.move_camera(x=1, y=-1, z=1.5, duration=2)

ui.run()
````

## Changing Controls

You can change the controls of a scene using the `control_type` argument.

The available control types are:

- **"orbit" (default)**:
  Works for most applications.
  But the camera stops when it orbits over the "north" and "south" poles to maintain a fixed up direction.
- "trackball":
  Similar to orbit, but it keeps going around the poles.
  It is a good choice for applications where camera flexibility is important.
- "map":
  Allows to pan and zoom like in a 3D map view application.
  Good for map-like applications such as in [RoSys](https://rosys.io).

main.py

[Button]

````python
from nicegui import ui

ui.label('Orbit controls (default)')
with ui.scene(width=285, height=220):
    ui.scene.sphere()

ui.label('Trackball controls')
with ui.scene(width=285, height=220, control_type='trackball'):
    ui.scene.sphere()

ui.label('Map controls')
with ui.scene(width=285, height=220, control_type='map'):
    ui.scene.sphere()

ui.run()
````

## Scene View

Display an additional view of a 3D scene using `three.js <https://threejs.org/>`_.
This component can only show a scene and not modify it.
You can, however, independently move the camera.

Current limitation: 2D and 3D text objects are not supported and will not be displayed in the scene view.

:scene: the scene which will be shown on the canvas
:width: width of the canvas
:height: height of the canvas
:camera: camera definition, either instance of ``ui.scene.perspective_camera`` (default) or ``ui.scene.orthographic_camera``
:on_click: callback to execute when a 3D object is clicked
:fps: target frame rate for the scene view in frames per second (default: 20, *added in version 3.2.0*)
:show_stats: whether to show performance stats (default: ``False``, *added in version 3.2.0*)

main.py

[Button]

````python
from nicegui import ui

with ui.grid(columns=2).classes('w-full'):
    with ui.scene().classes('h-64 col-span-2') as scene:
        teapot = 'https://upload.wikimedia.org/wikipedia/commons/9/93/Utah_teapot_(solid).stl'
        scene.stl(teapot).scale(0.3)

    with ui.scene_view(scene).classes('h-32') as scene_view1:
        scene_view1.move_camera(x=1, y=-3, z=5)

    with ui.scene_view(scene).classes('h-32') as scene_view2:
        scene_view2.move_camera(x=0, y=4, z=3)

ui.run()
````

## Frame Rate and Statistics

You can configure the target frames per second (FPS) of the scene using the `fps` argument.
The default value is 20.
To see the changes for yourself, enable the statistics display using the `show_stats` argument.
This demo shows how to set the frame rate to 40 FPS for the main scene and 5 FPS for the static view.
The FPS is generally lower than the target frame rate, because the browser also takes some time to render the scene.
This also applies to `ui.scene_view`.

*Added in version 3.2.0*

main.py

[Button]

````python
from nicegui import ui

ui.label('Higher frame rate for the movable view')
with ui.scene(fps=40, show_stats=True).classes('w-full h-32') as scene:
    scene.sphere()

ui.label('Lower frame rate for the static view')
with ui.scene_view(scene, fps=5, show_stats=True).classes('w-full h-32') as scene_view:
    scene_view.move_camera(x=1, y=-3, z=5)

ui.run()
````

## Camera Parameters

You can use the `camera` argument to `ui.scene` to use a custom camera.
This allows you to set the field of view of a perspective camera or the size of an orthographic camera.

main.py

[Button]

````python
from nicegui import ui

with ui.scene(camera=ui.scene.orthographic_camera(size=2)) \
        .classes('w-full h-64') as scene:
    scene.box()

ui.run()
````

## Get current camera pose

Using the `get_camera` method you can get a dictionary of current camera parameters like position, rotation, field of view and more.
This demo shows how to continuously move a sphere towards the camera.
Try moving the camera around to see the sphere following it.

main.py

[Button]

````python
from nicegui import ui

with ui.scene().classes('w-full h-64') as scene:
    ball = scene.sphere()

async def move():
    camera = await scene.get_camera()
    if camera is not None:
        ball.move(x=0.95 * ball.x + 0.05 * camera['position']['x'],
                  y=0.95 * ball.y + 0.05 * camera['position']['y'],
                  z=1.0)
ui.timer(0.1, move)

ui.run()
````

## Custom Background

You can set a custom background color using the `background_color` parameter of `ui.scene`.

main.py

[Button]

````python
from nicegui import ui

with ui.scene(background_color='#222').classes('w-full h-64') as scene:
    scene.box()

ui.run()
````

## Custom Grid

You can set custom grid parameters using the `grid` parameter of `ui.scene`.
It accepts a tuple of two integers, the first one for the grid size and the second one for the number of divisions.

main.py

[Button]

````python
from nicegui import ui

with ui.scene(grid=(3, 2)).classes('w-full h-64') as scene:
    scene.sphere()

ui.run()
````

## Custom Composed 3D Objects

This demo creates a custom class for visualizing a coordinate system with colored X, Y and Z axes.
This can be a nice alternative to the default `axes_helper` object.

main.py

[Button]

````python
import math
from nicegui import ui

class CoordinateSystem(ui.scene.group):

    def __init__(self, name: str, *, length: float = 1.0) -> None:
        super().__init__()

        with self:
            for label, color, rx, ry, rz in [
                ('x', '#ff0000', 0, 0, -math.pi / 2),
                ('y', '#00ff00', 0, 0, 0),
                ('z', '#0000ff', math.pi / 2, 0, 0),
            ]:
                with ui.scene.group().rotate(rx, ry, rz):
                    ui.scene.cylinder(0.02 * length, 0.02 * length, 0.8 * length) \
                        .move(y=0.4 * length).material(color)
                    ui.scene.cylinder(0, 0.1 * length, 0.2 * length) \
                        .move(y=0.9 * length).material(color)
                    ui.scene.text(label, style=f'color: {color}') \
                        .move(y=1.1 * length)
            ui.scene.text(name, style='color: #808080')

with ui.scene().classes('w-full h-64'):
    CoordinateSystem('origin')
    CoordinateSystem('custom frame').move(-2, -2, 1).rotate(0.1, 0.2, 0.3)

ui.run()
````

## Attaching/detaching objects

To add or remove objects from groups you can use the `attach` and `detach` methods.
The position and rotation of the object are preserved so that the object does not move in space.
But note that scaling is not preserved.
If either the parent or the object itself is scaled, the object shape and position can change.

*Added in version 2.7.0*

main.py

[Button]

````python
import math
import time
from nicegui import ui

with ui.scene().classes('w-full h-64') as scene:
    with scene.group() as group:
        a = scene.box().move(-2)
        b = scene.box().move(0)
        c = scene.box().move(2)

ui.timer(0.1, lambda: group.move(y=math.sin(time.time())).rotate(0, 0, time.time()))
ui.button('Detach', on_click=a.detach)
ui.button('Attach', on_click=lambda: a.attach(group))

ui.run()
````

## Reference

## Initializer

:width: width of the canvas
:height: height of the canvas
:grid: whether to display a grid (boolean or tuple of ``size`` and ``divisions`` for `Three.js' GridHelper <https://threejs.org/docs/#api/en/helpers/GridHelper>`_, default: 100x100)
:camera: camera definition, either instance of ``ui.scene.perspective_camera`` (default) or ``ui.scene.orthographic_camera``
:on_click: callback to execute when a 3D object is clicked (use ``click_events`` to specify which events to subscribe to)
:click_events: list of JavaScript click events to subscribe to (default: ``['click', 'dblclick']``)
:on_drag_start: callback to execute when a 3D object is dragged
:on_drag_end: callback to execute when a 3D object is dropped
:drag_constraints: comma-separated JavaScript expression for constraining positions of dragged objects (e.g. ``'x = 0, z = y / 2'``)
:background_color: background color of the scene (default: "#eee")
:control_type: type of controls to use for navigating the scene, one of "orbit", "trackball", "map" (default: "orbit", *added in version 3.9.0*)
:fps: target frame rate for the scene in frames per second (default: 20, *added in version 3.2.0*)
:show_stats: whether to show performance stats (default: ``False``, *added in version 3.2.0*)

## Properties

**Inherited properties**

**`classes`**`: 'Classes[Self]'`

The classes of the element.

**`client`**`: 'Client'`

The client this element belongs to.

**`html_id`**`: 'str'`

The ID of the element in the HTML DOM.

*Added in version 2.16.0*

**`is_deleted`**`: 'bool'`

Whether the element has been deleted.

**`is_ignoring_events`**`: bool`

Return whether the element is currently ignoring events.

**`parent_slot`**`: 'Slot | None' (settable)`

The parent slot of the element.

**`props`**`: 'Props[Self]'`

The props of the element.

**`style`**`: 'Style[Self]'`

The style of the element.

**`visible`**`: BindableProperty`

## Methods

**`clear`**`() -> Self`

Remove all objects from the scene.

**`delete_objects`**`(predicate: collections.abc.Callable[[nicegui.elements.scene.scene_object3d.Object3D], bool] = [...]) -> None`

Remove objects from the scene.

:param predicate: function which returns `True` for objects which should be deleted

**`get_camera`**`() -> dict[str, Any]`

Get the current camera parameters.

In contrast to the `camera` property,
the result of this method includes the current camera pose caused by the user navigating the scene in the browser.

**`initialized`**`() -> None`

Wait until the scene is initialized.

**`move_camera`**`(x: float | None = None, y: float | None = None, z: float | None = None, look_at_x: float | None = None, look_at_y: float | None = None, look_at_z: float | None = None, up_x: float | None = None, up_y: float | None = None, up_z: float | None = None, duration: float = 0.5) -> None`

Move the camera to a new position.

:param x: camera x position
:param y: camera y position
:param z: camera z position
:param look_at_x: camera look-at x position
:param look_at_y: camera look-at y position
:param look_at_z: camera look-at z position
:param up_x: x component of the camera up vector
:param up_y: y component of the camera up vector
:param up_z: z component of the camera up vector
:param duration: duration of the movement in seconds (default: `0.5`)

**`on_click`**`(callback: collections.abc.Callable[[nicegui.events.SceneClickEventArguments], Any] | collections.abc.Callable[[], Any]) -> Self`

Add a callback to be invoked when a 3D object is clicked.

**`on_drag_end`**`(callback: collections.abc.Callable[[nicegui.events.SceneDragEventArguments], Any] | collections.abc.Callable[[], Any]) -> Self`

Add a callback to be invoked when a 3D object is dropped.

**`on_drag_start`**`(callback: collections.abc.Callable[[nicegui.events.SceneDragEventArguments], Any] | collections.abc.Callable[[], Any]) -> Self`

Add a callback to be invoked when a 3D object is dragged.

`@staticmethod`<br />**`orthographic_camera`**`(size: float = 10, near: float = 0.1, far: float = 1000) -> nicegui.elements.scene.scene.SceneCamera`

Create a orthographic camera.

The size defines the vertical size of the view volume, i.e. the distance between the top and bottom clipping planes.
The left and right clipping planes are set such that the aspect ratio matches the viewport.

:param size: vertical size of the view volume
:param near: near clipping plane
:param far: far clipping plane

`@staticmethod`<br />**`perspective_camera`**`(fov: float = 75, near: float = 0.1, far: float = 1000) -> nicegui.elements.scene.scene.SceneCamera`

Create a perspective camera.

:param fov: vertical field of view in degrees
:param near: near clipping plane
:param far: far clipping plane

**Inherited methods**

**`add_dynamic_resource`**`(name: str, function: Callable) -> None`

Add a dynamic resource to the element which returns the result of a function.

:param name: name of the resource
:param function: function that returns the resource response

**`add_resource`**`(path: str | Path) -> None`

Add a resource to the element.

:param path: path to the resource (e.g. folder with CSS and JavaScript files)

**`add_slot`**`(name: str, template: str | None = None) -> Slot`

Add a slot to the element.

NiceGUI is using the slot concept from Vue:
Elements can have multiple slots, each possibly with a number of children.
Most elements only have one slot, e.g. a `ui.card` (QCard) only has a default slot.
But more complex elements like `ui.table` (QTable) can have more slots like "header", "body" and so on.
If you nest NiceGUI elements via with `ui.row(): ...` you place new elements inside of the row's default slot.
But if you use with `table.add_slot(...): ...`, you enter a different slot.

The slot stack helps NiceGUI to keep track of which slot is currently used for new elements.
The `parent` field holds a reference to its element.
Whenever an element is entered via a `with` expression, its default slot is automatically entered as well.

:param name: name of the slot
:param template: Vue template of the slot
:return: the slot

**`ancestors`**`(include_self: bool = False) -> Iterator[Element]`

Iterate over the ancestors of the element.

:param include_self: whether to include the element itself in the iteration

**`bind_visibility`**`(target_object: Any, target_name: str | tuple[str, ...] = 'visible', forward: collections.abc.Callable[[Any], Any] | None = None, backward: collections.abc.Callable[[Any], Any] | None = None, value: Any = None, strict: bool | None = None) -> Self`

Bind the visibility of this element to the target object's target_name property.

The binding works both ways, from this element to the target and from the target to this element.
The update happens immediately and whenever a value changes.
The backward binding takes precedence for the initial synchronization.
The ``target_name`` parameter also accepts a tuple of strings for nested keys (*since version 3.10.0*).

:param target_object: The object to bind to.
:param target_name: The name of the property to bind to.
:param forward: A function to apply to the value before applying it to the target (default: identity).
:param backward: A function to apply to the value before applying it to this element (default: identity).
:param value: If specified, the element will be visible only when the target value is equal to this value.
:param strict: Whether to check (and raise) if the target object has the specified property (default: None,
    performs a check if the object is not a dictionary, *added in version 3.0.0*).

**`bind_visibility_from`**`(target_object: Any, target_name: str | tuple[str, ...] = 'visible', backward: collections.abc.Callable[[Any], Any] | None = None, value: Any = None, strict: bool | None = None) -> Self`

Bind the visibility of this element from the target object's target_name property.

The binding works one way only, from the target to this element.
The update happens immediately and whenever a value changes.
The ``target_name`` parameter also accepts a tuple of strings for nested keys (*since version 3.10.0*).

:param target_object: The object to bind from.
:param target_name: The name of the property to bind from.
:param backward: A function to apply to the value before applying it to this element (default: identity).
:param value: If specified, the element will be visible only when the target value is equal to this value.
:param strict: Whether to check (and raise) if the target object has the specified property (default: None,
    performs a check if the object is not a dictionary, *added in version 3.0.0*).

**`bind_visibility_to`**`(target_object: Any, target_name: str | tuple[str, ...] = 'visible', forward: collections.abc.Callable[[Any], Any] | None = None, strict: bool | None = None) -> Self`

Bind the visibility of this element to the target object's target_name property.

The binding works one way only, from this element to the target.
The update happens immediately and whenever a value changes.
The ``target_name`` parameter also accepts a tuple of strings for nested keys (*since version 3.10.0*).

:param target_object: The object to bind to.
:param target_name: The name of the property to bind to.
:param forward: A function to apply to the value before applying it to the target (default: identity).
:param strict: Whether to check (and raise) if the target object has the specified property (default: None,
    performs a check if the object is not a dictionary, *added in version 3.0.0*).

`@classmethod`<br />**`default_classes`**`(add: str | None = None, remove: str | None = None, toggle: str | None = None, replace: str | None = None) -> type[Self]`

Apply, remove, toggle, or replace default HTML classes.

This allows modifying the look of the element or its layout using `Tailwind <https://tailwindcss.com/>`_ or `Quasar <https://quasar.dev/>`_ classes.

Removing or replacing classes can be helpful if predefined classes are not desired.
All elements of this class will share these HTML classes.
These must be defined before element instantiation.

:param add: whitespace-delimited string of classes
:param remove: whitespace-delimited string of classes to remove from the element
:param toggle: whitespace-delimited string of classes to toggle (*added in version 2.7.0*)
:param replace: whitespace-delimited string of classes to use instead of existing ones

`@classmethod`<br />**`default_props`**`(add: str | None = None, remove: str | None = None) -> type[Self]`

Add or remove default props.

This allows modifying the look of the element or its layout using `Quasar <https://quasar.dev/>`_ props.
Since props are simply applied as HTML attributes, they can be used with any HTML element.
All elements of this class will share these props.
These must be defined before element instantiation.

Boolean properties are assumed ``True`` if no value is specified.

:param add: whitespace-delimited list of either boolean values or key=value pair to add
:param remove: whitespace-delimited list of property keys to remove

`@classmethod`<br />**`default_style`**`(add: str | None = None, remove: str | None = None, replace: str | None = None) -> type[Self]`

Apply, remove, or replace default CSS definitions.

Removing or replacing styles can be helpful if the predefined style is not desired.
All elements of this class will share these CSS definitions.
These must be defined before element instantiation.

:param add: semicolon-separated list of styles to add to the element
:param remove: semicolon-separated list of styles to remove from the element
:param replace: semicolon-separated list of styles to use instead of existing ones

**`delete`**`() -> None`

Delete the element and all its children.

**`descendants`**`(include_self: bool = False) -> Iterator[Element]`

Iterate over the descendants of the element.

:param include_self: whether to include the element itself in the iteration

**`get_computed_prop`**`(prop_name: str, timeout: float = 1) -> AwaitableResponse`

Return a computed property.

This function should be awaited so that the computed property is properly returned.

:param prop_name: name of the computed prop
:param timeout: maximum time to wait for a response (default: 1 second)

**`mark`**`(*markers: str) -> Self`

Replace markers of the element.

Markers are used to identify elements for querying with `ElementFilter </documentation/element_filter>`_
which is heavily used in testing
but can also be used to reduce the number of global variables or passing around dependencies.

:param markers: list of strings or single string with whitespace-delimited markers; replaces existing markers

**`move`**`(target_container: Element | None = None, target_index: int = -1, target_slot: str | None = None) -> Self`

Move the element to another container.

:param target_container: container to move the element to (default: the parent container)
:param target_index: index within the target slot (default: append to the end)
:param target_slot: slot within the target container (default: default slot)

**`on`**`(type: str, handler: events.Handler[events.GenericEventArguments] | None = None, args: None | Sequence[str] | Sequence[Sequence[str] | None] = None, throttle: float = 0.0, leading_events: bool = True, trailing_events: bool = True, js_handler: str = '(...args) => emit(...args)') -> Self`

Subscribe to an event.

The event handler can be a Python function, a JavaScript function or a combination of both:

- If you want to handle the event on the server with all (serializable) event arguments,
  use a Python ``handler``.
- If you want to handle the event on the client side without emitting anything to the server,
  use ``js_handler`` with a JavaScript function handling the event.
- If you want to handle the event on the server with a subset or transformed version of the event arguments,
  use ``js_handler`` with a JavaScript function emitting the transformed arguments using ``emit()``, and
  use a Python ``handler`` to handle these arguments on the server side.
  The ``js_handler`` can also decide to selectively emit arguments to the server,
  in which case the Python ``handler`` will not always be called.

Note that the arguments ``throttle``, ``leading_events``, and ``trailing_events`` are only relevant
when emitting events to the server.

*Updated in version 2.18.0: Both handlers can be specified at the same time.*

:param type: name of the event (e.g. "click", "mousedown", or "update:model-value")
:param handler: callback that is called upon occurrence of the event
:param args: arguments included in the event message sent to the event handler (default: ``None`` meaning all)
:param throttle: minimum time (in seconds) between event occurrences (default: 0.0)
:param leading_events: whether to trigger the event handler immediately upon the first event occurrence (default: ``True``)
:param trailing_events: whether to trigger the event handler after the last event occurrence (default: ``True``)
:param js_handler: JavaScript function that is handling the event on the client (default: "(...args) => emit(...args)")

**`remove`**`(element: Element | int) -> None`

Remove a child element.

:param element: either the element instance or its ID

**`run_method`**`(name: str, *args: Any, timeout: float = 1) -> AwaitableResponse`

Run a method on the client side.

If the function is awaited, the result of the method call is returned.
Otherwise, the method is executed without waiting for a response.

:param name: name of the method
:param args: arguments to pass to the method
:param timeout: maximum time to wait for a response (default: 1 second)

**`set_visibility`**`(visible: bool) -> Self`

Set the visibility of this element.

:param visible: Whether the element should be visible.

**`tooltip`**`(text: str) -> Self`

Add a tooltip to the element.

:param text: text of the tooltip

**`update`**`() -> None`

Update the element on the client side.

## Inheritance

- `Element`
- `Visibility`

**Nice**GUI

The Python UI framework that shows up in your browser.

[](https://github.com/zauberzeug/nicegui/)

[](https://discord.gg/TEpFeAaF4f)

[](https://www.reddit.com/r/nicegui/)

Resources

[Documentation](/documentation)

[Examples](/examples)

[LLM reference](/llms.txt)

[GitHub](https://github.com/zauberzeug/nicegui/)

[PyPI](https://pypi.org/project/nicegui/)

Community

[Discussions](https://github.com/zauberzeug/nicegui/discussions)

[Discord](https://discord.gg/TEpFeAaF4f)

[Reddit](https://www.reddit.com/r/nicegui/)

[Contributing](https://github.com/zauberzeug/nicegui/blob/main/CONTRIBUTING.md)

[Sponsors](https://github.com/sponsors/zauberzeug)

Legal

[Imprint](/imprint_privacy#imprint)

[Privacy](/imprint_privacy#privacy)

Made with NiceGUI by [Zauberzeug](https://zauberzeug.com)

© 2026 [Zauberzeug GmbH](https://zauberzeug.com)