Library / SDK
GibbsConsulting/django-plotly-dash avatar
GibbsConsulting/django-plotly-dash

django-plotly-dash: embedding Dash apps in Django templates as tags

Expose plotly dash apps as django tags

592 stars133 forksPythonMIT

At a glance

What is it?
django-plotly-dash exposes Plotly Dash applications inside a Django project through a template tag, so several Dash apps can share a page, share internal state and reach the current user and session. The trade is that the Dash app stops being a standalone server.
Who is it for?
Adopt django-plotly-dash when you already run Django and want to reuse an existing Dash app inside a Django page, keeping the login and session handling in one place. Do not adopt it if you want Dash to keep its own deployment, since embedding a Dash app in Django changes how the callbacks and their state are served.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 165 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the package actually bridges

The repository description is short: expose plotly dash apps as django tags. The README expands it with the useful detail. Expose Plotly Dash apps as Django tags, so multiple Dash apps can then be embedded into a single web page, persist and share internal state, and also have access to the current user and session variables.

That is the whole proposition, and it is narrower than it may first sound. This is not a Dash-to-Django exporter, and it is not a set of chart components for Django templates. It is a way to run a Dash application inside a Django page.

The mechanism is a class substitution. The `DjangoDash` class replaces the `Dash` class of the dash package, and the Dash application is registered under a name. That name is then used to identify the app in templates through a template tag.

The three capabilities follow from that arrangement. Multiple apps on one page work because each is embedded independently but sits in the same Django-rendered document. Shared internal state works because the apps live in the same Django process and can be given a common store. Access to the current user and session works because the request that serves the page is a Django request, not a Dash request, so the usual Django session machinery is in reach.

The licence is MIT, the primary language is Python, and the documentation lives at https://django-plotly-dash.readthedocs.io/en/latest/ with an online demo at https://djangoplotlydash.com.

The four installation steps, and one that is easy to miss

Installation is four steps, and the third one is where people get stuck.

The package install pulls in plotly and the dash packages if they are not already present.

bash
    pip install django_plotly_dash

Then the app is added to INSTALLED_APPS in your Django settings.py file.

python
    INSTALLED_APPS = [
        ...
        'django_plotly_dash.apps.DjangoPlotlyDashConfig',
        ...
        ]

Then the routes are registered with an include statement in a urls.py file. The README is emphatic about this step: django_plotly_dash is not a name of your application, it is referring to the inner namespace of this library, and you should not skip this step.

python
    urlpatterns = [
        ...
        path('django_plotly_dash/', include('django_plotly_dash.urls')),
    ]

The name within the URL is not important and can be changed.

Then the migration.

bash
    ./manage.py migrate

And the step that produces a blank page when it is missing. The use of frames within HTML documents has to be enabled by adding to the settings.py file.

python
    X_FRAME_OPTIONS = 'SAMEORIGIN'

That last one is a consequence of how the app is embedded rather than a stylistic choice. The Dash app renders inside a frame in the Django page, and a browser will refuse to display framed content unless the server permits it. Setting this in a project that does other things with framing is worth a thought, since it applies to the whole site rather than to the Dash app alone.

Registering the app, and where the import has to live

The usage pattern is to take an existing Dash application and register it with DjangoDash instead of Dash, then use the name in a template.

python
import dash
from dash import dcc, html

from django_plotly_dash import DjangoDash

app = DjangoDash('SimpleExample')

app.layout = html.Div([
    dcc.RadioItems(
        id='dropdown-color',
        options=[{'label': c, 'value': c.lower()} for c in ['Red', 'Green', 'Blue']],
        value='red'
    ),
    html.Div(id='output-color'),

The layout and the callbacks are ordinary Dash code. The callbacks use the usual dash.dependencies Output and Input declarations and are attached with the app.callback decorator.

python
@app.callback(
    dash.dependencies.Output('output-color', 'children'),
    [dash.dependencies.Input('dropdown-color', 'value')])
def callback_color(dropdown_value):
    return "The selected color is %s." % dropdown_value

The DjangoDash constructor requires a name to be specified, and that name is then used to identify the dash app in templates.

jinja2
{% load plotly_dash %}

{% plotly_app name="SimpleExample" %}

The template tag is loaded with the standard load tag, and the name is what ties the template to the registered application.

The one non-obvious requirement is import ordering. The registration code needs to be in a location that will be imported into the Django process before any model or template tag attempts to use it. The example Django application achieves this through an import in the main urls.py file, and any views.py would also be sufficient.

That is the classic Django app-registry ordering constraint, and it is the one thing here that will produce a confusing error if you get it wrong.

Sharing state, and the extra dependency that buys it

The README is careful about one limit. Whilst the example allows for the direct use of existing Dash applications, it does not provide for the sharing or updating of internal state, and the online documentation provides details on using these and other additional features.

The reason is visible in the dependency list. The requirements are dash at 2.0 or newer, plotly, dpd-components at 0.2.0 or newer, dash-bootstrap-components, channels at 4.0 or newer, Django at 4.0.0 or newer, Flask at 1.0.2 or newer and Werkzeug.

Two of those are the interesting ones. channels is a Django package for WebSocket and ASGI support, and it is there for the live updating that shares application state. The demo script even downloads a Redis container and launches it with default settings, and marks that step as optional, which is the giveaway: live state sharing needs a shared store, and Redis is the one the demo uses.

So the picture is that a plain embedded Dash app works out of the box with no extra infrastructure, and the state sharing and live updating features need channels plus a message broker. The distinction is worth understanding before you commit, because the second mode changes your deployment topology from a single web process to a web process plus a broker.

The Flask and Werkzeug dependencies also point at the architecture. Dash is built on Flask, and DjangoDash keeps that machinery while adapting the request handling to Django's.

Running the demo from a clone

The source repository contains a demo application, and the scripts to run it are at the top of the tree.

bash
git clone https://github.com/GibbsConsulting/django-plotly-dash.git

cd django-plotly-dash

./make_env                # sets up a virtual environment for development
                          #   with direct use of the source code for the package

./prepare_redis           # downloads a redis docker container
                          #   and launches it with default settings
                          #   *THIS STEP IS OPTIONAL*

./prepare_demo            # prepares and launches the demo
                          #   using the Django debug server at http://localhost:8000

The three scripts do three things. make_env sets up a development virtual environment with direct use of the source code for the package, so edits to the package take effect without reinstalling. prepare_redis downloads a redis docker container and launches it with default settings, and the comment marks it optional, which matches the earlier point that live state sharing is the part that needs a broker. prepare_demo prepares and launches the demo using the Django debug server at http://localhost:8000.

The demo directory also holds its own manage.py, pytest.ini and configdb.py, so it is a standalone Django project rather than a folder of fixtures.

For development work, the repository also ships check_code, check_code_demo, check_code_demo_test and check_code_dpd scripts. The check_code script invokes the test suite using pytest as well as invoking pylint on both the package and the associated demo; the other check_code_* scripts exist but the README does not describe what each one does.

The limits of embedding a Dash app in a Django page

There are three boundaries, and they come from the same source, which is that the Dash app is no longer a standalone server.

The first is deployment shape. A Dash application served on its own owns its routing, its static assets and its own process. Embedded in Django, the callbacks and their state are served through the Django application and its URLs, which means the Django project's process model, worker count and proxy configuration now also govern the Dash app. If the Dash app is the heaviest thing in your stack, embedding it does not reduce your resource needs, it just moves them.

The second is what is missing. The example in the documentation does not provide for the sharing or updating of internal state, so the feature that makes several Dash apps on one page genuinely useful needs channels and a broker. That is a real infrastructure decision that does not appear in the pip install line.

The third is the frame configuration. X_FRAME_OPTIONS set to SAMEORIGIN is a project-wide setting, and it changes the framing policy for every page in the Django site, not only for the ones with a Dash app. If you have other framed content with a different origin policy, this collides.

The maintenance picture is worth a look too. The repository is not archived, the last push was on 2026-05-11, and the releases are v2.5.1 on 2026-05-11, v2.5.0 on 2025-07-09 and v2.4.7 on 2025-07-05. The setup.py classifier still says Development Status :: 4 - Beta, and the README badge shows a Travis build status badge for the master branch, which suggests the continuous integration setup has not been modernised even though the package itself has been.

The dependency floor is also worth noting: Django 4.0.0 or newer and Python 3.8 or newer, with dash at 2.0 or newer, so a project on an older Django is out of scope.

Editorial conclusion

Adopt django-plotly-dash when you already run Django and want to reuse an existing Dash app inside a Django page, keeping the login and session handling in one place. Do not adopt it if you want Dash to keep its own deployment, since embedding a Dash app in Django changes how the callbacks and their state are served. Before you start, note that the framing of the embedded app needs X_FRAME_OPTIONS set to SAMEORIGIN, and check that the last push was on 2026-05-11 with release v2.5.1 the same day.

Frequently asked questions

How do I install django-plotly-dash in a Django project?

Install with pip install django_plotly_dash, add django_plotly_dash.apps.DjangoPlotlyDashConfig to INSTALLED_APPS, include django_plotly_dash.urls in your urlpatterns, run ./manage.py migrate, and set X_FRAME_OPTIONS to SAMEORIGIN so the embedded app can be framed.

How do I embed a Dash app in a Django template?

Register the app with the DjangoDash class instead of Dash, giving it a name, then load the template tag library and reference it. The template needs {% load plotly_dash %} and then {% plotly_app name="SimpleExample" %}.

Does django-plotly-dash support sharing state between Dash apps?

The documented example does not provide for the sharing or updating of internal state. Live updating and shared state are described in the online documentation, and the dependency on channels at 4.0 or newer plus the optional Redis step in the demo point to the extra infrastructure those features need.

What are the dependencies of django-plotly-dash?

plotly, dash at 2.0 or newer, dpd-components at 0.2.0 or newer, dash-bootstrap-components, channels at 4.0 or newer, Django at 4.0.0 or newer, Flask at 1.0.2 or newer and Werkzeug. Python 3.8 or newer is required.

How do I run the django-plotly-dash demo application?

Clone the repository and run the three scripts: ./make_env for a development virtual environment, ./prepare_redis to download and launch a Redis container, which is optional, and ./prepare_demo to launch the demo on the Django debug server at http://localhost:8000.

Official sources

  1. GibbsConsulting/django-plotly-dash on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/gibbsconsulting-django-plotly-dash.svg)](https://hysenlabs.com/projects/gibbsconsulting-django-plotly-dash)

Was this useful?