Skip to main content

A configurable full-screen startup loading overlay for Dash apps, packaged as a Dash Hooks plugin.

Project description

dash-startup-loading-plugin

dash-startup-loading-plugin is an installable Dash Hooks plugin that displays a full-screen loading overlay while a Dash application performs its initial browser-side startup.

The overlay is injected into the final HTML document with hooks.index, so it is visible before Dash and React mount the application layout. The packaged CSS and JavaScript are registered with hooks.stylesheet and hooks.script; an app does not need to copy assets or replace Dash's index_string.

Features

  • Appears before the Dash renderer starts, avoiding a blank startup page.
  • Is discovered automatically through Dash's dash_hooks entry point.
  • Waits for real rendered content and optional app-specific readiness selectors.
  • Can wait for lazy-loading placeholders to disappear.
  • Includes a timeout fallback, minimum display time, and fade-out transition.
  • Supports light and dark themes, reduced-motion preferences, custom colors, custom spinner geometry, and trusted custom loader markup.
  • Exposes a small browser API and emits a completion event.
  • Requires no changes to the app layout or callbacks.

Requirements

  • Python 3.9 or later
  • Dash 3.0.3 or later

Dash Hooks were introduced in Dash 3.0. The plugin uses automatic hook discovery, resource hooks, and the index hook described in the official Dash plugin documentation.

Installation

pip install dash-startup-loading-plugin

The package declares the following entry point:

[project.entry-points."dash_hooks"]
dash_startup_loading_plugin = "dash_startup_loading_plugin"

Dash imports registered dash_hooks packages automatically. Installing the package therefore enables the default startup overlay for Dash applications in that Python environment; an explicit import is only needed when changing its configuration or using its Python API.

Quick start

The defaults work without any plugin-specific code:

from dash import Dash, html

app = Dash(__name__)
app.layout = html.Main(
    [
        html.H1("My Dash app"),
        html.P("The overlay disappears after this layout is mounted."),
    ]
)

if __name__ == "__main__":
    app.run(debug=True)

To customize the readiness conditions or appearance, call configure() before creating the Dash instance:

from dash import Dash, html
from dash_startup_loading_plugin import configure

configure(
    required_selectors=["#page-header", "#sidebar-menu"],
    pending_selector="[data-dac-async-placeholder]",
    timeout_ms=6000,
    minimum_display_ms=200,
    fade_duration_ms=160,
    color="#1677ff",
    dark_color="#4096ff",
)

app = Dash(__name__)
app.layout = html.Div(
    [
        html.Header("Dashboard", id="page-header"),
        html.Nav("Navigation", id="sidebar-menu"),
    ]
)

See examples/basic.py for a runnable example.

Readiness behavior

The overlay closes with the ready reason after all of these conditions are true:

  1. root_selector exists.
  2. The root no longer contains Dash's initial ._dash-loading element.
  3. The root contains an element or non-empty text node.
  4. Every CSS selector in required_selectors exists in the document.
  5. No node matching pending_selector remains under the root.
  6. The conditions stay true for two consecutive animation frames.

A MutationObserver rechecks these conditions as the page changes. The two-frame confirmation prevents the overlay from disappearing during an intermediate render.

timeout_ms is a safety fallback and dismisses the overlay even when the readiness contract is not satisfied. Set it to None to disable forced dismissal. A timeout is not delayed by minimum_display_ms.

Configuration reference

configure(**changes) updates only the supplied values and returns the active, immutable StartupLoadingConfig instance.

Option Default Description
enabled True Inject the startup overlay. Set to False to disable it.
overlay_id "dash-startup-loading" HTML id of the injected overlay; also used by the browser API.
aria_label "Loading" Accessible label on the overlay's role="status" element.
root_selector "#react-entry-point" Dash renderer root observed for mounted content.
required_selectors ("#react-entry-point",) Iterable of document-level CSS selectors that must all exist. A single string is not accepted.
pending_selector "[data-dac-async-placeholder]" Selector for placeholders under the root that delay dismissal. Use None to disable this check.
timeout_ms 6000 Forced-dismiss timeout in milliseconds. Use None to disable it.
minimum_display_ms 0 Minimum display time for ready or manual dismissal.
fade_duration_ms 160 Opacity transition duration before the overlay is removed.
z_index 9999 Overlay stacking order.
background "#ffffff" Light-theme background color.
dark_background "#0f0f0f" Dark-theme background color.
color "#1677ff" Light-theme spinner/current color.
dark_color "#4096ff" Dark-theme spinner/current color.
spinner_size_px 28 Default spinner width and height in pixels.
spinner_stroke_px 3 Default spinner stroke width in pixels.
hide_default_loading True Hide Dash's built-in initial ._dash-loading indicator while the overlay is present.
custom_loader_html None Trusted HTML that replaces the default spinner.

The default stylesheet recognizes html.dark and html.light. When neither class forces a theme, it follows prefers-color-scheme. It also adjusts its animation when the user enables prefers-reduced-motion.

Custom loader markup

from dash_startup_loading_plugin import configure

configure(
    aria_label="Loading dashboard",
    custom_loader_html="""
    <div class="brand-loader" aria-hidden="true">
      <span></span><span></span><span></span>
    </div>
    """,
)

Add the matching .brand-loader rules to the app's normal assets directory. custom_loader_html is inserted verbatim and must be trusted application configuration. Never populate it with user input.

Python API

from dash_startup_loading_plugin import (
    StartupLoadingConfig,
    configure,
    get_config,
    reset_config,
)
  • configure(**changes) validates and applies a partial configuration update.
  • get_config() returns the current immutable configuration.
  • reset_config() restores all defaults. It is primarily useful in tests.
  • StartupLoadingConfig is the frozen dataclass containing all options.

Unknown option names raise TypeError. Invalid selector collections and negative timing or spinner values are rejected rather than silently ignored.

Browser API and events

The plugin exposes two methods for integrations that need explicit control:

// Recheck the configured readiness conditions.
window.dashStartupLoading.check();

// Begin a manual dismissal.
window.dashStartupLoading.finish();

// A custom overlay_id can be supplied to either method.
window.dashStartupLoading.finish("my-loading-overlay");

Before fading out, the overlay dispatches a bubbling dash-startup-loading:ready event. Its detail.reason is "ready", "timeout", or "manual":

document.addEventListener("dash-startup-loading:ready", function (event) {
    console.log("Startup overlay finished:", event.detail.reason);
});

Migrating from a custom index_string

If an app previously injected a loader into a hand-written index_string, move its readiness contract into the plugin and remove the app.index_string = ... assignment:

from dash_startup_loading_plugin import configure

configure(
    required_selectors=["#usage-header", "#usage-sidebar-menu"],
    pending_selector="[data-dac-async-placeholder]",
    timeout_ms=6000,
    fade_duration_ms=160,
)

The plugin preserves Dash's normal index template, injects the overlay directly after the opening <body> tag, and registers its versioned package assets via Dash Hooks. The index hook uses priority 100, so it runs before lower-priority index hooks. If multiple hooks share the same priority, Dash does not guarantee their relative order.

Scope and process model

Dash's hook registry is process-wide. configure() therefore affects every Dash app created in the same Python process. Use one shared configuration per process, or set enabled=False when the overlay should not be injected.

This plugin handles initial application startup only. It does not show a full-screen overlay for later callback execution; use dcc.Loading or another callback-specific loading pattern for that use case.

Troubleshooting

The overlay only disappears after the timeout

One of the configured selectors is probably never becoming ready. Check that:

  • root_selector matches the actual Dash renderer root.
  • Every required_selectors entry exists in the final document.
  • pending_selector does not match a placeholder that remains permanently.
  • Custom selectors are valid CSS selectors.

Temporarily keep the fallback enabled and listen for the completion event. A "timeout" reason confirms that the readiness contract was not met.

The overlay disappears too quickly

Set minimum_display_ms to keep it visible for a predictable minimum duration, or add stable application elements to required_selectors.

The overlay appears in every app in the environment

This is expected with automatic dash_hooks discovery. Use a dedicated virtual environment, uninstall the package where it is not wanted, or call configure(enabled=False) before constructing those Dash apps.

Development

Clone the repository, then install the project and test dependencies:

uv sync --extra test

Run the tests and example:

uv run pytest
uv run python examples/basic.py

Build the source distribution and wheel:

uv build

License

MIT. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dash_startup_loading_plugin-0.1.0.tar.gz (15.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dash_startup_loading_plugin-0.1.0-py3-none-any.whl (12.0 kB view details)

Uploaded Python 3

File details

Details for the file dash_startup_loading_plugin-0.1.0.tar.gz.

File metadata

  • Download URL: dash_startup_loading_plugin-0.1.0.tar.gz
  • Upload date:
  • Size: 15.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.22 {"installer":{"name":"uv","version":"0.9.22","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for dash_startup_loading_plugin-0.1.0.tar.gz
Algorithm Hash digest
SHA256 01aa5a4707ac0b5adbea197e00de78b930a1558e2ae590b16dd860f01848ac37
MD5 a8283865c151999425a73e100b0a9d66
BLAKE2b-256 e2c2c65c5328967d5f742648273401b84f14ec040148d3ca4f2bc2e2c64c5018

See more details on using hashes here.

File details

Details for the file dash_startup_loading_plugin-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: dash_startup_loading_plugin-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 12.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.22 {"installer":{"name":"uv","version":"0.9.22","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for dash_startup_loading_plugin-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2cdbee38a181cd5dcdd6fc08cc4cdf45a7d3398f9e0b8926e59660841db80af4
MD5 41f3c3d8b2b553f8b2eacf544b613df1
BLAKE2b-256 25b55dc0d2a3cfc1555fee354a3a6c72c05b9475f6b6480417838ab951aa117c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page