✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
🐇 Announcing Dash VTK for 3d simulation graphics. Check out the March webinar.

Serve_locally option with additional scripts and style sheets

thank you! Adding the style sheet within a link bypasses the problem.
The app is now available without any internet connection.

1 Like

I’m looking to update this behaviour (adding external CSS and JS) in this PR: https://github.com/plotly/dash/pull/171. Feedback welcome! (Please comment on the PR itself)

2 Likes

I’ve managed to use the html.Link solution above to serve local css files in my dash app, however, subsequent changes to the local css are not reflected in the app. The original css is being loaded instead. Is this being cached somewhere? Is there any way that I can purge this?

At the moment I basically have to change the name of the css file and the href in the main app to get it to load any changes.

Thanks in advance.

The browser is likely caching these assets. You can force the browser to refresh and also bypass the cache by hitting CTRL+F5.

Under chrome, forcing a refresh/by-passing the cache with CTRL+F5 doesn’t resolve the issue. The old stylesheet is still being used. The chrome developer tools panel tells me this is being take from the disk cache, has a max-age=43200 and was initiated by http://localhost:8050/_dash-component-suites/dash_renderer/react-dom@15.4.2.min.js?v=0.11.3.

It would appear this is linked to the react issue in this thread: https://github.com/facebook/create-react-app/issues/1910

Ah, frustrating.

I never liked the html.Link solution much myself, as for the app I tried it on it resulted in a momentary period of time where the styles were not applied, while the sheet was loaded. This is somewhat jarring, so I gave up on that. The other approach you can take until Dash properly supports adding you own entries in the header, is to subclass the Dash class and override the index method which includes any stylesheets you want.

Like this for example:

from dash import Dash

STYLESHEETS = ['foo.css', 'bar.css']

class CustomIndexDash(Dash):
    """Custom Dash class overriding index() method for local CSS support"""
    def _generate_css_custom_html(self):
        link_str = '<link rel="stylesheet" href="{}/{}">'
        static_url_path = self.server.config['STATIC_URL_PATH']
        return '\n'.join(link_str.format(static_url_path, path)
                         for path in STYLESHEETS)

    def index(self, *args, **kwargs):
        scripts = self._generate_scripts_html()
        css = self._generate_css_dist_html()
        custom_css = self._generate_css_custom_html()
        config = self._generate_config_html()
        title = getattr(self, 'title', 'Dash')
        return f'''
        <!DOCTYPE html>
        <html>
            <head>
                <meta charset="UTF-8">
                <title>{title}</title>
                {css}
                {custom_css}
            </head>
            <body>
                <div id="react-entry-point">
                    <div class="_dash-loading">
                        Loading...
                    </div>
                </div>
                <footer>
                    {config}
                    {scripts}
                </footer>
            </body>
        </html>
        '''
2 Likes

Thanks for the response. I managed to get this working with a bit of tweaking. The line

static_url_path = self.server.config['STATIC_URL_PATH']

in _generate_css_custom_html threw a KeyError, even when I tried to initialise the app like a flask instance using

app = CustomIndexDash(__name__,static_url_path='',static_folder='static')

Changing the _generate_css_custom_html line to

static_url_path = ''

sorted the issue. CSS changes were reflected in the app in chrome when refreshing page with CRTL+F5 to clear the cache. Thank you very much!

I am following the following issue: It works perfectly untill I make any change at the .css file. For example, I change the background-color at the file and this does not apply any change to the interface. If I want to see the change I have to save the modified file with a different name (e.g. stylesheet_new.css), then change the href='/static/stylesheet_new.css and only then I will notice the changes.

Hey @Dimkoim,

This is due to browser caching as discussed by @nedned and @adm78. You can work around this by using their approach:

import dash

STYLESHEETS = ['style.css']

class CustomIndexDash(dash.Dash):
    """Custom Dash class overriding index() method for local CSS support"""
    def _generate_css_custom_html(self):
        link_str = '<link rel="stylesheet" href="{}/{}">'
        static_url_path = 'static'
        return '\n'.join(link_str.format(static_url_path, path)
                         for path in STYLESHEETS)

    def index(self, *args, **kwargs):
        scripts = self._generate_scripts_html()
        css = self._generate_css_dist_html()
        custom_css = self._generate_css_custom_html()
        config = self._generate_config_html()
        title = getattr(self, 'title', 'Dash')
        return f'''
        <!DOCTYPE html>
        <html>
            <head>
                <meta charset="UTF-8">
                <title>{title}</title>
                {css}
                {custom_css}
            </head>
            <body>
                <div id="react-entry-point">
                    <div class="_dash-loading">
                        Loading...
                    </div>
                </div>
                <footer>
                    {config}
                    {scripts}
                </footer>
            </body>
        </html>
        '''

app = CustomIndexDash(
    __name__,
    # Serve any files that are available in the `static` folder
    static_folder='static'
)

for the folder structure of:

-- app.py
-- static/
-- static/style.css

Thanks for your reply! Still does not work for me. I have to clear my cache in order to see any change.

Hello. We are using Dash to build a fairly straight forward web app. This app includes custom stylesheets, custom Dash components and other custom javascript. We have our own static content web server (not nginx but equivalent). We want to serve everything locally from this server including the javascript.

However, when we set

app.css.config.serve_locally = True
app.scripts.config.serve_locally = True

Dash will only apparently try to serve it’s own files and ignores ours even though we have added our files to app.css.append_css and app.scripts.append_script.

The work around given here (thanks for that!) using html.Link does work for css files but there seems to be no equivalent way of loading our custom javascript when serve_locally is true. Doing the same but with the html.Script component seems to have no effect.

Does anyone have any ideas or suggestions? The problem does not seem to be how we are serving things statically but rather in how Dash itself works.

Cheers,
andrewp

If you’re silly like me and 1) didn’t know what f-strings were and 2) didn’t realize that they didn’t exist until python 3.6 and 3) you need to use python 2.7 for some reason… use:

return '''
    <!DOCTYPE html>
    <html>
        ...
    </html>
    '''.format(title=title, css=css, custom_css=custom_css, config=config, scripts=scripts)
1 Like

Hi,

while some of these answers work for single small settings, my situation is a bit different. I develop a larger server app, with flask pages and dash pages, moreover I am abroad and have only very limited internet resources, so I would like to develop the flask/dash app without internet connection.

I serve my css, and js files via a /static/ Flask route, which works when I set both of the next two lines to ‘False’

dash_app.scripts.config.serve_locally=True
dash_app.css.config.serve_locally=False

However, my Flask pages need also jQuery which I would like to server also via the static route.
If both set to “False” my static rout provides the relevant css and js files, all is well, but the React components needed to render dash pages are not loaded (no inet connection). If I set the lines to “True” and “False” as shown above, Flask is unhappy (as the locally stored jQuery et all. are not loaded) but the dash pages have the react scripts…

Any idea how to solve my offline problem?

In the end the server runs with an internet connection, but for development without internet the situation is rather daring. (When the React components are being tried to load from the iNet, the server start is rather long winded, as there are many timeouts to wait for, this makes offline development essentially not possible)

Help and ideas welcome!
Cheers

PS: Maybe the app.head functionality can help? I have not investigated that yet. See allow optional headers:

We’re working on a better solution for this in https://github.com/plotly/dash/pull/286. Please hang tight!

I’ve got a similar issue to @bfauser - I’d like to add extra css and js files to be served, using something like:

app.css.append_css({'external_url':'my/file/not/served/by/flask'})

This works fine as long as I leave:

app.css.config.serve_locally=False

but as soon as I set this flag to True (it makes my development a lot easier), Dash will silently exclude my file from the html served up in the index route of the application.

I’ve traced this behaviour to the Resources._filter_resources() function in dash/resources.py, where external urls are only served if serve_locally is False. Is there any reason for this restriction (equivalently: would you welcome a PR that enhances this logic)?

This is getting fixed in https://github.com/plotly/dash/pull/286

That PR looks really good, but I fear that I have a different use case - admittedly one that is probably an edge case.

I want to set serve_locally=True (to serve up the files I’m developing, as well as in general when I don’t have internet access) but also to have other extra files served from somewhere else - essentially some other server.

Right now, I’m using external_url in calls to append_css and append_script to achieve this, along with a patch of Resources._filter_resources. From a reading of the code - unfortunately I’ve not been able to try to run it - and the associated github notes, it looks like PR 286 won’t change this. Also, it isn’t really possible for me to cleanly put a copy of the extra files into an assets_folder directory that can be scanned by the code in PR 286, although some sort of kludge might work.

Of course, I may have misread things - but if not, I’m still in the same situation.

So with the new PR, you’ll be able to set your own index.html string and then include whatever scripts you want there. We’re also considering adding a scripts=[…] argument in the dash constructor (there are some comments in the PR with more info). Eventually, these methods will replace the existing append_script interface

Setting the index.html will be ideal - thanks!

A post was split to a new topic: Serve assets directory from server?