Skip to content

Styling options

Showing a clone in the sortable list

It is possible to show a clone of the dragged component inside the list by providing showClone = True to SortableGroup. The clone cannot be styled directly but it is possible to override its default style using CSS stylesheets since it holds the data-dnd-placeholder="clone" attribute. For instance, in the example below the background of all the clones of SortableItem components with the 'item' class name is set to green, and the background of the component with ID 'mycustomitem' is set to red:

.item[data-dnd-placeholder="clone"] {
    background-color : green !important;
}

#mycustomitem[data-dnd-placeholder="clone"] {
    background-color : red !important;
}

Customizing the drop animation

When a dragged item is released, it is brought to its final position. By default, no animation is shown but it is possible to define one by providing dropAnimation as an argument of SortableGroup as follows:

SortableGroup(
    ...
    dropAnimation = {
        'duration' : 1000,
        'easing'   : 'ease-in-out'
    }
    ...
)

If provided and not None, dropAnimation must contain two keys: 'duration' and 'easing'. The former corresponds to the length of the animation in millisecond and the latter specifies a CSS easing function.

Customizing the transition animation

Similarly to the drop animation, it is possible to customize the animation played when an item moves from one position to another in the list when dragging occurs. This is set in SortableItem using the transitionAnimation argument as follows:

SortableItem(
    ...
    transitionAnimation = {
        'duration' : 1000,
        'easing'   : 'ease-in-out'
    }
    ...
)

Dynamically style items

For each SortableItem, it is possible to provide CSS stylesheets that are applied when the item is being dragged or dropped without using callbacks. The three following style dictionaries can be provided:

  • 'styles' which corresponds to the default style
  • 'stylesDrag' which is applied on top of 'styles' when the item is being dragged
  • 'stylesDrop' which is applied on top of 'styles' when the item is being dropped
  • 'styleLock' which is applied on top of 'styles' when the item is locked

Each is a dictionary with the following structure: {'div' : CSSDict, 'handle' : CSSDict}, where 'div' styles the parent Div HTML element and 'handle' styles the handle, if provided. It is also possible to style dynamically the items with callbacks, though it is recommended to rather use 'stylesDrag', 'stylesDrop', and 'styleLock'.

Alternatively, a dragged component (e.g. with class name 'item') can be customized via CSS stylesheets as follows:

.item[data-dnd-dragging="true"] {
    ...
}

Dynamically update the handle

It is possible to update the Dash component used as handle and its position when dragging, dropping, or locking it by passing the arguments dynamicHandle' and dynamicHandlePos to SortableItem. Both are dictionaries with the following keys:

  • 'drag' applied when the item is being dragged
  • 'drop' applied when the item is being dropped
  • 'lock' applied when the item is locked

For dynamicHandle, the values associated to the keys must be Dash components, whereas for dynamicHandlePos they must be either 'start' or 'end'. Alternatively, one can update handle and handlePos in a callback using the 'isDragging', 'isDropping', and 'lock' properties as inputs, though it is recommended to rather use dynamicHandle' and dynamicHandlePos.

In the example below, different icons are used depending on the item's state and its position changes from 'start'' to 'end'' when locked and dragged:

import dash
from   dash_iconify        import DashIconify
from   dash_sortable_items import SortableGroup, SortableItem

app = dash.Dash(__name__)

item = SortableItem(
    id        = 'item',
    children  = 'This is a row with a dynamic handle',
    handle    = DashIconify(icon='majesticons:hand-pointer-line', width=50, height=50), # Default handle when idle
    handlePos = 'start', # Default position when idle
    dynamicHandle    = {
        'drag' : DashIconify(icon='majesticons:hand-pointer-event-line', width=50, height=50), # Handle when dragging
        'drop' : DashIconify(icon='majesticons:arrows-collapse-full',    width=50, height=50), # Handle when dropping
        'lock' : DashIconify(icon='majesticons:lock',                    width=50, height=50)  # Handle when locked
    },
    dynamicHandlePos = {
        'drag' : 'end',   # Handle's position when dragging
        'drop' : 'start', # Handle's position when dropping
        'lock' : 'end'    # Handle's position when locked
    }
)

button = dash.html.Button('Lock/Unlock', id='button')

group = SortableGroup(
    id            = 'group', 
    children      = [item], 
    dropAnimation = {'duration' : 2000}
)

app.layout = dash.html.Div([group, button])

@app.callback(
    dash.Output('item', 'lock'),
    dash.Input('button', 'n_clicks'),
)
def lock(n_clicks: int | None) -> bool:

    if n_clicks is None: raise dash.exceptions.PreventUpdate

    return n_clicks % 2 == 1

app.run(debug=True)