How to use a feathericons in Dash app?

Hello,

can somebody please advice how to use an feathericons in dash app?

My html element can use an argument ‘data-feather=“shopping-cart”’ based on which an icon should be displayed.

For example:

                    <div class="stat stat-sm">												
                      <i class="align-middle" data-feather="shopping-bag"></i>											
                    </div>	

Thanks,
Tomas

You can set the data-feather property in html.I by unpacking a dictionary as keyword arguments

html.I(className="align-middle", **{"data-feather": "shopping-bag"})

The challenging thing though is that you need to call feather.replace() to insert the SVGs, and because the app content is populated by React, the <i> doesn’t exist in the layout at the time the scripts are loaded.

One way to work around this is to use a clientside callback that is triggered by something like the id of a component in your layout, and then call feather.replace() inside it. When your Dash app starts up, the initial call of the clientside callback will happen after your layout is populated and replace the icons correctly. Here’s an example of that

import dash
import dash_html_components as html
from dash.dependencies import Input, Output

app = dash.Dash(external_scripts=["https://unpkg.com/feather-icons"])

app.layout = html.Div(
    html.I(id="icon", **{"data-feather": "shopping-bag"}),
    id="content",
)


app.clientside_callback(
    """
    function(id) {
        feather.replace();
        return window.dash_clientside.no_update
    }
    """,
    Output("content", "className"),
    Input("content", "id"),
)


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

This simple example works, but I think it will probably start to break down if you have an app where icons aren’t in the initial layout (e.g. if you’re making a multi-page app).

The best solution (though also a nontrivial amount of work) might be to use the dash-component-boilerplate to wrap react-feather and make the icons available as Dash components.

1 Like

Hi Tom,

many thanks for your response.

At the beginning I am trying to use your simpler solution. Please see below parts of my code:

app.layout = html.Div([
                        dcc.Location(id='url', refresh=False),
                        sidebar,
                        html.Div(id='page-content', children=[], className='main')
                    ],
                className='wrapper',
                id='main-layout-div'
)



app.clientside_callback(
    """
    function(id) {
        feather.replace();
        return window.dash_clientside.no_update
    }
    """,
    Output("main-layout-div", "className"),
    Input("main-layout-div", "id"),
)

Sidebar and page-content includes html elements. For example Sidebar:

sidebar = html.Nav(
    [
        html.Div(
            [
                html.A([
                        html.Span('mySideBar', className='align-middle me-3')
                    ], className='sidebar-brand', href='/'
                ),
                html.Ul(
                    [
                        html.Li([
                               html.A([
                                        html.I('', className='align-middle',
                                                **{
                                                    'data-feather':'list'
                                                   }
                                            ),
                                        html.Span('Summary', className='align-middle'),

                                    ],
                                    className='sidebar-link',
                                    href='/apps/myDashboard'
                                )
                            ], className='sidebar-item'
                        ),
                        html.Li('Pages', className='sidebar-header'),
                        html.Li([
                               html.A([
                                        html.I('', className='align-middle',
                                                **{
                                                    'data-feather':'sliders'
                                                   }
                                            ),
                                        html.Span('Dashboards', className='align-middle'),
                                        html.Span('5', className='badge badge-sidebar-primary'),
                                    ],
                                    **{
                                    'data-bs-toggle':'collapse',
                                    'data-bs-target':'#dashboards',
                                    },
                                    className='sidebar-link',
                                ),
                                html.Ul(
                                    [
                                        html.Li(
                                            [
                                                html.A('myTradesList', className='sidebar-link', href='/apps/myTradesList')
                                            ], className="sidebar-item"
                                        )
                                    ],
                                    id="dashboards", className='sidebar-dropdown list-unstyled collapse show',
                                    **{'data-bs-parent':'#sidebar'}
                                )

                            ], className='sidebar-item', #active="exact"
                        ),
                    ], className='sidebar-nav'
                )

            ],
            className='sidebar-content js-simplebar',
        )
    ], id="sidebar", className='sidebar',
)

In a generated source code I see:

<script>
var clientside = window.dash_clientside = window.dash_clientside || {};
var ns = clientside["_dashprivate_main-layout-div"] = clientside["_dashprivate_main-layout-div"] || {};
ns["className"] = 
    function(id) {
        feather.replace();
        return window.dash_clientside.no_update
    }
    ;
</script>

Unfortunately, I am still missing something or doing some details incorrectly.
I am not sure whether Output(“main-layout-div”, “className”) in clientside_callback is correct.

As I am just starting with dash, usage dash-component-boilerplate is for me really nontrivial.

Kind regards,
Tomas

Hi Tom,

not sure what was wrong in above code, but now it’s working. Even on multipage app, however an clientside callback must be added to each page under main div element.

Thanks a lot.
Tomas

1 Like

Great! Glad I could help.

By the way, I’ve found FontAwesome pretty easy to use with Dash in the past (no need for clientside callbacks and so on). If you are just looking for some icons and aren’t tied to feather-icons you could try that.

Thanks Tom.

Regarding FontAwsome, don’t you have any simple example how to implement it into app?
What are the prerequisites?

Thanks,
Tomas

Sure, you just need to link a stylesheet and then set class names appropriately

import dash
import dash_html_components as html

FA = "https://use.fontawesome.com/releases/v5.15.2/css/all.css"

app = dash.Dash(external_stylesheets=[FA])

app.layout = html.Div(
    [
        html.I(className="fas fa-smile"),
        html.I(className="fas fa-smile fa-3x"),  # resize with classes
        html.I(className="far fa-smile"),  # fas: solid, far: regular
    ]
)

if __name__ == "__main__":
    app.run_server(debug=True)
2 Likes

Works like a charm!
Thanks a lot great man. I was really struggling with it, now your solution is elegant, easy to implement and works as it should.

Tomas

1 Like

i was looking for this thank you

1 Like