<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Jupyter Blog - Maarten Breddels</title><link href="https://jupyter.org/blog/" rel="alternate"/><link href="https://jupyter.org/blog/feeds/author-maarten-breddels.atom.xml" rel="self"/><id>https://jupyter.org/blog/</id><updated>2024-12-18T08:06:00+00:00</updated><subtitle>The Project Jupyter blog: news, releases, and community stories, archived from blog.jupyter.org.</subtitle><entry><title>Build your Jupyter dashboard using Solara</title><link href="https://jupyter.org/blog/posts/2024/build-your-jupyter-dashboard-using-solara/" rel="alternate"/><published>2024-12-17T10:26:00+00:00</published><updated>2024-12-18T08:06:00+00:00</updated><author><name>Maarten Breddels</name></author><id>tag:jupyter.org,2024-12-17:/blog/posts/2024/build-your-jupyter-dashboard-using-solara/</id><summary type="html">&lt;p&gt;Welcome to the first part of a series of articles showing you how to create a dashboard in Jupyter and deploy it as a standalone web app. Importantly, you won’t need to rewrite your app in a different framework for deployment. We will use a pure Python solution with no JavaScript or CSS required.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Welcome to the first part of a series of articles showing you how to create a dashboard in Jupyter and deploy it as a standalone web app. Importantly, you won’t need to rewrite your app in a different framework for deployment. We will use a pure Python solution with no JavaScript or CSS required.&lt;/p&gt;
&lt;p&gt;Jupyter notebooks are an incredible data analysis tool since they blend code, visualization, and narrative into a single document. However, we do not want to show the code if the insights must be presented to a non-technical audience.&lt;/p&gt;
&lt;p&gt;Built on top of ipywidgets, the Solara framework integrates into the Jupyter Notebook, Jupyter Lab, and other Jupyter environments and is Open Source. As we will see in a later article, Solara apps can also be deployed efficiently using the Solara server. This, by itself, makes Solara a perfect solution for creating dashboards or data apps.&lt;/p&gt;
&lt;p&gt;In this article, we will create a simple dashboard using Solara’s UI components in Jupyter. The final product will allow an end-user to filter, visualize, and explore a dataset on a map.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The end result will be a modern looking deploy app" src="https://jupyter.org/blog/posts/2024/build-your-jupyter-dashboard-using-solara/images/001-1_84N-UBW-fy0xX1pCagIeiw.jpg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The end result will be a modern looking deploy app&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="pre-requisites"&gt;Pre-requisites&lt;/h2&gt;
&lt;p&gt;You need to install &lt;code&gt;pandas&lt;/code&gt;, &lt;code&gt;matplotlib&lt;/code&gt;, &lt;code&gt;folium&lt;/code&gt; and &lt;code&gt;solara&lt;/code&gt;. Assuming you are using pip, you can execute on your shell:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;pandas&lt;span class="w"&gt; &lt;/span&gt;matplotlib&lt;span class="w"&gt; &lt;/span&gt;folium&lt;span class="w"&gt; &lt;/span&gt;solara
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Or in your notebook&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;%pip install pandas matplotlib folium solara
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="the-start"&gt;The start&lt;/h2&gt;
&lt;p&gt;We will use a subsample of the &lt;a href="https://www.kaggle.com/competitions/sf-crime/data"&gt;San Fransisco crime dataset&lt;/a&gt; which contains information on types of crimes and where they were committed.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://raw.githubusercontent.com/widgetti/solara/master/solara/website/pages/docs/content/04-tutorial/SF_crime_sample.csv.gz"&gt;Download the CSV file&lt;/a&gt; to run this locally, or let the code below sort it out.&lt;/p&gt;
&lt;p&gt;The first thing we do when we read the data is to print it out to see what the dataset contains.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pandas&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pd&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pathlib&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;solara&lt;/span&gt;

&lt;span class="n"&gt;ROOT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;solara&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;website&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;pages&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;docs&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;content&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;04-tutorial&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ROOT&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;SF_crime_sample.csv.gz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;https://raw.githubusercontent.com/widgetti/solara/master/solara/website/pages/docs/content/04-tutorial/SF_crime_sample.csv&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;df_crime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;df_crime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;df_crime&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;
&lt;img alt="Pandas printout of the Crime dataset Dataframe" src="https://jupyter.org/blog/posts/2024/build-your-jupyter-dashboard-using-solara/images/002-1_bctoyIMn6I60D6gzMq-wjQ.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Pandas printout of the Crime dataset Dataframe&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The data looks clean, but since we will work with the &lt;code&gt;Category&lt;/code&gt; and &lt;code&gt;PdDistrict&lt;/code&gt; column data, let us convert those columns to title case.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;df_crime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Category&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df_crime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Category&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;df_crime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;PdDistrict&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df_crime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;PdDistrict&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;df_crime&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;
&lt;img alt="Category and PdDistrict columns nicely formatted with title casing" src="https://jupyter.org/blog/posts/2024/build-your-jupyter-dashboard-using-solara/images/003-1_3qlsT6qyJsLyRB9Ded461Q.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Category and PdDistrict columns nicely formatted with title casing&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Using proper software engineering practices, we write a function that filters a dataframe to contain only the rows that match our chosen districts and categories.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;crime_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;district_values&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category_values&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;df_dist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;PdDistrict&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;district_values&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;df_category&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df_dist&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;df_dist&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Category&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category_values&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;df_category&lt;/span&gt;

&lt;span class="n"&gt;dff_crime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;crime_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df_crime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Bayview&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Northern&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Vandalism&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Assault&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Robbery&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, with our filtered dataset, we create two bar charts. We use regular Pandas and Matplotlib, but Seaborn or Plotly are also appropriate choices.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;matplotlib.pyplot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;plt&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;crime_charts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cat_unique&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Category&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value_counts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;cat_unique&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cat_unique&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reset_index&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;dist_unique&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;PdDistrict&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value_counts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;dist_unique&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dist_unique&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reset_index&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;fig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ax1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ax2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subplots&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;figsize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;ax1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cat_unique&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Category&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;cat_unique&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;count&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;ax1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Amount of Criminal Case Based on Category&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ax2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dist_unique&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;PdDistrict&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;dist_unique&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;count&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;ax2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Amount of Criminal Case in Selected District&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;crime_charts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dff_crime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2024/build-your-jupyter-dashboard-using-solara/images/004-1_5O_dyZhxuJMZNAMt3gw6rg.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Since we do not need bidirectional communication (e.g., we do not need to receive events or data from our map), we use Folium to display the locations of the committed crimes on a map. If we do need bidirectional communication, we can use &lt;a href="https://ipyleaflet.readthedocs.io/"&gt;ipyleaflet&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Since we cannot display all the data on the map without crashing your browser, we limit it to a maximum of 50 points.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;folium&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;folium.plugins&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;crime_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;latitude&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;37.77&lt;/span&gt;
    &lt;span class="n"&gt;longitude&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;122.42&lt;/span&gt;

    &lt;span class="n"&gt;sanfran_map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;folium&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;zoom_start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;incidents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;folium&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MarkerCluster&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sanfran_map&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# loop through the dataframe and add each data point to the mark cluster&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;folium&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Marker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;icon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;popup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;incidents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# show map&lt;/span&gt;
    &lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sanfran_map&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;crime_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dff_crime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iloc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2024/build-your-jupyter-dashboard-using-solara/images/005-1_tTvZxrjpxsrsd7f1w55QjA.jpg" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;h2 id="making-our-first-reactive-visualization"&gt;Making our first reactive visualization&lt;/h2&gt;
&lt;p&gt;The above code works nicely, but if we want to explore different types of crimes, we need to modify and run all cells that determine our output manually. Would it not be much better to have a UI with controls determining the filtering and a view displaying the filtered data interactively?&lt;/p&gt;
&lt;p&gt;Let’s start by importing the solara package and creating three reactive variables.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;solara&lt;/span&gt;

&lt;span class="n"&gt;districts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;solara&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reactive&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Bayview&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Northern&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;categories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;solara&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reactive&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Vandalism&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Assault&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Robbery&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;solara&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reactive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A reactive variable is a container around a value (like an int, string, or list) that allows the UI to listen to changes automatically. Any change to &lt;code&gt;your_reactive_variable.value&lt;/code&gt; will be picked up by Solara components that use them so that they can automatically redraw or update themselves.&lt;/p&gt;
&lt;p&gt;Let us now create our first component (&lt;code&gt;View&lt;/code&gt;), which filters the data based on the reactive variables and shows the map and the charts. Solara supports the &lt;code&gt;display&lt;/code&gt; mechanism of Jupyter so that we can use our previously defined functions.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nd"&gt;@solara&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;View&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;dff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;crime_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df_crime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;districts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;row_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;row_count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;solara&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Only showing the first &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; of &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;row_count&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; crimes on map&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;crime_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dff&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iloc&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;row_count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;crime_charts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;solara&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;You filtered out all the data, no charts shown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;View&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;
&lt;img alt="Our reactive visualization auto-updates when any reactive variable changes" src="https://jupyter.org/blog/posts/2024/build-your-jupyter-dashboard-using-solara/images/006-1_BhAMB3EetCG4IfnyuWuY1A.jpg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Our reactive visualization auto-updates when any reactive variable changes&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Note that some UI parts (like the warning and the charts) are conditional. Solara will automatically find out what to add, remove, or update without you having to do this manually. Solara is declarative (similar to ReactJS) but also reactive. If we change the reactive variables, Solara will see those changes and notify the component instances that use its value.&lt;/p&gt;
&lt;p&gt;If we run the next lines of code in our notebook, our &lt;code&gt;View&lt;/code&gt; will automatically update.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;70&lt;/span&gt;
&lt;span class="n"&gt;districts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Soutern&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Northern&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can now explore our data much faster since we don’t need to re-run the cells that depend on it.&lt;/p&gt;
&lt;p&gt;Solara’s reactive and declarative nature makes it scalable to much larger applications than regular ipywidgets, where keeping the UI in sync and adding, removing, and updating widgets is a manual and bug-prone process.&lt;/p&gt;
&lt;h2 id="adding-controls"&gt;Adding controls&lt;/h2&gt;
&lt;p&gt;We created a declarative and reactive mini app in our notebook, but we still need to manually modify the values by executing a code cell in our Notebook. Now, let us create a UI to control it. All Solara input components support reactive variables. This means that controlling a reactive variable using a UI element is often a one-liner.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;solara&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SelectMultiple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;District&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;all_values&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;df_crime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;PdDistrict&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
    &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;districts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;
&lt;img alt="In one line of code we made our app filtering interactive by adding a dropdown" src="https://jupyter.org/blog/posts/2024/build-your-jupyter-dashboard-using-solara/images/007-1_oWB4IjwHBDQZHsf13zKG6w.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;In one line of code we made our app filtering interactive by adding a dropdown&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Whow, that was simple! We can now easily change the filter and see the results update. Lets do this for all our reactive variables, and put them into a single component.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nd"&gt;@solara&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Controls&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;solara&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SelectMultiple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;District&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;all_values&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;df_crime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;PdDistrict&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;districts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;solara&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SelectMultiple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Category&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;all_values&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;df_crime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Category&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;solara&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Maximum number of rows to show on map&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;solara&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SliderInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;Controls&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;
&lt;img alt="All controls for the filtering of our dataframe and visualization" src="https://jupyter.org/blog/posts/2024/build-your-jupyter-dashboard-using-solara/images/008-1_MhxvqxsURNH-1X9yr2La4g.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;All controls for the filtering of our dataframe and visualization&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Note that the reactive variables are bi-directional, meaning that if you change it in the UI elements, it gets reflected on the Python code!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Note that we can read AND write reactive variables&lt;/span&gt;
&lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Warrants&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="the-final-dashboard"&gt;The final dashboard&lt;/h2&gt;
&lt;p&gt;We now have two parts of our UI in separate cells. This can be an amazing experience when developing in a notebook, as it flows naturally in the data exploration process while writing your notebook.&lt;/p&gt;
&lt;p&gt;However, your end user will probably want something more coherent. The components we created are perfectly reusable, so we put them together in a single UI.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nd"&gt;@solara&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;solara&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sidebar&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;Controls&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;View&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;
&lt;img alt="Our end result" src="https://jupyter.org/blog/posts/2024/build-your-jupyter-dashboard-using-solara/images/009-1_5Ujq7HfB0YnX9yoAzFZmwA.jpg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Our end result&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Finally, our dashboard is finished!&lt;/p&gt;
&lt;h2 id="conclusions"&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;Using Solara, you created an interactive dashboard within a Jupyter Notebook. Your Solara components are declarative, and when using reactive variables, they are also reactive. Whether you change reactive variables via code or the UI elements, your visualizations and maps update automatically.&lt;/p&gt;
&lt;p&gt;Your dashboard prototype now runs in your Jupyter Notebook environment, but we can only deploy it as an app. In our next article, we will focus on deploying our notebook without making any code changes. In our third tutorial, we will expand our dashboard with a few more components and create a more advanced layout.&lt;/p&gt;
&lt;p&gt;All documentation for Solara can be found at &lt;a href="https://solara.dev"&gt;https://solara.dev&lt;/a&gt;, the GitHub repo can be found at &lt;a href="https://github.com/widgetti/solara/"&gt;https://github.com/widgetti/solara/&lt;/a&gt; and feel free to join our &lt;a href="https://discord.solara.dev/"&gt;Discord&lt;/a&gt; for a chat.&lt;/p&gt;
</content><category term="dashboards"/><category term="visualization"/></entry><entry><title>And voilà!</title><link href="https://jupyter.org/blog/posts/2019/and-voila/" rel="alternate"/><published>2019-06-11T17:42:00+00:00</published><updated>2021-01-08T08:38:00+00:00</updated><author><name>Sylvain Corlay</name></author><id>tag:jupyter.org,2019-06-11:/blog/posts/2019/and-voila/</id><summary type="html">&lt;p&gt;… from Jupyter notebooks to standalone applications and dashboards&lt;/p&gt;
</summary><content type="html">&lt;p&gt;The goal of Project Jupyter is to improve the workflows of researchers, educators, scientists, and other practitioners of scientific computing, from the &lt;em&gt;&lt;strong&gt;exploratory phase&lt;/strong&gt;&lt;/em&gt; of their work to the &lt;em&gt;&lt;strong&gt;communication&lt;/strong&gt;&lt;/em&gt; of the results.&lt;/p&gt;
&lt;p&gt;But interactive notebooks are not the best communication tool for all audiences. While they have proven invaluable to provide a &lt;em&gt;narrative&lt;/em&gt; alongside the source, they are not ideal to address &lt;em&gt;&lt;strong&gt;non-technical readers&lt;/strong&gt;&lt;/em&gt;, who may be put off by the presence of code cells, or the need to run the notebook to see the results. Finally, following the order as the code often results in the most interesting content to be at the &lt;em&gt;&lt;strong&gt;end&lt;/strong&gt;&lt;/em&gt; of the document.&lt;/p&gt;
&lt;p&gt;Another challenge with sharing notebooks is the &lt;em&gt;&lt;strong&gt;security&lt;/strong&gt;&lt;/em&gt; model. How can we offer the interactivity of a notebook making use of e.g. Jupyter widgets without allowing arbitrary code execution by the end user?&lt;/p&gt;
&lt;p&gt;We set ourselves to solve these challenges, and we are happy to announce the first release of &lt;em&gt;&lt;strong&gt;Voilà&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2019/and-voila/images/001-1_c1xwFRqy99o8nLVxDSqNZg.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Voilà&lt;/strong&gt;&lt;/em&gt; turns Jupyter notebooks into standalone web applications.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Voilà supports &lt;em&gt;&lt;strong&gt;Jupyter interactive widgets&lt;/strong&gt;&lt;/em&gt;, including the roundtrips to the kernel.&lt;/li&gt;
&lt;li&gt;Voilà &lt;em&gt;&lt;strong&gt;does not permit arbitrary code execution&lt;/strong&gt;&lt;/em&gt; by consumers of dashboards.&lt;/li&gt;
&lt;li&gt;Built upon Jupyter standard protocols and file formats, Voilà works with any Jupyter kernel (C++, Python, Julia), making it a &lt;em&gt;&lt;strong&gt;language-agnostic&lt;/strong&gt;&lt;/em&gt; dashboarding system.&lt;/li&gt;
&lt;li&gt;Voilà is extensible. It includes a flexible &lt;em&gt;&lt;strong&gt;template system&lt;/strong&gt;&lt;/em&gt; to produce rich application layouts.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="installation-and-first-time-use"&gt;Installation and first-time use&lt;/h2&gt;
&lt;p&gt;Voilà can be installed from pypi:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip install voila
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;or conda-forge:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;conda install voila -c conda-forge
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Upon installation, several components are installed, one of which is the &lt;code&gt;voila&lt;/code&gt; command-line utility. You can try it by typing &lt;code&gt;voila notebook.ipynb&lt;/code&gt;. It results in the browser opening to a new tornado application showing markdown cells, rich outputs, and interactive widgets.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="From a notebook to a standalone web application" src="https://jupyter.org/blog/posts/2019/and-voila/images/002-1_imDFJcYj8k-apbrvIK9ZVQ.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;From a notebook to a standalone web application&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;As you can see in the screencast, Jupyter interactive widgets remain fully functional even when they require computation by the kernel.&lt;/p&gt;
&lt;p&gt;You can immediately try out some of the command-line options to Voilà&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;with &lt;code&gt;--strip_sources=False&lt;/code&gt;, input cells will be included in the resulting web application (as read-only pygment snippets).&lt;/li&gt;
&lt;li&gt;with &lt;code&gt;--theme=dark&lt;/code&gt;, Voilà will make use of the dark JupyterLab theme, which will apply to code cells, widgets and all other visible components.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
&lt;img alt="Making use of the dark theme and including input cells" src="https://jupyter.org/blog/posts/2019/and-voila/images/003-1_ce142q3rm3TgJZVNEgGWcw.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Making use of the dark theme and including input cells&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Note that code is only shown, voilà does not allow users to edit or execute arbitrary code.&lt;/p&gt;
&lt;h2 id="voilas-execution-model"&gt;Voilà’s execution model&lt;/h2&gt;
&lt;p&gt;The execution model of Voilà is the following: upon connection to a notebook URL, Voilà launches the kernel for that notebook, and runs all the cells as it populates the notebook model with the outputs.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The execution model of Voilà" src="https://jupyter.org/blog/posts/2019/and-voila/images/004-1_KZj7rmVqAHmY1b-P-QMPLw.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The execution model of Voilà&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;After the execution, the associated kernel is not shut down. The notebook is converted to HTML and served to the user. The rendered HTML includes JavaScript that establishes a connection to the kernel. Jupyter interactive widgets referred in cell outputs are rendered and connected to their counterpart in the kernel. The kernel is only shut down when the user closes their browser tab.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The current version of Voilà only responds to the initial GET request when all the cells have finished running, which may take a long time, but there is &lt;a href="https://github.com/QuantStack/voila/pull/133"&gt;ongoing work&lt;/a&gt; on enabling progressive rendering, which should make it into a release soon.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;An important aspect of this execution model is that the front-end does not determine what code is run by the backend. In fact, unless specified otherwise (with option &lt;code&gt;--strip-sources=False&lt;/code&gt;), the source of the rendered notebook does not even make it to the front-end. The instance of the &lt;code&gt;jupyter_server&lt;/code&gt; instantiated by Voilà actually disallows execute requests by default.&lt;/p&gt;
&lt;h2 id="support-for-custom-interactive-widgets"&gt;Support for custom interactive widgets&lt;/h2&gt;
&lt;p&gt;Voilà can render custom Jupyter widget libraries, including (but not limited to) &lt;a href="https://github.com/bloomberg/bqplot"&gt;bqplot&lt;/a&gt;, &lt;a href="https://github.com/jupyter-widgets/ipyleaflet"&gt;ipyleafet&lt;/a&gt;, &lt;a href="https://github.com/maartenbreddels/ipyvolume"&gt;ipyvolume&lt;/a&gt;, &lt;a href="https://github.com/matplotlib/jupyter-matplotlib/"&gt;ipympl&lt;/a&gt;, &lt;a href="https://github.com/QuantStack/ipysheet"&gt;ipysheet&lt;/a&gt;, &lt;a href="https://github.com/plotly/plotly.py"&gt;plotly&lt;/a&gt;, &lt;a href="https://github.com/maartenbreddels/ipywebrtc"&gt;ipywebrtc&lt;/a&gt;, etc.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Including bqplot figures with Voilà" src="https://jupyter.org/blog/posts/2019/and-voila/images/005-1_iBz5dUYHT5N9dbymPKTGCg.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Including &lt;a href="https://github.com/bloomberg/bqplot"&gt;bqplot&lt;/a&gt; figures with Voilà&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Together with &lt;a href="https://github.com/matplotlib/jupyter-matplotlib/"&gt;ipympl&lt;/a&gt;, Voilà is actually a simple means to render interactive matplotlib figures in a standalone web application:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Rendering interactive matplotlib figures in a web application with voilà" src="https://jupyter.org/blog/posts/2019/and-voila/images/006-1_e3k8ZgJoCp0Pm-yZ5Xr0Xg.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Rendering interactive matplotlib figures in a web application with voilà&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="voila-is-language-agnostic"&gt;Voilà is language-agnostic&lt;/h2&gt;
&lt;p&gt;Voilà can be used to produce applications with any Jupyter kernel. The following screencast shows how Voilà can be used to produce a simple dashboard in C++ making use of leaflet.js maps, with the &lt;a href="https://github.com/QuantStack/xeus-cling"&gt;xeus-cling&lt;/a&gt; C++ kernel and the &lt;a href="https://github.com/QuantStack/xleaflet"&gt;xleaflet&lt;/a&gt; package.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="A standalone Voilà page making use of the C++ Jupyter kernel, xeus-cling (input cells display enabled)." src="https://jupyter.org/blog/posts/2019/and-voila/images/007-1_os2ABw7hEnfd1Dq5pOrTDg.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;A standalone Voilà page making use of the C++ Jupyter kernel, &lt;a href="https://github.com/QuantStack/xeus-cling"&gt;xeus-cling&lt;/a&gt; (input cells display enabled).&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;We hope that Voilà will be a stimulant to other languages (R, Julia, JVM/Java) to provide stronger widgets support.&lt;/p&gt;
&lt;h2 id="richer-layouts-with-voila-templates"&gt;Richer layouts with Voilà templates&lt;/h2&gt;
&lt;p&gt;The main extension point to Voilà is the custom &lt;em&gt;&lt;strong&gt;template system&lt;/strong&gt;&lt;/em&gt;. The HTML served to the end-user is produced from the notebook model by applying a Jinja template, which can be defined by the user.&lt;/p&gt;
&lt;p&gt;An example template for voilà is the &lt;code&gt;voila-gridstack&lt;/code&gt; template, which can be installed from pypi with&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip install voila-gridstack
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can try it by typing &lt;code&gt;voila notebook.ipynb --template=gridstack&lt;/code&gt;.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Making use of the Gridstack template to produce a dashboard with bqplot charts" src="https://jupyter.org/blog/posts/2019/and-voila/images/008-1_grVVSeKyw7bXYU7fZmHgQQ.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Making use of the Gridstack template to produce a dashboard with &lt;a href="https://github.com/bloomberg/bqplot/"&gt;bqplot&lt;/a&gt; charts&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The &lt;a href="https://github.com/QuantStack/voila-gridstack/"&gt;gridstack Voilà template&lt;/a&gt; makes use of the cell metadata to lay out the application.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A roadmap item for the gridstack Voilà template is to support the entire spec for the deprecated &lt;a href="https://github.com/jupyter/dashboards"&gt;jupyter dashboards&lt;/a&gt; and to create a WYSIWYG editor for these templates in the form of a JupyterLab extension.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Note that &lt;a href="https://github.com/QuantStack/voila-gridstack/"&gt;voila-gridstack&lt;/a&gt; template is still at an early stage of development.&lt;/p&gt;
&lt;h2 id="how-to-make-custom-voila-templates"&gt;How to make custom Voilà templates?&lt;/h2&gt;
&lt;p&gt;A Voilà template is actually a &lt;em&gt;&lt;strong&gt;folder&lt;/strong&gt;&lt;/em&gt; placed in the standard directory &lt;code&gt;PREFIX/share/jupyter/voila/templates&lt;/code&gt; and which may include&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;nbconvert&lt;/code&gt; templates (the jinja templates used to transform the notebook into HTML)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;static&lt;/code&gt; resources&lt;/li&gt;
&lt;li&gt;custom &lt;code&gt;tornado&lt;/code&gt; templates such as &lt;code&gt;404.html&lt;/code&gt; etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of these are optional. It may also contain a &lt;code&gt;conf.json&lt;/code&gt; file to set up which template to use as a base. The directory structure for a Voilà template is the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;PREFIX/share/jupyter/voila/templates/template_name/
|
├── conf.json                # Template configuration file
├── nbconvert_templates/     # Custom nbconvert templates
├── static/                  # Static directory
└── templates/               # Custom tornado templates
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The Voilà template system can be used to completely override the behavior of the front-end. One can make use of modern JavaScript frameworks such as &lt;a href="https://reactjs.org/"&gt;React&lt;/a&gt; or &lt;a href="https://vuejs.org/"&gt;Vue.js&lt;/a&gt; to produce modern UI including Jupyter widgets and outputs.&lt;/p&gt;
&lt;p&gt;Another example template for Voilà is &lt;code&gt;voila-vuetify&lt;/code&gt;, which is built upon vue.js:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The voila-vuetify template makes use of Vue.js" src="https://jupyter.org/blog/posts/2019/and-voila/images/009-1_HtXNf1rq26u9ss8L-Oo6VQ.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The &lt;a href="https://github.com/QuantStack/voila-vuetify"&gt;voila-vuetify&lt;/a&gt; template makes use of Vue.js&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;em&gt;The voila-gridstack and voila-vuetify templates are still at an early stage of development, but will be iterated upon quickly in the next weeks as we are exploring templates.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="a-jupyter-server-extension"&gt;A Jupyter server extension&lt;/h2&gt;
&lt;p&gt;Beyond the &lt;code&gt;voila&lt;/code&gt; command-line utility, the Voilà package also include a Jupyter &lt;em&gt;&lt;strong&gt;server extension&lt;/strong&gt;&lt;/em&gt;, so that Voilà dashboards can be served alongside the Jupyter notebook application.&lt;/p&gt;
&lt;p&gt;When Voilà is installed, a running Jupyter server will serve the Voilà web application under &lt;code&gt;BASE_URL/voila&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="the-jupyter-community-workshop-on-dashboarding"&gt;The Jupyter Community Workshop on Dashboarding&lt;/h2&gt;
&lt;p&gt;From June 3rd to June 6th 2019, a &lt;a href="https://jupyter.org/blog/posts/2019/jupyter-community-workshop-dashboarding-with-project/"&gt;community workshop on dashboarding&lt;/a&gt; with Project Jupyter took place in Paris. Over thirty Jupyter contributors and community members gathered to discuss dashboarding technologies and hack together.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The participants to the Paris Jupyter community workshop on dashboarding" src="https://jupyter.org/blog/posts/2019/and-voila/images/010-1_6LggiPlUoCSkP2A1aHp_3w.jpeg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The participants to the Paris Jupyter community workshop on dashboarding&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Several dashboarding solutions such as Dash and Panel were presented during the workshop and featured at the &lt;a href="https://www.meetup.com/PyData-Paris/events/261452824/"&gt;PyData Paris Meetup&lt;/a&gt; which was organized on the same week.&lt;/p&gt;
&lt;p&gt;The workshop was also the occasion for several contributors to start working on Voilà. Custom templates, a dashboard gallery, logos and UX mockups for JupyterLab extensions have been developed.&lt;/p&gt;
&lt;p&gt;We will soon publish a more detailed post on the workshop, detailing the many tracks of development that have been explored!&lt;/p&gt;
&lt;h2 id="what-is-coming"&gt;What is coming?&lt;/h2&gt;
&lt;p&gt;There is a lot of planned work around Voilà in the next weeks and months. Current work streams include better &lt;em&gt;&lt;strong&gt;integration with JupyterHub&lt;/strong&gt;&lt;/em&gt; for publicly sharing dashboard between users, as well as &lt;em&gt;&lt;strong&gt;JupyterLab extensions&lt;/strong&gt;&lt;/em&gt; (a &lt;a href="https://github.com/QuantStack/voila/pull/217"&gt;Voilà “preview” extension for notebooks&lt;/a&gt;, and a WYSIWYG editor for dashboard layouts). There are also ongoing discussions with the &lt;a href="https://www.ovh.com/fr/"&gt;OVH&lt;/a&gt; cloud provider (which already supports binder by handling some of its traffic) on hosting a binder-like service dedicated to Voilà dashboards. So stay tuned for more exciting developments!&lt;/p&gt;
&lt;p&gt;Last but not least, we are especially excited about what &lt;em&gt;&lt;strong&gt;you&lt;/strong&gt;&lt;/em&gt; will be building upon Voilà!&lt;/p&gt;
&lt;h2 id="acknowledgments"&gt;Acknowledgments&lt;/h2&gt;
&lt;p&gt;The development of Voilà and related packages at &lt;a href="https://twitter.com/QuantStack"&gt;QuantStack&lt;/a&gt; is sponsored by &lt;a href="http://www.techatbloomberg.com"&gt;&lt;strong&gt;Bloomberg&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We are also grateful to the attendees of the &lt;a href="https://jupyter.org/blog/posts/2019/jupyter-community-workshop-dashboarding-with-project/"&gt;&lt;strong&gt;Jupyter Community Workshop on Dashboarding&lt;/strong&gt;&lt;/a&gt; for their numerous contributions to Voilà!&lt;/p&gt;
&lt;p&gt;We would like to thank &lt;a href="https://twitter.com/choldgraf"&gt;Chris Holdgraf&lt;/a&gt; for his work on improving documentation, and integration with JupyterHub.&lt;/p&gt;
&lt;p&gt;We should mention &lt;a href="https://twitter.com/yuvipanda"&gt;Yuvi Panda&lt;/a&gt; and &lt;a href="https://twitter.com/pascalbugnion"&gt;Pascal Bugnion&lt;/a&gt; for getting the &lt;code&gt;voila-gallery&lt;/code&gt; project off the ground during the workshop. We are grateful to &lt;a href="https://twitter.com/zrsailer"&gt;Zach Sailer&lt;/a&gt; for his continued work on improving &lt;code&gt;jupyter_server&lt;/code&gt;. We should finally not forget to mention the prior art by &lt;a href="https://twitter.com/pascalbugnion"&gt;Pascal Bugnion&lt;/a&gt; with the Jupyter widgets server which was also an inspiration for Voilà.&lt;/p&gt;
&lt;h2 id="about-the-authors"&gt;About the Authors&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://twitter.com/SylvainCorlay"&gt;&lt;em&gt;Sylvain Corlay&lt;/em&gt;&lt;/a&gt; is the founder and CEO of &lt;a href="https://github.com/QuantStack/"&gt;QuantStack&lt;/a&gt; and a core team member for Project Jupyter.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://twitter.com/maartenbreddels"&gt;Maarten Breddels&lt;/a&gt; is an independent scientific software developer partnering with &lt;a href="https://github.com/QuantStack/"&gt;QuantStack&lt;/a&gt; on numerous projects, and a core developer of Project Jupyter.&lt;/p&gt;
</content><category term="dashboards"/><category term="Voilà"/></entry></feed>