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_hooksentry 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:
root_selectorexists.- The root no longer contains Dash's initial
._dash-loadingelement. - The root contains an element or non-empty text node.
- Every CSS selector in
required_selectorsexists in the document. - No node matching
pending_selectorremains under the root. - 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.StartupLoadingConfigis 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_selectormatches the actual Dash renderer root.- Every
required_selectorsentry exists in the final document. pending_selectordoes 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
01aa5a4707ac0b5adbea197e00de78b930a1558e2ae590b16dd860f01848ac37
|
|
| MD5 |
a8283865c151999425a73e100b0a9d66
|
|
| BLAKE2b-256 |
e2c2c65c5328967d5f742648273401b84f14ec040148d3ca4f2bc2e2c64c5018
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2cdbee38a181cd5dcdd6fc08cc4cdf45a7d3398f9e0b8926e59660841db80af4
|
|
| MD5 |
41f3c3d8b2b553f8b2eacf544b613df1
|
|
| BLAKE2b-256 |
25b55dc0d2a3cfc1555fee354a3a6c72c05b9475f6b6480417838ab951aa117c
|